Skip to content

feat: PostHog LLM observability ($ai_generation traces) - #320

Open
farhat-is-coding wants to merge 1 commit into
devfrom
farhat/feat/posthog-llm-observability
Open

feat: PostHog LLM observability ($ai_generation traces)#320
farhat-is-coding wants to merge 1 commit into
devfrom
farhat/feat/posthog-llm-observability

Conversation

@farhat-is-coding

Copy link
Copy Markdown
Collaborator

What

Integrates PostHog AI observability via manual capture: one $ai_generation event per LLM provider call, giving per-turn trace waterfalls (prompt + output + tool rounds), token/cost dashboards, latency breakdowns (incl. time-to-first-token), and error-rate tracking across all providers.

How

Capture happens at a single choke pointLLMService._execute_llm_completion — so chat, Socratic, workflow steps, and post-tool follow-up calls are all covered with zero per-caller instrumentation:

  • core/services/llm_observability_service.pyLLMObservabilityService (process-wide singleton, PostHog client) + GenerationTracker (per-call accumulator; finish() runs from finally, so client disconnects still capture).
  • core/services/dtos/llm_generation_dto.py — frozen LLMGenerationContext / LLMGenerationRecord DTOs (rules.md §1).
  • Trace identity: message id → $ai_trace_id (one chat turn incl. tool follow-ups), conversation id → $ai_session_id, user id → distinct_id (anonymous for public bots).
  • Error semantics: catches raised exceptions and the provider services' "Error: ..." sentinel chunks (which never raise) — so silent provider failures become visible.

Config

Env var Default Behavior
POSTHOG_API_KEY unset unset = complete no-op (safe local/CI)
POSTHOG_HOST https://us.i.posthog.com or EU host
POSTHOG_LLM_CAPTURE_CONTENT True False = telemetry only, no prompt/response content

Safety

  • Strictly fire-and-forget: every observability method swallows its own exceptions; a PostHog outage can never break or slow a chat turn (verified: never-raise smoke test).
  • PostHog client sends from a background thread — no added latency on the stream path.

Verification

  • python manage.py check clean; black + isort (black profile) clean on all touched Python files.
  • Smoke-tested 7 paths: disabled no-op, enabled capture payload shape, anonymous/auto-trace defaults, error sentinel, exception path, capture-never-raises, content-privacy flag.
  • Live end-to-end pending env vars (POSTHOG_API_KEY) — integration doc at docs/integration/posthog-llm-observability.md.

Emit one $ai_generation event per LLM provider call at the single
LLMService._execute_llm_completion choke point, covering all providers
(OpenAI, Claude, Gemini, Llama, custom/LiteLLM), streaming and structured
paths, and post-tool follow-up calls.

- LLMObservabilityService: process-wide PostHog client, strict no-op when
  POSTHOG_API_KEY is unset, fire-and-forget (never raises into chat path)
- GenerationTracker: per-call accumulator for chunks, usage, first-token
  time; finish() runs from finally so aborted streams still capture
- LLMGenerationContext / LLMGenerationRecord DTOs per rules.md
- Trace identity: message id -> $ai_trace_id, conversation id ->
  $ai_session_id, user id -> distinct_id
- Detects both raised exceptions and provider 'Error:' sentinel chunks
- POSTHOG_LLM_CAPTURE_CONTENT=False keeps telemetry but omits content
- docs/integration/posthog-llm-observability.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant