> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Metrics

> Read latency and token usage for each pipeline stage with metrics hooks, see them per turn on the Dashboard, and export them to Datadog, SigNoz, New Relic, or any OpenTelemetry backend.

Zero Runtime measures every stage of the pipeline on every turn: how long speech-to-text took, how fast the LLM produced its first token, how many tokens it used, and how quickly text-to-speech started speaking. You can read these metrics in three places:

| Where | Use it to |
| - | - |
| [Metrics hooks](#get-metrics-for-each-stage) | React to a stage's metrics in your own code, as each turn completes. |
| [Session Dashboard](#view-metrics-on-the-dashboard) | Review per-turn latency, tokens, and cost after a session. |
| [Your observability stack](#export-metrics-to-your-observability-stack) | Ship metrics to Datadog, SigNoz, New Relic, or any OpenTelemetry backend. |

## Get metrics for each stage

Register a hook on `pipeline.metrics` for the stage you want. Each hook fires once per turn, when that stage finishes, and receives the stage's metrics as a dictionary. Hooks only observe: they never change what the pipeline does, so you can register as many as you need.

```python title="Python" Python theme={null}
import logging

from zeroruntime import Pipeline
from zeroruntime.plugins import SileroVAD
from zeroruntime.inference import TurnDetector, DeepgramSTT, GoogleLLM, CartesiaTTS

logger = logging.getLogger(__name__)

pipeline = Pipeline(
    stt=DeepgramSTT(),
    llm=GoogleLLM(),
    tts=CartesiaTTS(),
    vad=SileroVAD(),
    turn_detector=TurnDetector(),
)


@pipeline.metrics.on("stt")
def on_stt_metrics(data: dict) -> None:
    logger.info("stt %s", data)


@pipeline.metrics.on("llm")
def on_llm_metrics(data: dict) -> None:
    logger.info("llm %s", data)


@pipeline.metrics.on("tts")
def on_tts_metrics(data: dict) -> None:
    logger.info("tts %s", data)
```

```typescript title="Node JS" Node JS theme={null}
import { Pipeline } from '@zeroruntime/js-sdk';
import { SileroVAD } from '@zeroruntime/js-sdk/plugins';
import { TurnDetector, DeepgramSTT, GoogleLLM, CartesiaTTS } from '@zeroruntime/js-sdk/inference';

const pipeline = Pipeline({
  stt: DeepgramSTT(),
  llm: GoogleLLM(),
  tts: CartesiaTTS(),
  vad: SileroVAD(),
  turn_detector: TurnDetector(),
});

pipeline.metrics.on('stt', async (data: Record<string, any>) => {
  console.log('stt', data);
});

pipeline.metrics.on('llm', async (data: Record<string, any>) => {
  console.log('llm', data);
});

pipeline.metrics.on('tts', async (data: Record<string, any>) => {
  console.log('tts', data);
});
```

Each stage reports its own fields. Latencies are in milliseconds.

| Stage | Fires when | Fields |
| - | - | - |
| `stt` | Speech-to-text finishes a transcript. | `stt_latency` |
| `llm` | The LLM finishes its response. | `llm_ttft`, `llm_duration`, `prompt_tokens`, `completion_tokens`, `total_tokens` |
| `tts` | Text-to-speech finishes speaking. | `ttfb`, `tts_latency` |
| `eou` | The turn detector decides the user has finished speaking. | `eou_latency`, `eou_wait_ms` |
| `realtime` | A realtime model finishes a turn, in Realtime or Hybrid mode. | `realtime_ttfb` and input, output, text, and audio token counts |
| `knowledge_base` | A knowledge base search completes. Python only. | `kb_id`, `kb_retrieval_latency`, `kb_retrievals` |

For every field and a complete example per stage, see [Observability Hooks](https://docs.zeroruntime.ai/runtime-sdk/build/configure-a-pipeline/observability-hooks.md).

## View metrics on the Dashboard

You don't need hooks to see metrics. Zero Runtime records them for every session and shows them on the **Sessions** page of the Zero Runtime Dashboard. Each session lists its latency percentiles, total tokens, and total cost, and the transcript shows the latency of every turn: STT and end-of-utterance latency on user messages, TTS and end-to-end latency on agent messages.

### Session List View

![Sessions list showing Session ID, Room ID, created time, duration, latency, total cost, recording, and status](https://cdn.zeroruntime.ai/zrt/docs-resource/analytics-sessions-list.png)

Sessions list on the Zero Runtime Dashboard

### Session Transcript View

![Session view showing session totals and latency percentiles in the header, and the transcript with timestamps, Agent and User labels, and per-message latency](https://cdn.zeroruntime.ai/zrt/docs-resource/analytics-session-transcript.png)

Session view with the transcript, per-message latency, and recording playback

## Export metrics to your observability stack

To send metrics to your own observability stack, such as Datadog, SigNoz, or New Relic, set `metrics` on the room's `Observability` to an `Exporter` that points at your collector's OpenTelemetry (OTLP) endpoint.

```python title="Python" Python theme={null}
import zeroruntime
from zeroruntime import Exporter, Observability, Room

if __name__ == "__main__":
    zeroruntime.serve(
        MyVoiceAgent,
        room=Room(
            name="Observability Hooks",
            observability=Observability(
                metrics=Exporter(
                    enabled=True,
                    export_url="https://otel-collector.example.com/v1/metrics",
                ),
            ),
        ),
    )
```

```typescript title="Node JS" Node JS theme={null}
import * as zeroruntime from '@zeroruntime/js-sdk';
import { Exporter, Observability, Room } from '@zeroruntime/js-sdk';

zeroruntime.serve(MyVoiceAgent, {
  room: Room({
    name: 'Observability Hooks',
    observability: Observability({
      metrics: Exporter({
        enabled: true,
        export_url: 'https://otel-collector.example.com/v1/metrics',
      }),
    }),
  }),
});
```

Replace the example URL with your collector's metrics endpoint. Set the `Room` on `serve()` to apply it to every session, or on `invoke()` to apply it to one session.

| Exporter field | Type | Default | Description |
| - | - | - | - |
| `enabled` | `bool` | `True` | Turns the metrics export on or off. Set it to `False` to stop exporting while keeping the rest of the configuration. |
| `export_url` | `str` | `None` | The OTLP endpoint to send metrics to. `None` uses the runtime's default destination. |
| `export_headers` | `dict[str, str]` | `{}` | Headers sent with each export. Put your vendor's API key or auth header here. |

> [!NOTE]
>
> Leaving `metrics` unset is not the same as turning it off. An unset stream follows the runtime's default settings. To stop exporting metrics, pass `Exporter(enabled=False)`.

## What's Next

> [card] [Logs](https://docs.zeroruntime.ai/runtime-sdk/build/observability-and-monitoring/melt/logs.md)
>
> Set the log level and ship agent logs to your stack.

> [card] [Traces](https://docs.zeroruntime.ai/runtime-sdk/build/observability-and-monitoring/melt/traces.md)
>
> Follow each turn span by span and export the spans.

> [card] [Observability Hooks](https://docs.zeroruntime.ai/runtime-sdk/build/configure-a-pipeline/observability-hooks.md)
>
> Every metrics field, with an example per stage.

## References

> [tab] Python
>
> #### Examples
>
> > [card] [Observability Hooks](https://github.com/ZeroRuntimeAI/zeroruntime-python-examples/blob/main/observability/observability_hooks.py)
> >
> > Read per-stage metrics with pipeline hooks.

> [tab] Node JS
>
> #### Examples
>
> > [card] [Pipeline Events](https://github.com/ZeroRuntimeAI/zeroruntime-js-examples/blob/main/observability/pipeline_events.ts)
> >
> > Read per-stage metrics with pipeline hooks.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.