Session Enrichment

Problem: You need to add metadata, metrics, and context to entire sessions (collections of related spans) for tracking user workflows, experiments, or multi-step operations.

Solution: Use enrich_session() to add session-level metadata that persists across all spans in a session and is stored in the HoneyHive backend.

This guide covers session enrichment patterns. For span-level enrichment, see Span Enrichment Patterns.

Understanding Session Enrichment

Session enrichment differs from span enrichment:

Span Enrichment (enrich_span()):

  • Adds metadata to a single span (one operation)

  • Stored in OpenTelemetry span attributes

  • Local to the trace

Session Enrichment (enrich_session()):

  • Adds metadata to an entire session (collection of spans)

  • Persisted to HoneyHive backend via API

  • Available for analysis across all spans in the session

  • Supports complex nested data structures

Use Cases

Session enrichment is ideal for:

  • User Workflows: Track user journeys across multiple LLM calls

  • Experiments: Add experiment parameters and results

  • A/B Testing: Tag sessions with test variants

  • Business Context: Add customer IDs, subscription tiers, feature flags

  • Performance Metrics: Session-level latency, success rates, cost tracking

API Reference

Function Signature

enrich_session(session_id=None, *, metadata=None, inputs=None, outputs=None, config=None, feedback=None, metrics=None, user_properties=None, **kwargs)

Add metadata and metrics to a session with backend persistence.

Note

All parameters are optional: You can call enrich_session() without any parameters. The function will work correctly as long as a valid session_id is available (either explicitly provided or detected from the active context). This is useful for ensuring a session exists or “touching” it even when you don’t have enrichment data to add.

Parameters:

Parameters:
  • metadata (Optional[Dict[str, Any]]) – Business context data (user IDs, features, session info).

  • inputs (Optional[Dict[str, Any]]) – Input data for the session (e.g., initial query, configuration).

  • outputs (Optional[Dict[str, Any]]) – Output data from the session (e.g., final response, results).

  • config (Optional[Dict[str, Any]]) – Configuration parameters for the session (model settings, hyperparameters).

  • feedback (Optional[Dict[str, Any]]) – User or system feedback for the session (ratings, quality scores).

  • metrics (Optional[Dict[str, Any]]) – Numeric measurements for the session (latency, cost, token counts).

  • user_properties (Optional[Dict[str, Any]]) – User-specific properties (user_id, plan, etc.). Stored as a separate field in the backend, not merged into metadata.

  • session_id (Optional[str]) – Explicit session ID to enrich. If not provided, uses the active session from context.

  • kwargs (Any) – Additional keyword arguments (passed through for extensibility).

Returns:

Return type:

None

Returns:

None (updates session in backend)

Raises:

  • No exceptions raised - failures are logged and gracefully handled

Key Differences from enrich_span:

  1. Backend Persistence: enrich_session() makes API calls to persist data, while enrich_span() only sets local span attributes

  2. Session Scope: Affects the entire session, not just the current span

  3. Complex Data: Supports nested dictionaries and lists

  4. Explicit Session ID: Can target any session by ID, not just the active one

Basic Usage

Enrich Active Session

The simplest usage enriches the currently active session:

from honeyhive import HoneyHiveTracer, enrich_session
import openai

# Initialize tracer (creates a session automatically)
tracer = HoneyHiveTracer.init(
    project="my-app",
    session_name="user-123-chat"
)

# Enrich the active session
enrich_session(
    metadata={
        "user_id": "user_123",
        "subscription_tier": "premium",
        "feature": "chat_assistant"
    }
)

# All subsequent traces in this session will be associated with this metadata
client = openai.OpenAI()
response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": "Hello!"}]
)

Note

Optional Parameters: All parameters to enrich_session() are optional. You can call enrich_session() without any parameters to ensure the session exists or to “touch” it, even if you don’t have enrichment data to add at that moment. The function will work correctly as long as a valid session_id is available (either explicitly provided or detected from the active context).

Enrich Specific Session

Target a specific session by providing its ID:

from honeyhive import enrich_session

# Enrich a specific session (not necessarily the active one)
enrich_session(
    session_id="sess_abc123xyz",
    metadata={
        "experiment": "variant_b",
        "completed": True
    },
    metrics={
        "total_tokens": 1500,
        "total_cost": 0.045,
        "duration_seconds": 12.5
    }
)

Backwards Compatible Signatures

The enrich_session() function maintains full backwards compatibility with previous versions:

Legacy Signature (Still Supported)

 # Old style: positional session_id
 enrich_session(
     "sess_abc123",  # session_id as first positional arg
     metadata={"user_id": "user_456"}
 )

# Old style: user_properties parameter
enrich_session(
    session_id="sess_abc123",
    user_properties={
        "tier": "premium",
        "region": "us-east"
    }
)

# Result: user_properties stored as a separate field in the backend
# Backend receives:
# {
#   "user_properties": {
#     "tier": "premium",
#     "region": "us-east"
#   }
# }

