Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .sampo/changesets/openai-provider-override.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pypi/posthog: minor
---

`posthog.ai.openai.OpenAI` / `AsyncOpenAI` now accept a per-call `posthog_provider_override` argument. The wrapper is commonly pointed at OpenAI-compatible endpoints (DeepSeek, Groq, Mistral, Together, Fireworks, xAI, Perplexity, Ollama, Cerebras, and various gateways) via a custom `base_url`, but always reported `$ai_provider: "openai"`, which breaks PostHog's cost attribution for those calls. Passing `posthog_provider_override="deepseek"` (for example) sets `$ai_provider` on the emitted event without changing how the OpenAI-shaped response is parsed. Omitting it leaves `$ai_provider` as `"openai"`, exactly as before. Covers chat completions, the Responses API, `.parse()`, and embeddings, across sync, async, and streaming calls.
59 changes: 57 additions & 2 deletions posthog/ai/openai/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
)
from posthog.client import Client as PostHogClient
from posthog import setup
from posthog.ai.openai.wrapper_utils import _OpenAIWrapperResource
from posthog.ai.openai.wrapper_utils import (
_OpenAIWrapperResource,
merge_provider_override,
)


class OpenAI(openai.OpenAI):
Expand Down Expand Up @@ -76,14 +79,15 @@ def _parse_and_track(
posthog_properties: Optional[Dict[str, Any]],
posthog_privacy_mode: bool,
posthog_groups: Optional[Dict[str, Any]],
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
return call_llm_and_track_usage(
posthog_distinct_id,
wrapper._client._ph_client,
"openai",
posthog_trace_id,
posthog_properties,
merge_provider_override(posthog_properties, posthog_provider_override),
posthog_privacy_mode,
posthog_groups,
wrapper._client.base_url,
Expand All @@ -102,6 +106,7 @@ def create(
posthog_properties: Optional[Dict[str, Any]] = None,
posthog_privacy_mode: bool = False,
posthog_groups: Optional[Dict[str, Any]] = None,
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
"""
Expand All @@ -113,6 +118,11 @@ def create(
posthog_properties: Additional properties to include with the usage event.
posthog_privacy_mode: Whether to redact captured input and output.
posthog_groups: Optional PostHog groups to associate with the event.
posthog_provider_override: Optional override for the ``$ai_provider``
reported on the usage event. Useful when this client is pointed at
an OpenAI-compatible endpoint (e.g. DeepSeek, Groq) via a custom
``base_url``, so cost attribution matches the real provider.
Defaults to ``"openai"`` when omitted.
**kwargs: Arguments passed to OpenAI's ``responses.create`` API.

Returns:
Expand All @@ -121,6 +131,10 @@ def create(
if posthog_trace_id is None:
posthog_trace_id = str(uuid.uuid4())

posthog_properties = merge_provider_override(
posthog_properties, posthog_provider_override
)

if kwargs.get("stream", False):
return self._create_streaming(
posthog_distinct_id,
Expand Down Expand Up @@ -274,6 +288,7 @@ def parse(
posthog_properties: Optional[Dict[str, Any]] = None,
posthog_privacy_mode: bool = False,
posthog_groups: Optional[Dict[str, Any]] = None,
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
"""
Expand All @@ -285,6 +300,11 @@ def parse(
posthog_properties: Optional dictionary of extra properties to include in the event.
posthog_privacy_mode: Whether to anonymize the input and output.
posthog_groups: Optional dictionary of groups to associate with the event.
posthog_provider_override: Optional override for the ``$ai_provider``
reported on the usage event. Useful when this client is pointed at
an OpenAI-compatible endpoint (e.g. DeepSeek, Groq) via a custom
``base_url``, so cost attribution matches the real provider.
Defaults to ``"openai"`` when omitted.
**kwargs: Any additional parameters for the OpenAI Responses Parse API.

Returns:
Expand All @@ -297,6 +317,7 @@ def parse(
posthog_properties,
posthog_privacy_mode,
posthog_groups,
posthog_provider_override,
**kwargs,
)

Expand All @@ -320,6 +341,7 @@ def parse(
posthog_properties: Optional[Dict[str, Any]] = None,
posthog_privacy_mode: bool = False,
posthog_groups: Optional[Dict[str, Any]] = None,
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
"""
Expand All @@ -331,6 +353,11 @@ def parse(
posthog_properties: Additional properties to include with the usage event.
posthog_privacy_mode: Whether to redact captured input and output.
posthog_groups: Optional PostHog groups to associate with the event.
posthog_provider_override: Optional override for the ``$ai_provider``
reported on the usage event. Useful when this client is pointed at
an OpenAI-compatible endpoint (e.g. DeepSeek, Groq) via a custom
``base_url``, so cost attribution matches the real provider.
Defaults to ``"openai"`` when omitted.
**kwargs: Arguments passed to OpenAI's ``chat.completions.parse`` API.

Returns:
Expand All @@ -343,6 +370,7 @@ def parse(
posthog_properties,
posthog_privacy_mode,
posthog_groups,
posthog_provider_override,
**kwargs,
)

Expand All @@ -353,6 +381,7 @@ def create(
posthog_properties: Optional[Dict[str, Any]] = None,
posthog_privacy_mode: bool = False,
posthog_groups: Optional[Dict[str, Any]] = None,
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
"""
Expand All @@ -364,6 +393,11 @@ def create(
posthog_properties: Additional properties to include with the usage event.
posthog_privacy_mode: Whether to redact captured input and output.
posthog_groups: Optional PostHog groups to associate with the event.
posthog_provider_override: Optional override for the ``$ai_provider``
reported on the usage event. Useful when this client is pointed at
an OpenAI-compatible endpoint (e.g. DeepSeek, Groq) via a custom
``base_url``, so cost attribution matches the real provider.
Defaults to ``"openai"`` when omitted.
**kwargs: Arguments passed to OpenAI's ``chat.completions.create`` API.

Returns:
Expand All @@ -372,6 +406,10 @@ def create(
if posthog_trace_id is None:
posthog_trace_id = str(uuid.uuid4())

posthog_properties = merge_provider_override(
posthog_properties, posthog_provider_override
)

if kwargs.get("stream", False):
return self._create_streaming(
posthog_distinct_id,
Expand Down Expand Up @@ -545,6 +583,7 @@ def create(
posthog_properties: Optional[Dict[str, Any]] = None,
posthog_privacy_mode: bool = False,
posthog_groups: Optional[Dict[str, Any]] = None,
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
"""
Expand All @@ -556,6 +595,11 @@ def create(
posthog_properties: Optional dictionary of extra properties to include in the event.
posthog_privacy_mode: Whether to anonymize the input and output.
posthog_groups: Optional dictionary of groups to associate with the event.
posthog_provider_override: Optional override for the ``$ai_provider``
reported on the usage event. Useful when this client is pointed at
an OpenAI-compatible endpoint (e.g. DeepSeek, Groq) via a custom
``base_url``, so cost attribution matches the real provider.
Defaults to ``"openai"`` when omitted.
**kwargs: Any additional parameters for the OpenAI Embeddings API.

Returns:
Expand All @@ -565,6 +609,10 @@ def create(
if posthog_trace_id is None:
posthog_trace_id = str(uuid.uuid4())

posthog_properties = merge_provider_override(
posthog_properties, posthog_provider_override
)

start_time = time.time()
response = self._original.create(**kwargs)
end_time = time.time()
Expand Down Expand Up @@ -640,6 +688,7 @@ def parse(
posthog_properties: Optional[Dict[str, Any]] = None,
posthog_privacy_mode: bool = False,
posthog_groups: Optional[Dict[str, Any]] = None,
posthog_provider_override: Optional[str] = None,
**kwargs: Any,
):
"""
Expand All @@ -651,6 +700,11 @@ def parse(
posthog_properties: Additional properties to include with the usage event.
posthog_privacy_mode: Whether to redact captured input and output.
posthog_groups: Optional PostHog groups to associate with the event.
posthog_provider_override: Optional override for the ``$ai_provider``
reported on the usage event. Useful when this client is pointed at
an OpenAI-compatible endpoint (e.g. DeepSeek, Groq) via a custom
``base_url``, so cost attribution matches the real provider.
Defaults to ``"openai"`` when omitted.
**kwargs: Arguments passed to OpenAI's beta ``chat.completions.parse`` API.

Returns:
Expand All @@ -663,5 +717,6 @@ def parse(
posthog_properties,
posthog_privacy_mode,
posthog_groups,
posthog_provider_override,
**kwargs,
)
Loading