# `SuperPi.Telemetry`
[🔗](https://github.com/oleksandr/super-pi-beam/blob/v0.1.0/lib/super_pi/telemetry.ex#L1)

Traces and metrics for conversations.

## Traces (OpenTelemetry)

| Span | Where | Attributes |
|---|---|---|
| `super_pi.gateway.request` | each HTTP request | `http.request.method`, `url.path`, `http.response.status_code` |
| `super_pi.turn` | one user submit, from input to idle | `super_pi.conversation.id`, `super_pi.task.id` |
| `chat <model>` | one provider call | `gen_ai.request.model`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` |
| `execute_tool <name>` | one tool call | `gen_ai.tool.name`, `super_pi.tool.error` |

A request span is the parent of the turn span, the turn span of its provider and tool spans.
A span that fails has status `error` and the reason as its message.

Spans are no-ops until an exporter is configured. Set `OTEL_EXPORTER_OTLP_ENDPOINT`
(for example `http://localhost:4318`) and the app exports over OTLP/HTTP. The standard
`OTEL_SERVICE_NAME` and `OTEL_RESOURCE_ATTRIBUTES` apply.

## Metrics (`:telemetry`, Prometheus)

The same code emits `:telemetry` events: `[:super_pi, :turn | :llm | :tool, :stop]` with a
`:duration` in native units, plus `:input_tokens` and `:output_tokens` for `:llm`.
`metrics/0` defines counters, duration histograms and token sums over them. The gateway
serves them at `GET /metrics` in Prometheus text format, behind the same authentication as
the rest of the API.

Prompt and completion text and tool arguments are never put in a span or a metric.

# `handle`

```elixir
@type handle() :: %{span: term(), token: term(), started: integer(), meta: map()}
```

# `attach`

Makes `ctx` current in this process. Pair it with `current_ctx/0`.

# `current_ctx`

The current trace context. Capture it before spawning a process that continues the work.

# `llm_span`

Runs `fun` inside a provider-call span. `fun` returns the responder's result, `{:ok, message}`
or `{:error, reason}`. Token counts come from `message["usage"]`.

# `metrics`

`Telemetry.Metrics` definitions for the events above.

# `scrape`

The Prometheus text exposition of the metrics, or an empty string if the reporter is not running.

# `start_span`

```elixir
@spec start_span(String.t(), map(), term()) :: handle()
```

Starts a span, makes it current in this process, and returns a handle for `stop_span/3`.
`parent` is a context from `current_ctx/0`, or `nil` for the current one.

# `stop_span`

Ends the span and restores the previous context. `status` is `:ok` or `{:error, reason}`.
Returns the duration in native time units. `attributes` are added before it ends.

# `tool_span`

Runs `fun` inside a tool-call span. `fun` returns a tool result map; `"is_error"` marks failure.

# `turn_start`

Starts the span for a turn. Call `turn_stop/2` when the conversation is idle again.

# `turn_stop`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
