Skip to main content
Layers of visibility, from inside a call out to your dashboards and billing.

1. Per-turn timing & token hooks

The real per-turn timing and token numbers arrive on two hooks the core fires every turn. Register them on the session:
llm_call and turn_complete carry the actual per-turn latency and token data. The CallMetrics snapshot below (metrics.live()) only populates if you feed it from these hooks — see the caveat in Layer 2.

2. Live per-call snapshot (opt-in)

Inside (or after) session.run(), take a CallMetrics snapshot:
The latency/cost/token fields populate only if you call metrics.record_turn(...) yourself — typically from the turn_complete hook above. The SDK does not auto-fill them, so out of the box only duration_s is meaningful. For real numbers, use the per-turn hooks (Layer 1) or the post-call transcript timing (Layer 5).

3. Usage & billing ledger

The SDK buffers per-session LLM usage and flushes it to the cloud billing ledger on call_end. It is best-effort: a no-op when UNPOD_USAGE_INGEST_URL is unset, and never blocks or fails the call.
Counters posted per session: Prompt-cache read/write tokens are forwarded so cached turns are billed at the correct (lower) rate.

4. Langfuse tracing

When LANGFUSE_SECRET_KEY is set, the SDK emits per-turn spans plus a generation span per LLM call (with token usage). No wiring needed — set the key and traces appear in Langfuse. When unset, tracing is a no-op.

5. Runner pool stats

Poll this on a timer for liveness dashboards — see AgentRunner & Sessions.

6. Post-call timing

After the call, the transcript carries a per-turn, per-stage latency breakdown (audio_ingress_ms, stt_ms, bridge_to_dev_ms, dev_brain_ms, tts_ms) — see Recordings & Transcripts.
High dev_brain_ms with healthy stt_ms/tts_ms means the latency is in YOUR brain — usually a stream() that is not actually streaming. See Streaming is the hot path.