T#664
Conversation
…failures (PostHog#390) * Add $feature_flag_error property to track flag evaluation failures Track errors in feature flag evaluation by adding a `$feature_flag_error` property to the `$feature_flag_called` event. * Refactor requests exception imports through request.py Export RequestsTimeout and RequestsConnectionError from posthog/request.py to keep all requests library imports in one place and avoid mypy issues. * Address PR review feedback - Fix fallback logic to only trigger on actual exceptions, not when errors_while_computing or flag_missing is set from a successful API response - Change log.exception() to log.warning() for expected operational errors (quota limits, timeouts, connection errors, API errors) to reduce log noise - Keep log.exception() only for truly unexpected errors (unknown_error) - Extract stale cache fallback into _get_stale_flag_fallback() helper method * Add tests for stale cache fallback and error absence - Add TestFeatureFlagErrorWithStaleCacheFallback test class with 4 tests: - test_timeout_error_returns_stale_cached_value - test_connection_error_returns_stale_cached_value - test_api_error_returns_stale_cached_value - test_error_without_cache_returns_none - Add negative assertions to verify $feature_flag_error is absent on success: - test_get_feature_flag_result_boolean_local_evaluation - test_get_feature_flag_result_variant_local_evaluation - test_get_feature_flag_result_boolean_decide - test_get_feature_flag_result_variant_decide * Report combined errors when both errors_while_computing and flag_missing When the server returns errorsWhileComputingFlags=true AND the requested flag is not in the response, report both conditions as a comma-separated string: "errors_while_computing_flags,flag_missing" This provides better debugging context when both conditions occur. * Add FeatureFlagError constants class for error type values - Add FeatureFlagError class to types.py with constants: - ERRORS_WHILE_COMPUTING, FLAG_MISSING, QUOTA_LIMITED - TIMEOUT, CONNECTION_ERROR, UNKNOWN_ERROR - api_error(status) static method for dynamic error strings - Update client.py to use FeatureFlagError constants instead of magic strings - Update all tests to use constants for maintainability This improves maintainability by: - Single source of truth for error values - IDE autocomplete and typo detection - Documentation of analytics-stable values * Remove print statements from test failure handlers * Fix mypy type error in FeatureFlagError.api_error method Accept Union[int, str] to match APIError.status type.
* Add urllib3-based retry for feature flag requests Use urllib3's built-in Retry mechanism for feature flag POST requests instead of application-level retry logic. This is simpler and leverages well-tested library code. Key changes: - Add `RETRY_STATUS_FORCELIST` = [408, 500, 502, 503, 504] - Add `_build_flags_session()` with POST retries and `status_forcelist` - Update `flags()` to use dedicated flags session - Add tests for retry configuration and session usage The flags session retries on: - Network failures (connect/read errors) - Transient server errors (408, 500, 502, 503, 504) It does NOT retry on: - 429 (rate limit) - need to wait, not hammer - 402 (quota limit) - won't resolve with retries * Make examples run without requiring personal api key * Add integration tests for network retry behavior Add tests that verify actual retry behavior, not just configuration: - test_retries_on_503_then_succeeds: Spins up a local HTTP server that returns 503 twice then 200, verifying 3 requests are made - test_connection_errors_are_retried: Verifies connection errors trigger retries by measuring elapsed time with backoff Both tests use dynamically allocated ports for CI safety. * Bump version to 7.4.0
…_flag_events` in `get_feature_flag_payload` (PostHog#391) * fix: Respect `send_feature_flags` when local eval is enabled * fix: Deprecate `send_feature_flag_events` in `get_feature_flag_payload`
…tHog#395) * fix: extract model from response for OpenAI stored prompts When using OpenAI stored prompts, the model is defined in the OpenAI dashboard rather than passed in the API request. This change adds a fallback to extract the model from the response object when not provided in kwargs. Fixes PostHog/posthog#42861 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Apply suggestion from @greptile-apps[bot] Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Apply suggestion from @greptile-apps[bot] Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * test: add tests for model extraction fallback and bump to 7.4.1 - Add 8 tests covering model extraction from response for stored prompts - Fix utils.py to add 'unknown' fallback for consistency - Bump version to 7.4.1 - Update CHANGELOG.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: format utils.py with ruff 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: remove 'unknown' fallback from non-streaming to match original behavior Non-streaming originally returned None when model wasn't in kwargs. Streaming keeps "unknown" fallback as that was the original behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add test for None model fallback in non-streaming Verifies that non-streaming returns None (not "unknown") when model is not available in kwargs or response, matching original behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* add in_app configuration for python SDK * bump version * add in_app_modules to init script as well
The "Create GitHub release" step was broken in PR PostHog#386, which removed the GITHUB_TOKEN env var from the actions/create-release action. The action requires the token to be passed explicitly, so releases were being published to PyPI but GitHub tags/releases were not created. This replaces the archived actions/create-release@v1 with the gh CLI, which is already used elsewhere in this workflow. The gh CLI properly uses GH_TOKEN for authentication.
* feat: llma / error tracking integration * capture all metadata in llm event * instrument with contexts * bump version * indentation * linting * tests * raise * test: add exception capture integration tests for langchain Add 6 tests covering the new LLMA + error tracking integration: - capture_exception called on span/generation errors - $exception_event_id added to AI events - No capture when autocapture disabled - AI properties passed to exception event - Handles None return from capture_exception 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: pass context tags to capture() for test compatibility - Export get_tags() from posthog module - Explicitly pass context tags to capture() in AI utils - Fix $ai_model fallback to extract from response.model - Fix ruff formatting in langchain test_callbacks.py 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: disable auto-capture exceptions in LLM context The new_context() defaults to capture_exceptions=True which would auto-capture any exception regardless of enable_exception_autocapture setting. This was inconsistent with LangChain callbacks which explicitly check the setting. Pass capture_exceptions=False to let exception handling be controlled explicitly by the enable_exception_autocapture setting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: isolate LLM context with fresh=True to avoid tag inheritance Use fresh=True to start with a clean context for each LLM call. This avoids inheriting $ai_* tags from parent contexts which could cause mismatched AI metadata due to the tag merge order bug in contexts.py (parent tags incorrectly override child tags). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: correct tag merge order so child tags take precedence The collect_tags() method had a bug where parent tags would overwrite child tags, despite the comment saying the opposite. This fix ensures child context tags properly override parent tags. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: remove fresh=True now that tag merge order is fixed With the collect_tags() bug fixed, child tags properly override parent tags. LLM events can now inherit useful parent context tags (request_id, user info, etc.) while still having their $ai_* tags take precedence. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add test for child tags overriding parent tags Verifies that in non-fresh contexts, child tags properly override parent tags with the same key while still inheriting other parent tags. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: add TODO for OpenAI/Anthropic/Gemini exception capture Document that exception capture needs to be added for the direct SDK wrappers, similar to how it's implemented in LangChain callbacks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: David Newell <david@Mac.communityfibre.co.uk> Co-authored-by: Andrew Maguire <andrewm4894@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…ostHog#403) * chore: add a test to describe upload behaviour when there are errors * refactor the test file * add typehints to the test file
* docs: add Python version support table to README Add a table documenting which SDK versions introduced or dropped support for different Python versions, based on CHANGELOG.md entries. * fix: correct Python 3.14 support version to 7.4.3 * Apply suggestions from code review Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This is required to be able to run CodeQL against external contributors' PRs.
…PostHog#361) * fix: Avoid return from finally: block This fixes a SyntaxWarning on Python 3.14. ``` ❯ uvx --no-cache --python 3.14.0 --with posthog==6.7.11 python -c "import posthog" Installed 11 packages in 5ms .../lib/python3.14/site-packages/posthog/consumer.py:92: SyntaxWarning: 'return' in a 'finally' block return success ```` * add versioning info --------- Co-authored-by: Paul D'Ambra <paul.dambra@gmail.com>
Updated link formatting for clarity in changelog.
* feat: add device_id to flags request payload Add device_id parameter to all feature flag methods, similar to how distinct_id is handled. The device_id is included in the flags request payload sent to the server. - Add device_id parameter to Client methods and module-level functions - Add context support via set_context_device_id() for automatic fallback - Add tests for explicit device_id and context-based device_id - Bump version to 7.6.0
* 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()
…tions (PostHog#411) * feat: pass raw provider usage metadata for backend cost calculations Add raw_usage field to TokenUsage type to capture raw provider usage metadata (OpenAI, Anthropic, Gemini). This enables the backend to extract modality-specific token counts (text vs image vs audio) for accurate cost calculations. - Add raw_usage field to TokenUsage TypedDict - Update all provider converters to capture raw usage: - OpenAI: capture response.usage and chunk usage - Anthropic: capture usage from message_start and message_delta events - Gemini: capture usage_metadata from responses and chunks - Pass raw usage as $ai_usage property in PostHog events - Update merge_usage_stats to handle raw_usage in both modes - Add tests verifying $ai_usage is captured for all providers Backend will extract provider-specific details and delete $ai_usage after processing to avoid bloating properties. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: add serialize_raw_usage helper to ensure JSON serializability Address PR review feedback from @andrewm4894: 1. **Serialization**: Add serialize_raw_usage() helper with fallback chain: - .model_dump() for Pydantic models (OpenAI/Anthropic) - .to_dict() for protobuf-like objects - vars() for simple objects - str() as last resort This ensures we never pass unserializable objects to PostHog client. 2. **Data loss prevention**: Change from replacing to merging raw_usage in incremental mode. For Anthropic streaming, message_start has input token details and message_delta has output token details - merging preserves both instead of losing input data. 3. **Test coverage**: Enhanced tests to verify: - JSON serializability with json.dumps() - Expected structure of raw_usage dicts - Coverage for both non-streaming and streaming modes - Fixed Gemini test mocks to return proper dicts from model_dump() Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: move raw_usage serialization from utils to converters Address PR feedback from @andrewm4894 - serialize in converters, not utils. **Problem:** Utils was receiving raw Pydantic/protobuf objects and serializing them, which meant provider-specific knowledge leaked into generic code. **Solution:** Move serialization into converters where provider context exists: Converters (NEW): - OpenAI: serialize_raw_usage(response.usage) → dict - Anthropic: serialize_raw_usage(event.usage) → dict - Gemini: serialize_raw_usage(metadata) → dict Utils (SIMPLIFIED): - Just passes dicts through, no serialization needed - Merge operations work with dicts only **Benefits:** 1. Type correctness: raw_usage is always Dict[str, Any] 2. Separation of concerns: converters handle provider formats 3. Fail fast: serialization errors in converters with context 4. Cleaner abstraction: utils doesn't know about Pydantic/protobuf **Flow:** Provider object → Converter serializes → dict → Utils → PostHog Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: add type annotation for current_raw to satisfy mypy Fix mypy error: "Need type annotation for 'current_raw'" Extract value first, then apply explicit type annotation with ternary conditional to satisfy mypy's type checker. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat(llma): add prompt management * chore(llma): bump version * fix(llma): use SDK session with retry logic for prompt fetching Use _get_session() from posthog/request.py instead of raw requests.get() to benefit from the SDK's existing retry configuration on transient network failures.
* feat: SDK Compliance
* fix(llma): small fixes for prompt management * fix(llma): tests * fix(llma): tests
* Fix feature flag 401 errors causing HTTP request storm Set feature_flags = [] on 401 error to prevent repeated requests. * Clear flag_cache, group_type_mapping, cohorts on 401
Co-authored-by: Aleksander Błaszkiewicz <kqmdjc8@gmail.com>
* fix: Retry on 408 and respect Retry-After header 408 (Request Timeout) was incorrectly treated as a non-retryable client error. Retry-After response headers were ignored during backoff. Replace backoff library usage with a manual retry loop that honours Retry-After when present and falls back to exponential backoff otherwise. * fix: Parse HTTP-date Retry-After values Retry-After can be seconds or an HTTP-date per RFC 7231. Fall back to email.utils.parsedate_to_datetime when the numeric parse fails. * fix: Don't retry on unclassifiable APIError status When APIError.status is "N/A" (no HTTP status), treat it as non-retryable to avoid unexpected retry loops on errors the SDK cannot classify. * test: Add retry delay tests for Retry-After and exponential backoff Verify time.sleep is called with the Retry-After value when present, uses exponential backoff (2^attempt) when absent, and that 408 is retried.
* feat: initial * fix: ruff
* feat: limit max number of items in collection to scan * feat: changelog * fix: format * feat: test * feat: replace entire collection instead of truncating
…ostHog#424) * feat: Support device_id as bucketing identifier for local evaluation Add support for `bucketing_identifier` field on feature flags to allow using `device_id` instead of `distinct_id` for hashing/bucketing in local evaluation.
chore: update sentry license attribution
…ai-pydantic-ai (PostHog#649) chore(deps): bump starlette in /examples/example-ai-pydantic-ai Bumps [starlette](https://github.com/Kludex/starlette) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](Kludex/starlette@1.0.0...1.0.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ai-openai-agents (PostHog#650) chore(deps): bump starlette in /examples/example-ai-openai-agents Bumps [starlette](https://github.com/Kludex/starlette) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](Kludex/starlette@1.0.0...1.0.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ai-semantic-kernel (PostHog#651) chore(deps): bump starlette in /examples/example-ai-semantic-kernel Bumps [starlette](https://github.com/Kludex/starlette) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](Kludex/starlette@1.0.0...1.0.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…dates (PostHog#642) Bumps the ai-providers group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [openai](https://github.com/openai/openai-python) | `2.29.0` | `2.38.0` | | [anthropic](https://github.com/anthropics/anthropic-sdk-python) | `0.102.0` | `0.104.1` | | [langgraph](https://github.com/langchain-ai/langgraph) | `1.2.0` | `1.2.1` | | [langchain-community](https://github.com/langchain-ai/langchain-community) | `0.4.1` | `0.4.2` | | [langchain-openai](https://github.com/langchain-ai/langchain) | `1.1.12` | `1.2.2` | | [google-genai](https://github.com/googleapis/python-genai) | `1.24.0` | `2.6.0` | Updates `openai` from 2.29.0 to 2.38.0 - [Release notes](https://github.com/openai/openai-python/releases) - [Changelog](https://github.com/openai/openai-python/blob/main/CHANGELOG.md) - [Commits](openai/openai-python@v2.29.0...v2.38.0) Updates `anthropic` from 0.102.0 to 0.104.1 - [Release notes](https://github.com/anthropics/anthropic-sdk-python/releases) - [Changelog](https://github.com/anthropics/anthropic-sdk-python/blob/main/CHANGELOG.md) - [Commits](anthropics/anthropic-sdk-python@v0.102.0...v0.104.1) Updates `langgraph` from 1.2.0 to 1.2.1 - [Release notes](https://github.com/langchain-ai/langgraph/releases) - [Commits](langchain-ai/langgraph@1.2.0...1.2.1) Updates `langchain-community` from 0.4.1 to 0.4.2 - [Release notes](https://github.com/langchain-ai/langchain-community/releases) - [Commits](langchain-ai/langchain-community@libs/community/v0.4.1...libs/community/v0.4.2) Updates `langchain-openai` from 1.1.12 to 1.2.2 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](langchain-ai/langchain@langchain-openai==1.1.12...langchain-openai==1.2.2) Updates `google-genai` from 1.24.0 to 2.6.0 - [Release notes](https://github.com/googleapis/python-genai/releases) - [Changelog](https://github.com/googleapis/python-genai/blob/main/CHANGELOG.md) - [Commits](googleapis/python-genai@v1.24.0...v2.6.0) Updates `langchain-classic` from 1.0.3 to 1.0.7 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](langchain-ai/langchain@langchain-classic==1.0.3...langchain-classic==1.0.7) Updates `langchain-text-splitters` from 1.1.1 to 1.1.2 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](langchain-ai/langchain@langchain-text-splitters==1.1.1...langchain-text-splitters==1.1.2) --- updated-dependencies: - dependency-name: openai dependency-version: 2.38.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ai-providers - dependency-name: anthropic dependency-version: 0.104.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ai-providers - dependency-name: langgraph dependency-version: 1.2.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ai-providers - dependency-name: langchain-community dependency-version: 0.4.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ai-providers - dependency-name: langchain-openai dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ai-providers - dependency-name: google-genai dependency-version: 2.6.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: ai-providers - dependency-name: langchain-classic dependency-version: 1.0.7 dependency-type: indirect update-type: version-update:semver-patch dependency-group: ai-providers - dependency-name: langchain-text-splitters dependency-version: 1.1.2 dependency-type: indirect update-type: version-update:semver-patch dependency-group: ai-providers ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com>
* ci: skip flags project board workflow for Dependabot PRs The reusable PostHog/.github flags-project-board workflow generates a GitHub App token as its first step. Dependabot-triggered runs execute in a restricted secret context that has no access to the App credentials, so that step hard-fails with "The 'client-id' (or deprecated 'app-id') input must be set to a non-empty string" on every dependency-bump PR (failing since the 2025-09-09 PAT -> GitHub App migration upstream). Guard the job with github.actor != 'dependabot[bot]'. A job-level if reports as skipped, which branch protection treats as non-blocking, so it won't leave a "waiting for status" check. Adding the App secret to the Dependabot context would also work but would hand an org-write key to the untrusted Dependabot context, so the skip is the safer fix.
…ostHog#653) The Dependabot skip for the feature flags project board is now handled centrally in the reusable workflow (PostHog/.github#52). Bump the pin to that merged SHA and drop the redundant inline `github.actor != 'dependabot[bot]'` guard so the conditional lives in one place. No behavior change: Dependabot PRs are still skipped (now upstream), and the pin bump pulls in only that single workflow change. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) * feat(feature-flags): support early_exit in local evaluation Port PostHog/posthog-js#3705 to posthog-python. When a flag enables `filters.early_exit`, condition evaluation now stops and returns `False` as soon as a condition group's property filters match but the rollout percentage excludes the user, instead of falling through to later groups — matching the server-side (Rust) engine's `OutOfRolloutBound` short-circuit. - Introduce a `ConditionMatch` tri-state (MATCH / NO_MATCH / OUT_OF_ROLLOUT_BOUND) returned by `is_condition_match`, so the loop can distinguish a rollout exclusion from a property mismatch. Property mismatches still fall through, mirroring the Rust semantics exactly. - Read `filters.early_exit` in `match_feature_flag_properties` and short-circuit to `False` on OUT_OF_ROLLOUT_BOUND when enabled. - Tests for early-exit on, default off (regression), explicit off, rollout-only groups, and the property-mismatch case. Generated-By: PostHog Code Task-Id: 707b13a5-0e5d-4764-915a-21e1f2a80c63 * fix(feature-flags): remove redundant or False and add multivariate early_exit test
Bumps the ai-providers group with 5 updates: | Package | From | To | | --- | --- | --- | | [langchain](https://github.com/langchain-ai/langchain) | `1.3.1` | `1.3.2` | | [anthropic](https://github.com/anthropics/anthropic-sdk-python) | `0.104.1` | `0.105.2` | | [langgraph](https://github.com/langchain-ai/langgraph) | `1.2.1` | `1.2.2` | | [langchain-anthropic](https://github.com/langchain-ai/langchain) | `1.4.3` | `1.4.4` | | [google-genai](https://github.com/googleapis/python-genai) | `2.6.0` | `2.7.0` | Updates `langchain` from 1.3.1 to 1.3.2 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](langchain-ai/langchain@langchain==1.3.1...langchain==1.3.2) Updates `anthropic` from 0.104.1 to 0.105.2 - [Release notes](https://github.com/anthropics/anthropic-sdk-python/releases) - [Changelog](https://github.com/anthropics/anthropic-sdk-python/blob/main/CHANGELOG.md) - [Commits](anthropics/anthropic-sdk-python@v0.104.1...v0.105.2) Updates `langgraph` from 1.2.1 to 1.2.2 - [Release notes](https://github.com/langchain-ai/langgraph/releases) - [Commits](langchain-ai/langgraph@1.2.1...1.2.2) Updates `langchain-anthropic` from 1.4.3 to 1.4.4 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](langchain-ai/langchain@langchain-anthropic==1.4.3...langchain-anthropic==1.4.4) Updates `google-genai` from 2.6.0 to 2.7.0 - [Release notes](https://github.com/googleapis/python-genai/releases) - [Changelog](https://github.com/googleapis/python-genai/blob/main/CHANGELOG.md) - [Commits](googleapis/python-genai@v2.6.0...v2.7.0) --- updated-dependencies: - dependency-name: langchain dependency-version: 1.3.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ai-providers - dependency-name: anthropic dependency-version: 0.105.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ai-providers - dependency-name: langgraph dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ai-providers - dependency-name: langchain-anthropic dependency-version: 1.4.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ai-providers - dependency-name: google-genai dependency-version: 2.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ai-providers ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: dedicated ai endpoint routing * fix: attempt AI and analytics sends independently, rename flag to _dedicated_ai_endpoint Generated-By: PostHog Code Task-Id: 142d3917-41e8-4bb5-ad6c-5778e13041c2 * fix: increase size limit for AI events
* chore: split CI checks into separate jobs * address pr review feedback
…es (PostHog#644) * fix(consumer): remove legacy Python 2 imports and type-guard retry status math * fix(request): resolve type-shifting assignment, secure path concatenation, and strip dead ignore * fix(client, flags): handle optional before_send type variance and drop dead code signatures * fix: type external imports for mypy * fix: keep request payload behavior unchanged * test: preserve request payload behavior --------- Co-authored-by: Manoel Aranda Neto <marandaneto@gmail.com>
…ostHog#658) * feat(ai): warn when AI wrapper is pointed at the PostHog AI Gateway An AI wrapper pointed at gateway.us.posthog.com captures each generation twice (once by the wrapper, once by the gateway), doubling events and cost. Warn on every routed call without dropping the event, since the wrapper event carries groups, custom properties, and trace hierarchy the gateway never sees. Ports posthog-js #3793. Generated-By: PostHog Code Task-Id: d4ca2f3d-2579-4155-b6d4-1f072c7e9ace * fix(ai): port gateway warning faithfully from posthog-js#3793 Address review on PostHog#658 by matching gatewayWarning.ts as written: - List all five deployed gateway hosts, not just gateway.us.posthog.com, so EU and ai-gateway hosts are detected too. - Tolerate base URLs without a scheme (e.g. gateway.us.posthog.com/v1), which urlparse otherwise reads as a path with no hostname. - Use a static warning message, dropping the redundant second urlparse. - Detect the gateway on the OTel span path (server.address / url.full) in the processor and exporter, since those spans bypass the funnels. - Consolidate tests with pytest.mark.parametrize. Generated-By: PostHog Code Task-Id: d4ca2f3d-2579-4155-b6d4-1f072c7e9ace * chore(ai): mark gateway warning changeset as patch Generated-By: PostHog Code Task-Id: d4ca2f3d-2579-4155-b6d4-1f072c7e9ace
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
os:1-40
**File conflicts with Python standard library module**
A file named `os` at the repository root is dangerous: Python's import system can resolve `import os` to this file instead of the standard library module, breaking any code in the repo that depends on `os`. Beyond the naming conflict, the content is not valid Python — line 17 contains a bare `,` on its own, and the fragment has no function or class context, so it would raise a `SyntaxError` immediately on import.
### Issue 2 of 2
Semantic/Kernel:1
**Shell command committed as a source file**
This file contains a bare `pip install` shell command. It is not valid Python, not a shell script, not a `requirements.txt`, and not a `pyproject.toml` entry. Committing an ad-hoc install command as a file named `Semantic/Kernel` serves no reproducible purpose and would confuse anyone reading the repository.
Reviews (1): Last reviewed commit: "Refactor os configuration parameters" | Re-trigger Greptile |
| debug=debug, | ||
|
|
||
|
|
||
|
|
||
| feature_flags_request_timeout_seconds=feature_flags_request_timeout_seconds, | ||
|
|
||
| super_properties=super_properties | ||
|
|
||
|
|
||
| on_error=on_error, | ||
| send=send, | ||
| sync_mode=sync_mode, | ||
| personal_api_key=personal_api_key, | ||
| poll_interval=poll_interval, | ||
| disabled=disabled, | ||
| disable_geoip=disable_geoip, | ||
| is_server=is_server, | ||
| , | ||
| # TODO: Currently this monitoring begins only when the Client is initialised (which happens when you do something with the SDK) | ||
| # This kind of initialisation is very annoying for exception capture. We need to figure out a way around this, | ||
| # or deprecate this proxy option fully (it's already in the process of deprecation, no new clients should be using this method since like 5-6 months) | ||
| enable_exception_autocapture=enable_exception_autocapture, | ||
| log_captured_exceptions=log_captured_exceptions, | ||
| before_send=before_send, | ||
| enable_local_evaluation=enable_local_evaluation, | ||
| flag_definition_cache_provider=flag_definition_cache_provider, | ||
| capture_exception_code_variables=capture_exception_code_variables, | ||
| code_variables_mask_patterns=code_variables_mask_patterns, | ||
| code_variables_ignore_patterns=code_variables_ignore_patterns, | ||
| in_app_modules=in_app_modules, | ||
| ) | ||
|
|
||
| # Always set in case user changes it. Preserve Client's auto-disabled state | ||
| # for API keys that become empty after trimming. | ||
| default_client.disabled = disabled or not default_client.api_key | ||
| default_client.debug = debug | ||
| default_client._set_before_send(before_send) | ||
|
|
||
| return default_client | ||
|
|
There was a problem hiding this comment.
File conflicts with Python standard library module
A file named os at the repository root is dangerous: Python's import system can resolve import os to this file instead of the standard library module, breaking any code in the repo that depends on os. Beyond the naming conflict, the content is not valid Python — line 17 contains a bare , on its own, and the fragment has no function or class context, so it would raise a SyntaxError immediately on import.
Prompt To Fix With AI
This is a comment left during a code review.
Path: os
Line: 1-40
Comment:
**File conflicts with Python standard library module**
A file named `os` at the repository root is dangerous: Python's import system can resolve `import os` to this file instead of the standard library module, breaking any code in the repo that depends on `os`. Beyond the naming conflict, the content is not valid Python — line 17 contains a bare `,` on its own, and the fragment has no function or class context, so it would raise a `SyntaxError` immediately on import.
How can I resolve this? If you propose a fix, please make it concise.| @@ -0,0 +1 @@ | |||
| pip install semantic-kernel openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2 | |||
There was a problem hiding this comment.
Shell command committed as a source file
This file contains a bare pip install shell command. It is not valid Python, not a shell script, not a requirements.txt, and not a pyproject.toml entry. Committing an ad-hoc install command as a file named Semantic/Kernel serves no reproducible purpose and would confuse anyone reading the repository.
Prompt To Fix With AI
This is a comment left during a code review.
Path: Semantic/Kernel
Line: 1
Comment:
**Shell command committed as a source file**
This file contains a bare `pip install` shell command. It is not valid Python, not a shell script, not a `requirements.txt`, and not a `pyproject.toml` entry. Committing an ad-hoc install command as a file named `Semantic/Kernel` serves no reproducible purpose and would confuse anyone reading the repository.
How can I resolve this? If you propose a fix, please make it concise.
💡 Motivation and Context
💚 How did you test it?
📝 Checklist
If releasing new changes
sampo addto generate a changeset file