Common Patterns

Pattern 1: User Workflow Tracking

Track user journeys across multiple interactions:

from honeyhive import HoneyHiveTracer, enrich_session
from datetime import datetime
import openai

def handle_user_workflow(user_id: str, workflow_name: str):
    """Handle a multi-step user workflow."""

    # Initialize session for this workflow
    tracer = HoneyHiveTracer.init(
        project="customer-support",
        session_name=f"{workflow_name}-{user_id}"
    )

    # Enrich with user context
    enrich_session(
        metadata={
            "user_id": user_id,
            "workflow": workflow_name,
            "started_at": datetime.now().isoformat()
        }
    )

    # Step 1: Initial query
    client = openai.OpenAI()
    response1 = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": "How do I reset my password?"}]
    )

    # Update session with progress
    enrich_session(
        metadata={
            "step": "initial_query_complete"
        }
    )

    # Step 2: Follow-up
    response2 = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "user", "content": "How do I reset my password?"},
            {"role": "assistant", "content": response1.choices[0].message.content},
            {"role": "user", "content": "I didn't receive the email"}
        ]
    )

    # Final session enrichment
    enrich_session(
        metadata={
            "step": "workflow_complete",
            "completed_at": datetime.now().isoformat()
        },
        metrics={
            "total_interactions": 2,
            "resolution": "success"
        }
    )

    return response2.choices[0].message.content

Pattern 2: Experiment Tracking

Add experiment parameters and results to sessions:

from honeyhive import HoneyHiveTracer, enrich_session
import openai
import random
import time

def run_ab_test_experiment(query: str, user_id: str):
    """Run A/B test with different model configurations."""

    # Determine variant
    variant = "variant_a" if random.random() < 0.5 else "variant_b"

    # Initialize session
    tracer = HoneyHiveTracer.init(
        project="ab-testing",
        session_name=f"experiment-{user_id}"
    )

    # Enrich with experiment metadata
    enrich_session(
        metadata={
            "experiment": "prompt_optimization_v2",
            "variant": variant,
            "user_id": user_id
        },
        config={
            "model": "gpt-4" if variant == "variant_a" else "gpt-3.5-turbo",
            "temperature": 0.7 if variant == "variant_a" else 0.9
        }
    )

    # Run the experiment
    start_time = time.time()
    client = openai.OpenAI()
    response = client.chat.completions.create(
        model="gpt-4" if variant == "variant_a" else "gpt-3.5-turbo",
        messages=[{"role": "user", "content": query}],
        temperature=0.7 if variant == "variant_a" else 0.9
    )
    duration = time.time() - start_time

    # Enrich with results
    enrich_session(
        metrics={
            "response_time": duration,
            "token_count": response.usage.total_tokens,
            "cost": calculate_cost(response.usage)
        },
        outputs={
            "response": response.choices[0].message.content
        }
    )

    return response.choices[0].message.content

Pattern 3: Session Feedback Collection

Add user feedback to sessions after completion:

from honeyhive import enrich_session
from datetime import datetime

def collect_session_feedback(session_id: str, rating: int, comments: str):
    """Add user feedback to a completed session."""

    # Enrich the session with feedback (can be called after session ends)
    enrich_session(
        session_id=session_id,
        feedback={
            "user_rating": rating,
            "user_comments": comments,
            "feedback_timestamp": datetime.now().isoformat(),
            "helpful": rating >= 4
        },
        metadata={
            "feedback_collected": True
        }
    )

Pattern 4: Cost and Performance Tracking

Track session-level costs and performance metrics:

from honeyhive import HoneyHiveTracer, enrich_session
import openai

class SessionCostTracker:
    """Track costs across a session."""

    def __init__(self, project: str, session_name: str):
        self.tracer = HoneyHiveTracer.init(
            project=project,
            session_name=session_name
        )
        self.total_tokens = 0
        self.total_cost = 0.0
        self.call_count = 0

    def make_llm_call(self, messages: list, model: str = "gpt-3.5-turbo"):
        """Make an LLM call and track costs."""
        client = openai.OpenAI()
        response = client.chat.completions.create(
            model=model,
            messages=messages
        )

        # Update tracking
        self.call_count += 1
        self.total_tokens += response.usage.total_tokens
        self.total_cost += self.calculate_cost(response.usage, model)

        # Enrich session with updated metrics
        enrich_session(
            metrics={
                "total_tokens": self.total_tokens,
                "total_cost": self.total_cost,
                "call_count": self.call_count,
                "avg_tokens_per_call": self.total_tokens / self.call_count
            }
        )

        return response.choices[0].message.content

    def calculate_cost(self, usage, model):
        """Calculate cost based on token usage and model."""
        # Simplified cost calculation
        if "gpt-4" in model:
            return (usage.prompt_tokens * 0.00003 +
                    usage.completion_tokens * 0.00006)
        else:
            return (usage.prompt_tokens * 0.000001 +
                    usage.completion_tokens * 0.000002)

