Skip to content

feat(ai): warn when AI wrapper is pointed at the PostHog AI Gateway#658

Open
richardsolomou wants to merge 2 commits into
mainfrom
posthog-code/warn-ai-gateway
Open

feat(ai): warn when AI wrapper is pointed at the PostHog AI Gateway#658
richardsolomou wants to merge 2 commits into
mainfrom
posthog-code/warn-ai-gateway

Conversation

@richardsolomou

@richardsolomou richardsolomou commented Jun 10, 2026

Copy link
Copy Markdown
Member

💡 Motivation and Context

A wrapper pointed at the PostHog AI Gateway makes both the wrapper and the gateway capture $ai_generation, so every call is double-counted and double-billed. Detection is host-based (the five deployed gateway hosts, scheme/port/path tolerant) and warns on every routed call across the wrapper funnels (OpenAI, Anthropic, LangChain) and the OTel span path. It only warns, never drops the event, since the wrapper event carries groups, custom properties, and trace hierarchy the gateway never sees. Ports posthog-js #3793.

💚 How did you test it?

Added posthog/test/ai/test_gateway.py: host detection, scheme/port/case variants, look-alike domains, warn-on-every-call, and the OTel server.address / url.full paths. ruff check and ruff format clean.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

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
@richardsolomou richardsolomou requested a review from a team as a code owner June 10, 2026 19:55
@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
posthog/test/ai/test_gateway.py:31-54
**Non-parameterized tests violate the "always prefer parameterised tests" rule**

The three `warn_if_posthog_ai_gateway` test functions each have a fixed set of inputs and an expected outcome, making them natural candidates for a single `@parameterized.expand` case. As written, adding a new gateway host to `POSTHOG_AI_GATEWAY_HOSTS` requires editing three functions instead of one table. Consider consolidating them into a parameterized test that covers the cases `(gateway_url, expect_warning)` alongside the silent cases.

### Issue 2 of 3
posthog/ai/gateway.py:9-11
**EU gateway host may be absent from the detection list**

`POSTHOG_AI_GATEWAY_HOSTS` is intentionally plural (and the comment says "Hosts that resolve to…"), yet only `gateway.us.posthog.com` is listed. If a `gateway.eu.posthog.com` (or equivalent) exists or is planned, users pointing their wrapper at it would receive no warning and could silently double-capture. Is this an intentional decision to address in a follow-up, or does the EU region not yet have a gateway host?

### Issue 3 of 3
posthog/ai/gateway.py:25-43
**URL parsed twice when the warning fires**

`is_posthog_ai_gateway_url` already extracts `hostname` from `urlparse`, but `warn_if_posthog_ai_gateway` calls `urlparse` a second time inside `log.warning` just to retrieve the same hostname. Extracting the host once keeps the logic OnceAndOnlyOnce and removes the redundant parse.

```suggestion
def warn_if_posthog_ai_gateway(base_url: Any) -> None:
    """
    Warn when an AI wrapper is pointed at the PostHog AI Gateway.

    The wrapper and the gateway each capture the LLM generation, which would
    double-count (and double-bill) the event. We only warn and never drop the
    event, because the wrapper event carries data the gateway never sees
    (groups, custom properties, trace hierarchy). We warn on every call rather
    than once, since a single startup line is easy to miss.
    """
    try:
        host = urlparse(str(base_url)).hostname if base_url else None
    except Exception:
        return
    if host in POSTHOG_AI_GATEWAY_HOSTS:
        log.warning(
            "Your PostHog AI wrapper is pointed at the PostHog AI Gateway (%s). "
            "This will capture and bill each LLM generation twice — once by this "
            "wrapper and once by the gateway. Point the wrapper at the model "
            "provider's API directly, or remove the wrapper and rely on the "
            "gateway. See https://posthog.com/docs/ai-observability",
            host,
        )
```

Reviews (1): Last reviewed commit: "feat(ai): warn when AI wrapper is pointe..." | Re-trigger Greptile

Comment thread posthog/test/ai/test_gateway.py Outdated
Comment thread posthog/ai/gateway.py
Comment thread posthog/ai/gateway.py Outdated
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

posthog-python Compliance Report

Date: 2026-06-10 20:14:14 UTC
Duration: 176127ms

✅ All Tests Passed!

45/45 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 519ms
Format Validation.Event Has Uuid 1507ms
Format Validation.Event Has Lib Properties 1508ms
Format Validation.Distinct Id Is String 1507ms
Format Validation.Token Is Present 1507ms
Format Validation.Custom Properties Preserved 1507ms
Format Validation.Event Has Timestamp 1507ms
Retry Behavior.Retries On 503 9519ms
Retry Behavior.Does Not Retry On 400 3506ms
Retry Behavior.Does Not Retry On 401 3508ms
Retry Behavior.Respects Retry After Header 9514ms
Retry Behavior.Implements Backoff 23529ms
Retry Behavior.Retries On 500 7502ms
Retry Behavior.Retries On 502 7515ms
Retry Behavior.Retries On 504 7512ms
Retry Behavior.Max Retries Respected 23517ms
Deduplication.Generates Unique Uuids 1508ms
Deduplication.Preserves Uuid On Retry 7515ms
Deduplication.Preserves Uuid And Timestamp On Retry 14521ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 7509ms
Deduplication.No Duplicate Events In Batch 1503ms
Deduplication.Different Events Have Different Uuids 1507ms
Compression.Sends Gzip When Enabled 1508ms
Batch Format.Uses Proper Batch Structure 1507ms
Batch Format.Flush With No Events Sends Nothing 1005ms
Batch Format.Multiple Events Batched Together 1505ms
Error Handling.Does Not Retry On 403 3509ms
Error Handling.Does Not Retry On 413 3508ms
Error Handling.Retries On 408 7510ms

Feature_Flags Tests

16/16 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 1008ms
Request Payload.Flags Request Uses V2 Query Param 1006ms
Request Payload.Flags Request Hits Flags Path Not Decide 1007ms
Request Payload.Flags Request Omits Authorization Header 1007ms
Request Payload.Token In Flags Body Matches Init 1007ms
Request Payload.Groups Round Trip 1007ms
Request Payload.Groups Default To Empty Object 1006ms
Request Payload.Person Properties Distinct Id Auto Populated When Caller Omits It 1008ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 1006ms
Request Payload.Disable Geoip Omitted Defaults To False 1007ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 1007ms
Request Lifecycle.No Flags Request On Init Alone 503ms
Request Lifecycle.No Flags Request On Normal Capture 1507ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 1011ms
Request Lifecycle.Mock Response Value Is Returned To Caller 1003ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 1509ms

Address review on #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
@richardsolomou richardsolomou requested a review from a team June 10, 2026 20:19
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