The AI engine translates pydantic-ai OpenTelemetry spans into PostHog
`$ai_generation`/`$ai_trace` events via a custom SpanProcessor. Two gaps
in that path let LLM telemetry stop silently and stay at zero:
- `PostHogSpanProcessor.on_end` was unguarded. OpenTelemetry calls it
synchronously from `Span.end()` with no exception handling of its own,
and pydantic-ai ends the model span inside a `with` block, so any error
while translating a span propagates into the model request itself. A
telemetry defect could therefore break the agent run and/or halt
emission for every subsequent span. It now translates and captures
defensively and logs-and-drops on failure.
- The PostHog client was created without an `on_error` callback. Its
`capture` is decorated `@no_throw` and delivery happens on a background
consumer thread, so a failing or stalled delivery pipeline was
completely invisible. We now log delivery failures and log the
enabled/disabled state at startup, so a halt is noticeable in minutes
instead of going unnoticed.
Adds unit tests covering the happy path, per-trace dedup, non-chat spans,
and that `on_end` never raises on delivery failure or a malformed span.
Generated-By: PostHog Code
Task-Id: 505d85dd-9cd2-4258-b76a-e027f4240277