# Observability (Production-Grade)

Entropic Core can emit structured runtime events for every LLM request/response/error.
You can export them to:
- JSONL (local files)
- OpenTelemetry (traces)
- Prometheus (metrics)

This is different from `entropic_core.telemetry`, which is opt-in anonymous usage telemetry.

## 1) JSONL Events (No Dependencies)

```python
from pathlib import Path

import entropic_core
from entropic_core.core.llm_middleware import LLMMiddleware, MiddlewareConfig
from entropic_core.core.runtime_events import JsonlEventSink
from openai import OpenAI

sink = JsonlEventSink(Path("runs/events.jsonl"))
cfg = MiddlewareConfig(event_sink=sink, run_id="run_local_001")
mw = LLMMiddleware(config=cfg)

client = OpenAI(api_key="...", base_url="https://openrouter.ai/api/v1")
entropic_core.wrap(client, provider="openrouter", middleware=mw)
```

You will get a JSONL stream with `request`, `response`, and `error` events.

### Runtime event contract

Events are versioned via `schema_version` and the packaged JSON Schema:
- `src/entropic_core/core/runtime_events.schema.json`

Event `type` values:
- `request`, `response`, `tool`, `intervention`, `outcome`, `error`

### Runtime report (experimental)

Generate a shareable HTML summary from `events.jsonl`:

```powershell
entropic-runtime-report --events runs/events.jsonl --out runs/report
```

## 2) OpenTelemetry Traces (Experimental)

Install:

```powershell
python -m pip install "entropic-core[otel]"
```

Use:

```python
from entropic_core.core.llm_middleware import LLMMiddleware, MiddlewareConfig
from entropic_core.experimental.observability import OpenTelemetryEventSink

sink = OpenTelemetryEventSink(service_name="entropic-core")
cfg = MiddlewareConfig(event_sink=sink, run_id="run_prod_001")
mw = LLMMiddleware(config=cfg)
```

Notes:
- By default this sink uses a local tracer provider and writes spans to the console.
- In production, pass your own tracer/provider from your app.

## 3) Prometheus Metrics (Experimental)

Install:

```powershell
python -m pip install "entropic-core[prometheus]"
```

Use:

```python
from entropic_core.core.llm_middleware import LLMMiddleware, MiddlewareConfig
from entropic_core.experimental.observability import PrometheusEventSink

sink = PrometheusEventSink(include_model_label=True)
sink.start_http_server(port=8000)

cfg = MiddlewareConfig(event_sink=sink, run_id="run_prod_001")
mw = LLMMiddleware(config=cfg)
```

Metrics exported (namespace `entropic_` by default):
- `entropic_requests_total`
- `entropic_responses_total`
- `entropic_errors_total`
- `entropic_latency_ms`
- `entropic_tokens_total{kind="prompt|completion|total"}`
- `entropic_entropy{stage="before|after"}`
- `entropic_interventions_total{kind="..."}`

## Operational Notes

- Event emission is best-effort and must never break the LLM call path.
- Keep label cardinality under control. If you have many models, set `include_model_label=False`.
