# AI Observability

The AI Observability module logs calls to AI providers to the Drupal Logger
(currently) and OpenTelemetry endpoints (in the future), including detailed
information like request duration, token usage, optionally input prompts text
and output, etc.

This makes it possible to track the usage of tokens per user, per provider,
build aggregated reports and usage charts, calculate cost usage and expenses,
limit per-week token usage per user, and many more.

This module only produces logs, that can be stored anywhere - in the local Drupal
database, Syslog, file, cloud log collectors.

For quick start and local development it's recommended to use the "Extended Logger"
module in conjunction with its submodule, "Extended Logger DB", to store logs
generated by this module into the Drupal Database as well as providing a page
to review these logs.

For production, it's recommended to use production-ready log scrappers.

## OpenTelemetry attribute schema

When OpenTelemetry export is enabled, the module emits spans and metrics using the
OpenTelemetry [GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
(`gen_ai.*`). These attributes are **dual-emitted** alongside the module's original
ad-hoc attributes, so existing dashboards keep working while consumers can migrate to
the standard schema. The `gen_ai.*` attributes are Experimental in the OpenTelemetry
specification.

### Span attributes (`AI provider request`)

Standard GenAI attributes (emitted unconditionally when span export is on):

| Attribute | Source | Notes |
|---|---|---|
| `gen_ai.provider.name` | provider plugin id | Mapped to a well-known value (`openai`, `anthropic`, `gcp.vertex_ai`, …); unknown providers fall back to the raw plugin id. |
| `gen_ai.operation.name` | operation type | Only the well-known values `chat`, `text_completion`, `embeddings` are emitted; non-standard operations (e.g. image generation) are omitted. |
| `gen_ai.request.model` | requested model id | |
| `gen_ai.usage.input_tokens` | token usage | Scalar; explicit `0` is preserved. |
| `gen_ai.usage.output_tokens` | token usage | Scalar; explicit `0` is preserved. |
| `gen_ai.response.model` | raw provider response `model` | Best-effort; emitted when present. |
| `gen_ai.response.finish_reasons` | raw provider response | Array of finish reasons (OpenAI `finish_reason` per choice / Anthropic `stop_reason`). For streamed responses the finish reason is taken from the stream iterator (requires the streaming-span finalization fix, [#3586473](https://www.drupal.org/i/3586473)). |

A sample exported span dump:

```json
{
  "name": "AI provider request",
  "attributes": {
    "gen_ai.provider.name": "openai",
    "gen_ai.operation.name": "chat",
    "gen_ai.request.model": "gpt-4o-mini",
    "gen_ai.usage.input_tokens": 11,
    "gen_ai.usage.output_tokens": 27,
    "gen_ai.response.model": "gpt-4o-mini-2024-07-18",
    "gen_ai.response.finish_reasons": ["stop"],

    "provider": "openai",
    "operation_type": "chat",
    "model": "gpt-4o-mini",
    "token_usage": {"input": 11, "output": 27, "total": 38}
  }
}
```

### Metrics

| Instrument | Type | Dimensions | Notes |
|---|---|---|---|
| `gen_ai.client.token.usage` | histogram | `gen_ai.token.type` (`input`/`output`), `gen_ai.provider.name`, `gen_ai.request.model` | Records input and output token counts. |

### Legacy attributes (deprecated)

The original ad-hoc attributes and counters remain for backwards compatibility but are
**deprecated, with removal targeted for 2.0.x**. Migrate consumers to the `gen_ai.*`
schema above.

- Span attributes: `provider`, `operation_type`, `model`, `token_usage` (an array; `NULL`
  and `0` values are filtered out).
- Metric counters: `ai_token_usage_<key>` (one counter per token-usage key).
