* 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()
PostHog Python
Please see the Python integration docs for details.
Python Version Support
| SDK Version | Python Versions Supported | Notes |
|---|---|---|
| 7.3.1+ | 3.10, 3.11, 3.12, 3.13, 3.14 | Added Python 3.14 support |
| 7.0.0 - 7.0.1 | 3.10, 3.11, 3.12, 3.13 | Dropped Python 3.9 support |
| 4.0.1 - 6.x | 3.9, 3.10, 3.11, 3.12, 3.13 | Python 3.9+ required |
Development
Testing Locally
We recommend using uv. It's super fast.
- Run
uv venv env(creates virtual environment called "env")- or
python3 -m venv env
- or
- Run
source env/bin/activate(activates the virtual environment) - Run
uv sync --extra dev --extra test(installs the package in develop mode, along with test dependencies)- or
pip install -e ".[dev,test]"
- or
- you have to run
pre-commit installto have auto linting pre commit - Run
make test - To run a specific test do
pytest -k test_no_api_key
PostHog recommends uv so...
uv python install 3.12
uv python pin 3.12
uv venv
source env/bin/activate
uv sync --extra dev --extra test
pre-commit install
make test
Running Locally
Assuming you have a local version of PostHog running, you can run python3 example.py to see the library in action.
Releasing Versions
Updates are released automatically using GitHub Actions when version.py is updated on master. After bumping version.py in master and adding to CHANGELOG.md, the release workflow will automatically trigger and deploy the new version.
If you need to check the latest runs or manually trigger a release, you can go to our release workflow's page and dispatch it manually, using workflow from master.
Testing changes locally with the PostHog app
You can run make prep_local, and it'll create a new folder alongside the SDK repo one called posthog-python-local, which you can then import into the posthog project by changing pyproject.toml to look like this:
dependencies = [
...
"posthoganalytics" #NOTE: no version number
...
]
...
[tools.uv.sources]
posthoganalytics = { path = "../posthog-python-local" }
This'll let you build and test SDK changes fully locally, incorporating them into your local posthog app stack. It mainly takes care of the posthog -> posthoganalytics module renaming. You'll need to re-run make prep_local each time you make a change, and re-run uv sync --active in the posthog app project.
