Sessions
Sessions group related agent runs under a single identifier. A common use case is tracking multi-turn conversations where each user message triggers a separate agent run, but you want to analyze the entire conversation as a unit.
What Gets Tracked
When runs share a session_id, Waxell Observe automatically aggregates:
| Metric | Description |
|---|---|
run_count | Number of runs in the session |
first_run | Timestamp of the earliest run |
last_activity | Timestamp of the most recent run |
total_duration | Combined execution time across all runs (seconds) |
total_cost | Sum of LLM costs across all runs (USD) |
total_tokens | Sum of tokens used across all runs |
agents | List of distinct agent names that participated |
Setting a Session ID
Context Manager
Pass session_id when creating a WaxellContext:
from waxell_observe import WaxellContext
async with WaxellContext(
agent_name="chat-agent",
session_id="session-abc123",
) as ctx:
response = await generate_response(user_message)
ctx.record_llm_call(
model="gpt-4o",
tokens_in=response.usage.prompt_tokens,
tokens_out=response.usage.completion_tokens,
)
ctx.set_result({"output": response.text})
Decorator
Pass session_id to the @observe decorator:
import waxell_observe as waxell
waxell.init()
@waxell.observe(agent_name="chat-agent", session_id="session-abc123")
async def handle_message(user_message: str) -> str:
response = await llm.chat(user_message)
return response.text
Dynamic Session IDs
For real applications, you typically derive the session ID from your application's conversation or request context:
from waxell_observe import WaxellContext
from waxell_observe.context import generate_session_id
# Generate a new session ID (format: sess_ + 16 hex chars)
session_id = generate_session_id()
# e.g. "sess_a1b2c3d4e5f60718"
# Or use your own identifier
session_id = f"conv-{conversation.id}"
async with WaxellContext(
agent_name="chat-agent",
session_id=session_id,
) as ctx:
# All runs with this session_id are grouped together
...
The session_id is propagated to both the HTTP data path (stored on the AgentExecutionRun record) and the OTel tracing path (as a waxell.session_id span attribute). This means sessions are queryable from both the Waxell UI and Grafana TraceQL.
REST API
List Sessions
GET /api/v1/observability/sessions/
Authentication: Session (UI)
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
search | string | Filter by session_id (substring match) | |
agent | string | Filter by agent name | |
start | ISO8601 | Only sessions with runs after this time | |
end | ISO8601 | Only sessions with runs before this time | |
sort | string | -last_activity | Sort field. Options: last_activity, -last_activity, first_run, -first_run, run_count, -run_count |
limit | int | 25 | Page size (max 100) |
offset | int | 0 | Pagination offset |
Example:
curl -s "https://acme.waxell.dev/api/v1/observability/sessions/?limit=10&sort=-run_count" \
-H "Cookie: sessionid=..."
Response:
{
"results": [
{
"session_id": "sess_a1b2c3d4e5f6g7h8",
"run_count": 5,
"first_run": "2026-02-07T10:00:00Z",
"last_activity": "2026-02-07T10:05:32Z",
"total_duration": 12.45,
"total_cost": 0.0234,
"total_tokens": 4520,
"agents": ["chat-agent", "retrieval-agent"]
}
],
"count": 42,
"next": "?offset=10&limit=10",
"previous": null
}
Get Session Detail
GET /api/v1/observability/sessions/{session_id}/
Authentication: Session (UI)
Returns the session's aggregate metrics and a chronological list of all runs.
Example:
curl -s "https://acme.waxell.dev/api/v1/observability/sessions/sess_a1b2c3d4e5f6g7h8/" \
-H "Cookie: sessionid=..."
Response:
{
"session_id": "sess_a1b2c3d4e5f6g7h8",
"aggregates": {
"run_count": 5,
"total_duration": 12.45,
"total_cost": 0.0234,
"total_tokens": 4520,
"agents": ["chat-agent", "retrieval-agent"]
},
"runs": [
{
"id": 101,
"agent_name": "chat-agent",
"workflow_name": "default",
"started_at": "2026-02-07T10:00:00Z",
"completed_at": "2026-02-07T10:00:02Z",
"duration": 2.1,
"status": "success",
"cost": 0.0045,
"tokens": 890,
"trace_id": "abcdef1234567890abcdef1234567890"
}
]
}
UI Walkthrough
Sessions List
The sessions list view shows all tracked sessions with sortable columns:
- Session ID -- click to open session detail
- Runs -- number of agent runs in the session
- First Run / Last Activity -- time range of the session
- Duration -- total execution time
- Cost -- aggregated LLM spend
- Tokens -- total token usage
- Agents -- which agents participated
Use the search bar to filter by session ID, or the agent dropdown to see sessions for a specific agent.
Session Detail
The session detail page shows:
- Summary cards at the top with run count, total duration, total cost, and total tokens
- Vertical timeline of runs in chronological order, showing each run's agent, duration, cost, and status
- Click any run to navigate to its full trace detail
Multi-Agent Sessions
Sessions are especially useful for multi-agent workflows where several agents collaborate on a single request. Pass the same session_id to each agent:
session_id = generate_session_id()
async with WaxellContext(agent_name="router", session_id=session_id) as ctx:
intent = await classify_intent(query)
ctx.set_result({"intent": intent})
async with WaxellContext(agent_name="retrieval", session_id=session_id) as ctx:
docs = await search_documents(query)
ctx.set_result({"doc_count": len(docs)})
async with WaxellContext(agent_name="synthesizer", session_id=session_id) as ctx:
answer = await synthesize(query, docs)
ctx.set_result({"output": answer})
All three runs appear under the same session, giving you a complete picture of the multi-agent pipeline.
Next Steps
- User Tracking -- Attribute sessions and costs to individual users
- Scoring -- Attach quality scores to runs within a session
- LLM Call Tracking -- Understand token and cost breakdown per run