Observability

Sessions

A session is the top-level unit of agent execution in Niitaka. Every time your agent runs — from the first API call to the final output — one session captures the full trace: goals, costs, events, errors, and outcome metrics.

Session lifecycle

A session starts running and ends in one of four terminal statuses.

running

  • Session open
  • Events streaming in
  • Cost accumulating
→

done

  • Context manager exited clean
  • Metrics finalised
  • ended_at stamped
→

error

  • Explicit failure status
  • ended_at stamped
  • Not authoritative — use error_count
→

stopped

  • Explicit abort
  • ended_at stamped
→

stale

  • No heartbeat for 5 min
  • Auto-closed by server
  • ended_at stamped
Warning:status = 'error' is real and reachable (set explicitly via PATCH /sessions/{id}) — but it's not authoritative for detecting failed runs. error_count isn't a stored column either; it's derived at query time from events where type='error'. Always detect failures via error_count > 0, never by filtering on session status.

Key fields

idUUID. Use this to link child sessions, events, and signals back to the run.
goalA natural-language description of what the agent was trying to accomplish. Set at session start.
agent_idMatches the agent registered in the Agent Dashboard. Used for filtering, stats, and policy lookup.
statusrunning while active, then one terminal status: done (clean exit) · error (explicit failure) · stopped (explicit abort) · stale (no heartbeat for 5 minutes). Not authoritative for error detection — see the callout above.
ended_atServer-stamped, write-once, the first time the session reaches a terminal status. null means never explicitly closed — the dashboard falls back to an estimated duration from the last event, shown with a "≈" prefix.
total_costCumulative LLM cost in USD across all events in this session.
error_countNumber of error events logged. Greater than 0 means the run encountered at least one error.
parent_session_idLinks this session to a parent. Enables tree view for multi-agent or orchestrator→worker flows.
experiment_idSet when the session is part of an A/B experiment. Combined with variant to route the run.
metricsOutcome metrics set via set_metrics() inside the session context — goal_completed, score, and up to 10 custom keys.

Duration: real vs. estimated

Duration prefers ended_at - created_at when the session has actually reached a terminal status — authoritative, server-stamped. If ended_at is still null (abandoned or crashed without ever closing, or genuinely still running), the dashboard falls back to the last event's timestamp instead and shows it with a ≈ prefix so an estimate is never confused with a real closed-session duration.

Starting a session

Use the start_session() context manager. The session is opened on entry and closed automatically on exit — even if the agent raises an exception.

python
import niitaka

with niitaka.start_session(
    goal="Summarise the weekly sales report",
    agent_id="report-summariser",
) as session:
    result = run_agent(report_text)

    session.set_metrics({
        "goal_completed": result.success,
        "score":          result.quality,   # float 0–1
    })

Pre-creating a parent session

For a chatbot or multi-turn workflow, use create_session() to establish a stable parent session before individual turns start, instead of wrapping the whole conversation in one long-lived context manager. Fire-and-forget and a no-op when unconfigured.

python
# For a chatbot/multi-turn workflow, establish a stable parent before
# individual turns start. Fire-and-forget, idempotent, safe to repeat:
niitaka.create_session(
    session_id=chat_id,
    goal=f"Chat with {user_name}",
    agent_id="my-chatbot",
    tags={"user_id": user_id},
)

# Each turn is a child session
with niitaka.start_session(
    goal=user_message[:200],
    agent_id="my-chatbot",
    parent_session_id=chat_id,
    tags={"user_id": user_id, "chat_id": chat_id},
) as session:
    response = run_agent(user_message)

Child sessions

Pass parent_session_id to link a sub-session to its parent. The Dashboard renders these as a collapsible tree so you can trace multi-agent orchestration flows end-to-end.

python
# Top-level session
with niitaka.start_session(
    goal="Process customer support ticket",
    agent_id="support-router",
) as parent:
    intent = classify_intent(ticket)

    # Sub-session linked to the parent
    with niitaka.start_session(
        goal="Draft reply for billing query",
        agent_id="reply-drafter",
        parent_session_id=parent.session_id,
    ) as child:
        reply = draft_reply(ticket, intent)
        child.set_metrics({"goal_completed": reply is not None})

Detecting errors programmatically

python
# status == 'error' is real and reachable, but it's not authoritative for
# error detection -- error_count isn't even a stored column, it's derived
# at query time from the events table. Check error_count, not status:
sessions = niitaka_api.list_sessions(agent_id="report-summariser")
failed = [s for s in sessions if s["error_count"] > 0]

Tool calls

Every instrumentation path (OpenAI, Anthropic, Gemini, Groq, LiteLLM, LangChain) normalizes a model's tool/function-call decision to one canonical shape. A pure tool-call turn legitimately has empty content — the dashboard renders tool_calls as explicit → name(args) blocks instead of showing what used to look like an empty response:

json
{
  "role": "assistant",
  "content": "",                 // legitimately empty for a pure tool-call turn
  "tool_calls": [
    {
      "id":        "call_abc123",
      "name":      "get_weather",
      "arguments": { "city": "Tokyo" }   // always a parsed dict, never a raw JSON string
    }
  ]
}

Sessions dashboard

The Sessions page gives you a real-time view of all runs in your organisation.

Total Sessions

Count for the selected date range.

Running

Sessions currently open (live count).

Total Cost

Summed LLM spend across all sessions.

Unique Agents

Distinct agent IDs seen in the range.

Filtering and search

  • Date range — presets for last 24 h, 7 d, 30 d, or a custom range.
  • Status — filter to running, done, error, stopped, or stale sessions.
  • Agent — scope the list to a single agent ID.
  • Search — full-text match against the session goal.

Tree view

Sessions with parent_session_id set are rendered as a collapsible subtree under their parent. Expand a row to see all child sessions without leaving the list. Depth is limited to prevent runaway recursion in deeply nested orchestrators.

Session drawer

Click any row to open the session drawer. It shows the event timeline, cost breakdown, signals fired during the run, and the decision traces from the policy engine.

Export

Use the Export button to download the current filtered view as CSV. Useful for offline analysis or sharing a slice of sessions with your team.

Live updates

The session list streams updates in real time via Server-Sent Events (SSE). New sessions appear at the top, and running sessions update their cost and status without a page refresh.

Next steps

  • Agent Dashboard — monitor agent health and manage lifecycle across all agents.
  • Signals & Alerts — see what the policy engine emitted during each session.
  • Experiments — attach sessions to A/B experiments and compare variants.