Commit 1875b71
authored
feat(ai): add OpenAI Agents SDK integration (#408)
* feat(ai): add OpenAI Agents SDK integration
Add PostHogTracingProcessor that implements the OpenAI Agents SDK
TracingProcessor interface to capture agent traces in PostHog.
- Maps GenerationSpanData to $ai_generation events
- Maps FunctionSpanData, AgentSpanData, HandoffSpanData, GuardrailSpanData
to $ai_span events with appropriate types
- Supports privacy mode, groups, and custom properties
- Includes instrument() helper for one-liner setup
- 22 unit tests covering all span types
* feat(openai-agents): add $ai_group_id support for linking conversation traces
- Capture group_id from trace and include as $ai_group_id on all events
- Add _get_group_id() helper to retrieve group_id from trace metadata
- Pass group_id through all span handlers (generation, function, agent, handoff, guardrail, response, custom, audio, mcp, generic)
- Enables linking multiple traces in the same conversation thread
* feat(openai-agents): add enhanced span properties
- Add $ai_total_tokens to generation and response spans (required by PostHog cost reporting)
- Add $ai_error_type for cross-provider error categorization (model_behavior_error, user_error, input_guardrail_triggered, output_guardrail_triggered, max_turns_exceeded)
- Add $ai_output_choices to response spans for output content capture
- Add audio pass-through properties for voice spans:
- first_content_at (time to first audio byte)
- audio_input_format / audio_output_format
- model_config
- $ai_input for TTS text input
- Add comprehensive tests for all new properties
* Add $ai_framework property and standardize $ai_provider for OpenAI Agents
- Add $ai_framework="openai-agents" to all events for framework identification
- Standardize $ai_provider="openai" on all events (previously some used "openai_agents")
- Follows pattern from posthog-js where $ai_provider is the underlying LLM provider
* chore: bump version to 7.7.0 for OpenAI Agents SDK integration
* fix: add openai_agents package to setuptools config
Without this, the module is not included in the distribution
and users get an ImportError after pip install.
* fix: correct indentation in on_trace_start properties dict
* fix: prevent unbounded growth of span/trace tracking dicts
Add max entry limit and eviction for _span_start_times and
_trace_metadata dicts. If on_span_end or on_trace_end is never
called (e.g., due to an SDK exception), these dicts could grow
indefinitely in long-running processes.
* fix: resolve distinct_id from trace metadata in on_span_end
Previously on_span_end always called _get_distinct_id(None), which
meant callable distinct_id resolvers never received the trace object
for spans. Now the resolved distinct_id is stored at trace start and
looked up by trace_id during span end.
* refactor: extract _base_properties helper to reduce duplication
All span handlers repeated the same 6 base fields (trace_id, span_id,
parent_id, provider, framework, latency) plus the group_id conditional.
Extract into a shared helper to reduce ~100 lines of boilerplate.
* test: add missing edge case tests for openai agents processor
- test_generation_span_with_no_usage: zero tokens when usage is None
- test_generation_span_with_partial_usage: only input_tokens present
- test_error_type_categorization_by_type_field_only: type field without
matching message content
- test_distinct_id_resolved_from_trace_for_spans: callable resolver
uses trace context for span events
- test_eviction_of_stale_entries: memory leak prevention works
* fix: handle non-dict error_info in span error parsing
If span.error is a string instead of a dict, calling .get() would
raise AttributeError. Now falls back to str() for non-dict errors.
* style: apply ruff formatting
* style: replace lambda assignments with def (ruff E731)
* fix: restore full CHANGELOG.md history
The rebase conflict resolution accidentally truncated the changelog
to only the most recent entries. Restored all historical entries.
* fix: preserve personless mode for trace-id fallback distinct IDs
When no distinct_id is provided, _get_distinct_id falls back to
trace_id or "unknown". Since these are non-None strings, the
$process_person_profile=False check in _capture_event never fired,
creating unwanted person profiles keyed by trace IDs.
Track whether the user explicitly provided a distinct_id and use
that flag to control personless mode, matching the pattern used
by the langchain and openai integrations.
* fix: restore changelog history and fix personless mode edge cases
Two fixes from bot review:
1. CHANGELOG.md was accidentally truncated to 38 lines during rebase
conflict resolution. Restored all 767 lines of history.
2. Personless mode now follows the same pattern as langchain/openai
integrations: _get_distinct_id returns None when no user-provided
ID is available, and callers set $process_person_profile=False
before falling back to trace_id. This covers the edge case where
a callable distinct_id returns None.
* fix: handle None token counts in generation span
Guard against input_tokens or output_tokens being None when computing
$ai_total_tokens to avoid TypeError.
* fix: check error_type_raw for all error categories
Check both error_type_raw and error_message for guardrail and
max_turns errors, consistent with how ModelBehaviorError and
UserError are already checked.
* fix: add type hints to instrument() function
* refactor: rename _safe_json to _ensure_serializable for clarity
The function validates JSON serializability and falls back to str(),
not serializes. Rename and update docstring to make the contract clear.
* refactor: emit $ai_trace at trace end instead of start
Move the $ai_trace event from on_trace_start to on_trace_end to
capture full metadata including latency, matching the LangChain
integration approach. on_trace_start now only stores metadata for
use by spans.
* style: fix ruff formatting
* fix: add TYPE_CHECKING imports for type hints in instrument()1 parent 661a0ec commit 1875b71
File tree
7 files changed
+1760
-1
lines changed- posthog
- ai/openai_agents
- test/ai/openai_agents
7 files changed
+1760
-1
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
1 | 7 | | |
2 | 8 | | |
3 | 9 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
0 commit comments