# Usage
tracker = SessionCostTracker("my-app", "cost-tracking-session")
tracker.make_llm_call([{"role": "user", "content": "Hello!"}])
tracker.make_llm_call([{"role": "user", "content": "Tell me more"}])

Pattern 5: Multi-Instance Session Enrichment

Enrich sessions across multiple tracer instances:

from honeyhive import HoneyHiveTracer, enrich_session

# Create multiple tracers for different workflows
prod_tracer = HoneyHiveTracer.init(
    project="production",
    session_name="prod-session-1",
    source="production"
)

test_tracer = HoneyHiveTracer.init(
    project="testing",
    session_name="test-session-1",
    source="testing"
)

# Enrich production session
enrich_session(
    metadata={
        "environment": "production",
        "user_id": "user_123"
    },
    tracer_instance=prod_tracer  # Specify which tracer's session to enrich
)

# Enrich test session
enrich_session(
    metadata={
        "environment": "testing",
        "test_case": "scenario_1"
    },
    tracer_instance=test_tracer
)

Advanced Usage

Session Lifecycle Management

Enrich sessions at different lifecycle stages:

from honeyhive import HoneyHiveTracer, enrich_session
from datetime import datetime
import openai

def managed_session_workflow(user_id: str, task: str):
    """Demonstrate session enrichment across lifecycle."""

    # Initialize session
    tracer = HoneyHiveTracer.init(
        project="managed-workflows",
        session_name=f"{task}-{user_id}"
    )

    # Start: Add initial metadata
    enrich_session(
        metadata={
            "user_id": user_id,
            "task": task,
            "status": "started",
            "started_at": datetime.now().isoformat()
        }
    )

    try:
        # In Progress: Update status
        enrich_session(
            metadata={
                "status": "in_progress"
            }
        )

        # Do work
        client = openai.OpenAI()
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": f"Help me with: {task}"}]
        )

        # Success: Add final metadata
        enrich_session(
            metadata={
                "status": "completed",
                "completed_at": datetime.now().isoformat()
            },
            outputs={
                "result": response.choices[0].message.content
            },
            metrics={
                "success": True
            }
        )

        return response.choices[0].message.content

    except Exception as e:
        # Error: Add error metadata
        enrich_session(
            metadata={
                "status": "failed",
                "failed_at": datetime.now().isoformat(),
                "error_type": type(e).__name__,
                "error_message": str(e)
            },
            metrics={
                "success": False
            }
        )
        raise

Complex Data Structures

enrich_session() supports nested dictionaries and lists:

from honeyhive import enrich_session

# Complex nested structures
enrich_session(
    metadata={
        "user": {
            "id": "user_123",
            "profile": {
                "tier": "premium",
                "features": ["chat", "analytics", "export"],
                "settings": {
                    "notifications": True,
                    "language": "en"
                }
            }
        }
    },
    config={
        "model_pipeline": [
            {"step": 1, "model": "gpt-4", "temperature": 0.7},
            {"step": 2, "model": "gpt-3.5-turbo", "temperature": 0.5}
        ],
        "fallback_strategy": {
            "enabled": True,
            "models": ["gpt-4", "gpt-3.5-turbo", "claude-2"]
        }
    }
)

Best Practices

DO:

  • Enrich sessions at key lifecycle points (start, progress, completion)

  • Use consistent naming conventions for metadata keys

  • Add business-relevant context (user IDs, feature flags, experiments)

  • Include performance metrics (cost, latency, token counts)

  • Collect and add user feedback to completed sessions

DON’T:

  • Include sensitive data (passwords, API keys, PII)

  • Add extremely large payloads (>100KB per enrichment)

  • Call enrich_session() excessively (it makes API calls)

  • Use inconsistent key names across sessions

  • Forget to handle enrichment failures gracefully

Troubleshooting

Session enrichment not appearing:

  • Verify tracer is initialized and session is active

  • Check API key has proper permissions

  • Ensure session_id is valid (if explicitly provided)

  • Check network connectivity and API endpoint

Performance impact:

  • enrich_session() makes API calls (expect ~50-200ms per call)

  • Batch enrichment calls when possible (send all data at once)

  • Don’t call inside tight loops

  • Consider async enrichment for high-throughput applications

Backwards compatibility issues:

  • The function accepts both old and new signatures

  • user_properties is stored as a separate field (not merged into metadata)

  • session_id can be positional or keyword argument

  • All enrichment data is gracefully merged

Comparison with enrich_span

Feature

enrich_span()

enrich_session()

Scope

Single span

Entire session

Storage

OpenTelemetry attributes

HoneyHive backend API

Persistence

Local to trace

Backend persisted

API Calls

No

Yes

Complex Data

Limited (OTel constraints)

Full support

Performance

Instant

~50-200ms per call

Use Case

Operation-level context

Workflow-level context

Next Steps

Key Takeaway: Use enrich_session() to add workflow-level context that persists across all spans in a session and is stored in the HoneyHive backend for comprehensive analysis. ✨