Repository navigation
feat: add captureAi on the capture V1 AI endpoint - #939
turnipdabeets wants to merge 9 commits into
Conversation
posthog-ios Compliance ReportDate: 2026-10-23T22:51:30.378361+00:00
|
| Test | Status | Duration |
|---|---|---|
| Endpoint And Method.Targets V1 Endpoint | ✅ | 3118ms |
| Endpoint And Method.Does Not Use Legacy Endpoints | ✅ | 2873ms |
| Required Headers.Has Authorization Bearer Header | ✅ | 2796ms |
| Required Headers.Has Content Type Json | ✅ | 2924ms |
| Required Headers.Has Posthog Sdk Info Format | ✅ | 2754ms |
| Required Headers.Has Posthog Attempt Header | ✅ | 2900ms |
| Required Headers.Has Posthog Request Id | ✅ | 404ms |
| Required Headers.Has Posthog Request Timestamp | ✅ | 435ms |
| Required Headers.Has User Agent | ✅ | 417ms |
| Body Format.Body Has Created At And Batch | ✅ | 2875ms |
| Body Format.No Api Key In Body | ✅ | 2879ms |
| Body Format.No Sent At In Body | ✅ | 2895ms |
| Event Format.Event Has Required Root Fields | ✅ | 2846ms |
| Event Format.Event Uuid Is Valid | ✅ | 2752ms |
| Event Format.Event Timestamp Is Rfc3339 | ✅ | 2725ms |
| Event Format.Non Utc Event Timestamp Is Converted To Utc | ✅ | 2791ms |
| Event Format.Distinct Id Is String | ✅ | 2797ms |
| Event Format.Distinct Id At Root Not Properties | ✅ | 342ms |
| Event Format.Custom Properties Preserved | ✅ | 3050ms |
| Event Format.Set Properties Preserved | ✅ | 2820ms |
| Event Format.Set Once Properties Preserved | ✅ | 2776ms |
| Event Format.Groups Properties Preserved | ✅ | 2855ms |
| Event Format.Sdk Generates Uuid If Not Provided | ✅ | 474ms |
| Event Format.Event Has Required Root Fields Batch | ✅ | 2906ms |
| Event Format.Event Uuid Is Valid Batch | ✅ | 3037ms |
| Event Format.Event Timestamp Is Rfc3339 Batch | ✅ | 2964ms |
| Event Format.Distinct Id Is String Batch | ✅ | 2834ms |
| Event Format.Distinct Id At Root Not Properties Batch | ✅ | 2796ms |
| Event Format.Custom Properties Preserved Batch | ✅ | 3023ms |
| Event Format.Set Properties Preserved Batch | ✅ | 2935ms |
| Event Format.Set Once Properties Preserved Batch | ✅ | 2990ms |
| Event Format.Groups Properties Preserved Batch | ✅ | 3220ms |
| Event Format.Sdk Generates Uuid If Not Provided Batch | ✅ | 2908ms |
| Batch Behavior.Multiple Events In Single Batch | ✅ | 3050ms |
| Batch Behavior.Batch Envelope Smoke | ✅ | 3017ms |
| Batch Behavior.Flush With No Events Sends Nothing | ✅ | 313ms |
| Batch Behavior.Flush At Triggers Batch | ✅ | 1244ms |
| Batch Behavior.Created At Reflects Batch Creation Time | ✅ | 2805ms |
| Deduplication.Generates Unique Uuids | ✅ | 3229ms |
| Deduplication.Different Events Same Content Different Uuids | ✅ | 2872ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 9307ms |
| Deduplication.Preserves Timestamp On Retry | ✅ | 9260ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 7915ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 2989ms |
| Header Behavior On Retry.Attempt Header Starts At One | ✅ | 2763ms |
| Header Behavior On Retry.Attempt Header Increments On Retry | ✅ | 16710ms |
| Header Behavior On Retry.Request Id Preserved On Retry | ✅ | 9285ms |
| Header Behavior On Retry.Different Requests Have Different Request Ids | ✅ | 7584ms |
| Header Behavior On Retry.Request Timestamp Changes On Retry | ✅ | 9290ms |
| Response Format Validation.Success Response Has Uuid Keyed Results | ✅ | 2875ms |
| Response Format Validation.Success Response Has Ok For Each Event | ✅ | 2847ms |
| Response Format Validation.Success No Retry After When All Ok | ✅ | 2931ms |
| Response Format Validation.Success Retry After Present When Retry Events | ✅ | 2844ms |
| Response Format Validation.Success No Retry After When Drop Only | ✅ | 2852ms |
| Response Format Validation.Response Echoes Request Id | ✅ | 551ms |
| Retry Behavior.Retries On 408 | ✅ | 9202ms |
| Retry Behavior.Retries On 500 | ✅ | 9190ms |
| Retry Behavior.Retries On 503 | ✅ | 11843ms |
| Retry Behavior.Retries On 504 | ✅ | 9153ms |
| Retry Behavior.Retryable Errors Have Retry After | ✅ | 6308ms |
| Retry Behavior.Respects Retry After On Retryable Error | ✅ | 10794ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2378ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 4799ms |
| Retry Behavior.Does Not Retry On 402 | ✅ | 5086ms |
| Retry Behavior.Does Not Retry On 413 | ✅ | 2412ms |
| Retry Behavior.Does Not Retry On 415 | ✅ | 2356ms |
| Retry Behavior.Non Retryable Errors Have No Retry After | ✅ | 4737ms |
| Retry Behavior.Implements Backoff | ✅ | 21813ms |
| Retry Behavior.Max Retries Respected | ❌ | 21867ms |
| Partial Batch Handling.Handles 200 Full Success | ✅ | 2383ms |
| Partial Batch Handling.Handles 200 With All Ok | ✅ | 5908ms |
| Partial Batch Handling.Does Not Retry Dropped Events | ✅ | 5890ms |
| Partial Batch Handling.Does Not Retry Limited Events | ✅ | 5834ms |
| Partial Batch Handling.Prunes Ok Events On Partial Retry | ✅ | 7955ms |
| Partial Batch Handling.Prunes Dropped Events On Partial Retry | ✅ | 7923ms |
| Partial Batch Handling.Retries Only Retry Events From Partial | ✅ | 7871ms |
| Partial Batch Handling.Partial Retry Preserves Uuids | ✅ | 7853ms |
| Partial Batch Handling.Partial Retry Attempt Header Increments | ✅ | 7707ms |
| Partial Batch Handling.Partial Retry Request Id Preserved | ✅ | 7928ms |
| Partial Batch Handling.Respects Retry After On Partial | ✅ | 7736ms |
| Partial Batch Handling.Unknown Result Treated As Terminal | ✅ | 5899ms |
| Partial Batch Handling.Mixed Ok Drop Limited No Retry | ✅ | 5861ms |
| Compression.Sends Gzip Content Encoding | ✅ | 2764ms |
| Compression.No Content Encoding When Disabled | ✅ | 2665ms |
| Compression.Compressed Body Is Decompressible | ✅ | 2735ms |
| Error Handling.Does Not Retry On Unknown 4Xx | ✅ | 4770ms |
| Event Options.Cookieless Mode Override | ✅ | 2762ms |
| Event Options.Disable Skew Correction Override | ✅ | 2784ms |
| Event Options.Process Person Profile Override | ✅ | 443ms |
| Event Options.Product Tour Id Override | ✅ | 2875ms |
| Event Options.Unset Options Omitted | ✅ | 2830ms |
| Event Options.Options Override In Batch | ✅ | 2853ms |
| Event Options.Unknown Option Passes Through | ✅ | 2781ms |
| Event Options.Option Value Is Not Converted | ✅ | 2968ms |
| Event Options.Lenient Bool Option Values Are Not Converted | ✅ | 2875ms |
| Event Options.Option Wins Over Legacy Property | ✅ | 2770ms |
| Event Options.Legacy Property Fills Unset Option | ✅ | 2767ms |
| Event Options.Null Option Falls Back To Legacy Property | ✅ | 543ms |
| Geoip And Historical Migration.Geoip Disable Injected Into Properties | ✅ | 2849ms |
| Geoip And Historical Migration.Historical Migration Set In Body | ❌ | 2903ms |
| Geoip And Historical Migration.Historical Migration Absent By Default | ✅ | 2834ms |
Failures
retry_behavior.max_retries_respected
Expected 4 requests, got 5
geoip_and_historical_migration.historical_migration_set_in_body
Body missing 'historical_migration' field. Body keys: ['created_at', 'batch']
Capture_Ai_V1 Tests
View Details
| Test | Status | Duration |
|---|---|---|
| Endpoint And Method.Targets V1 Endpoint | ✅ | 461ms |
| Endpoint And Method.Does Not Use Legacy Endpoints | ✅ | 2897ms |
| Endpoint And Method.Capture Does Not Reroute Ai Named Events | ✅ | 2849ms |
| Required Headers.Has Authorization Bearer Header | ✅ | 2818ms |
| Required Headers.Has Content Type Json | ✅ | 399ms |
| Required Headers.Has Posthog Sdk Info Format | ✅ | 2763ms |
| Required Headers.Has Posthog Attempt Header | ✅ | 2901ms |
| Required Headers.Has Posthog Request Id | ✅ | 2808ms |
| Required Headers.Has Posthog Request Timestamp | ✅ | 2909ms |
| Required Headers.Has User Agent | ✅ | 2883ms |
| Body Format.Body Has Created At And Batch | ✅ | 2767ms |
| Body Format.No Api Key In Body | ✅ | 514ms |
| Body Format.No Sent At In Body | ✅ | 316ms |
| Event Format.Event Has Required Root Fields | ✅ | 2803ms |
| Event Format.Event Uuid Is Valid | ✅ | 2844ms |
| Event Format.Event Timestamp Is Rfc3339 | ✅ | 2882ms |
| Event Format.Non Utc Event Timestamp Is Converted To Utc | ✅ | 2888ms |
| Event Format.Distinct Id Is String | ✅ | 2796ms |
| Event Format.Distinct Id At Root Not Properties | ✅ | 2705ms |
| Event Format.Custom Properties Preserved | ✅ | 2818ms |
| Event Format.Set Properties Preserved | ✅ | 2781ms |
| Event Format.Set Once Properties Preserved | ✅ | 415ms |
| Event Format.Groups Properties Preserved | ✅ | 2792ms |
| Event Format.Sdk Generates Uuid If Not Provided | ✅ | 2769ms |
| Event Format.Capture Ai Keeps Supplied Uuid | ❌ | 91ms |
| Event Format.Event Has Required Root Fields Batch | ❌ | 2835ms |
| Event Format.Event Uuid Is Valid Batch | ✅ | 2871ms |
| Event Format.Event Timestamp Is Rfc3339 Batch | ✅ | 2876ms |
| Event Format.Distinct Id Is String Batch | ✅ | 2722ms |
| Event Format.Distinct Id At Root Not Properties Batch | ✅ | 409ms |
| Event Format.Custom Properties Preserved Batch | ✅ | 2770ms |
| Event Format.Set Properties Preserved Batch | ✅ | 2817ms |
| Event Format.Set Once Properties Preserved Batch | ✅ | 550ms |
| Event Format.Groups Properties Preserved Batch | ✅ | 2906ms |
| Event Format.Sdk Generates Uuid If Not Provided Batch | ✅ | 2884ms |
| Batch Behavior.Multiple Events In Single Batch | ✅ | 2976ms |
| Batch Behavior.Batch Envelope Smoke | ✅ | 2967ms |
| Batch Behavior.Flush At Triggers Batch | ✅ | 1175ms |
| Batch Behavior.Created At Reflects Batch Creation Time | ✅ | 2763ms |
| Deduplication.Generates Unique Uuids | ✅ | 3096ms |
| Deduplication.Different Events Same Content Different Uuids | ✅ | 2970ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5540ms |
| Deduplication.Preserves Timestamp On Retry | ✅ | 9304ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 8099ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 2987ms |
| Header Behavior On Retry.Attempt Header Starts At One | ✅ | 2765ms |
| Header Behavior On Retry.Attempt Header Increments On Retry | ✅ | 15844ms |
| Header Behavior On Retry.Request Id Preserved On Retry | ✅ | 9325ms |
| Header Behavior On Retry.Different Requests Have Different Request Ids | ✅ | 7947ms |
| Header Behavior On Retry.Request Timestamp Changes On Retry | ✅ | 5473ms |
| Response Format Validation.Success Response Has Uuid Keyed Results | ✅ | 2953ms |
| Response Format Validation.Success Response Has Ok For Each Event | ✅ | 2993ms |
| Response Format Validation.Success No Retry After When All Ok | ✅ | 3004ms |
| Response Format Validation.Success Retry After Present When Retry Events | ✅ | 2864ms |
| Response Format Validation.Success No Retry After When Drop Only | ✅ | 2901ms |
| Response Format Validation.Response Echoes Request Id | ✅ | 401ms |
| Retry Behavior.Retries On 408 | ✅ | 9247ms |
| Retry Behavior.Retries On 500 | ✅ | 8864ms |
| Retry Behavior.Retries On 503 | ✅ | 11867ms |
| Retry Behavior.Retries On 504 | ✅ | 9296ms |
| Retry Behavior.Retryable Errors Have Retry After | ✅ | 6264ms |
| Retry Behavior.Respects Retry After On Retryable Error | ✅ | 10920ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 4925ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 4789ms |
| Retry Behavior.Does Not Retry On 402 | ✅ | 4833ms |
| Retry Behavior.Does Not Retry On 413 | ✅ | 4901ms |
| Retry Behavior.Does Not Retry On 415 | ✅ | 4680ms |
| Retry Behavior.Non Retryable Errors Have No Retry After | ✅ | 4852ms |
| Retry Behavior.Implements Backoff | ✅ | 19275ms |
| Retry Behavior.Max Retries Respected | ❌ | 21232ms |
| Partial Batch Handling.Handles 200 Full Success | ✅ | 4910ms |
| Partial Batch Handling.Handles 200 With All Ok | ✅ | 5885ms |
| Partial Batch Handling.Does Not Retry Dropped Events | ✅ | 5851ms |
| Partial Batch Handling.Does Not Retry Limited Events | ✅ | 5879ms |
| Partial Batch Handling.Prunes Ok Events On Partial Retry | ✅ | 7868ms |
| Partial Batch Handling.Prunes Dropped Events On Partial Retry | ✅ | 7838ms |
| Partial Batch Handling.Retries Only Retry Events From Partial | ✅ | 7875ms |
| Partial Batch Handling.Partial Retry Preserves Uuids | ✅ | 7943ms |
| Partial Batch Handling.Partial Retry Attempt Header Increments | ✅ | 7898ms |
| Partial Batch Handling.Partial Retry Request Id Preserved | ✅ | 7879ms |
| Partial Batch Handling.Respects Retry After On Partial | ✅ | 7859ms |
| Partial Batch Handling.Unknown Result Treated As Terminal | ✅ | 5804ms |
| Partial Batch Handling.Mixed Ok Drop Limited No Retry | ✅ | 5948ms |
| Compression.Sends Gzip Content Encoding | ✅ | 2833ms |
| Compression.No Content Encoding When Disabled | ❌ | 2869ms |
| Compression.Compressed Body Is Decompressible | ✅ | 2829ms |
| Error Handling.Does Not Retry On Unknown 4Xx | ✅ | 4826ms |
| Event Options.Cookieless Mode Override | ✅ | 2790ms |
| Event Options.Disable Skew Correction Override | ✅ | 2888ms |
| Event Options.Process Person Profile Override | ✅ | 2811ms |
| Event Options.Product Tour Id Override | ✅ | 2809ms |
| Event Options.Unset Options Omitted | ✅ | 2685ms |
| Event Options.Options Override In Batch | ✅ | 2814ms |
| Event Options.Unknown Option Passes Through | ✅ | 3002ms |
| Event Options.Option Value Is Not Converted | ✅ | 2747ms |
| Event Options.Lenient Bool Option Values Are Not Converted | ✅ | 2817ms |
| Event Options.Option Wins Over Legacy Property | ✅ | 2653ms |
| Event Options.Legacy Property Fills Unset Option | ✅ | 336ms |
| Event Options.Null Option Falls Back To Legacy Property | ✅ | 2663ms |
| Geoip And Historical Migration.Geoip Disable Injected Into Properties | ✅ | 366ms |
| Geoip And Historical Migration.Historical Migration Absent By Default | ✅ | 2654ms |
Failures
event_format.capture_ai_keeps_supplied_uuid
Expected last action result '0198c0de-0000-7000-8000-000000000abc', got '01a16aef-b9a1-7ea9-b9fc-a5bc8d0a1874'
event_format.event_has_required_root_fields_batch
Expected 1 requests, got 2
retry_behavior.max_retries_respected
Expected 4 requests, got 5
compression.no_content_encoding_when_disabled
Header 'Content-Encoding' should be absent but has value 'gzip'
Feature_Flags Tests
✅ 17/17 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 2762ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 2712ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 2759ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 2847ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 2818ms |
| Request Payload.Groups Round Trip | ✅ | 2815ms |
| Request Payload.Groups Default To Empty Object | ✅ | 2801ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 2876ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 2822ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 2734ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 156ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 2828ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 5413ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 2704ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 2849ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 2855ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 2942ms |
f633793 to
6fbe490
Compare
6fbe490 to
63569d4
Compare
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, post a comment or remove the |
63569d4 to
86a175b
Compare
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 86a175b: 5 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
86a175b to
4466c97
Compare
4466c97 to
1194cef
Compare
743c39f to
cb70f65
Compare
cb70f65 to
c37f915
Compare
c37f915 to
3630b21
Compare
| /// Soft request size for the AI endpoint, matching the other PostHog SDKs. | ||
| let aiMaxBatchBytes = 5 * 1024 * 1024 | ||
| /// AI events can be megabytes, so keep fewer of them on disk than analytics events. | ||
| let aiMaxQueueSize = 100 |
There was a problem hiding this comment.
Other v1 SDKs give the AI lane its own settings, separate from analytics: compression, queue size and (in Python and Go) a request timeout of at least 30 s, since AI events can be several MB and shouldn't share analytics' limits. Here the AI lane inherits compression, flushAt, maxBatchSize and the 10 s timeout, and hardcodes 100 events / 50 MiB. Could we add AI-specific settings, or document these as fixed mobile limits if that's the better fit for iOS?
There was a problem hiding this comment.
I didn't find AI-specific settings in the other SDKs: Node only has enableFullAiCapture, Python's AI lane uses the client's flush, batch, timeout and gzip settings, Go doesn't have AI capture on main yet, and the capture-ai spec only defines enable_full_ai_capture. Is that changing with the v1 work? If the other SDKs are adding AI settings, I'll add matching ones here.
For now, 3791af1 documents these as fixed mobile limits on captureAi. AI requests now time out after 60 s instead of 10 s. flushAt, maxBatchSize, flushIntervalSeconds and compression still come from the shared config. The 5 MiB request size, the send at 5 MiB queued, and the 100 events / 50 MiB disk caps are fixed.
There was a problem hiding this comment.
sorry for the confusion - these are present on Rust/Go/Python (soon node) v1 branches only as that's the only place we've added capture_ai explicit interfaces and pipelines yet. The idea is to be able to tune those submit pipelines explicitly for AI events.
Check those out and then let me know what you think. since they are all backend SDKs the needs of this one may differ
There was a problem hiding this comment.
Thanks, added in 4dd9dc5: captureAiCompression, captureAiMaxQueueSize, captureAiTimeout and captureAiMaxEventBytes on PostHogConfig, named after the backend ones. Defaults stay mobile-friendly: gzip, 100 events, 60 s, 8 MiB. captureAiMaxEventBytes can only lower the limit, and the 50 MiB disk cap stays fixed so a big queue can't fill the phone.
| 'posthog-ios': minor | ||
| --- | ||
|
|
||
| Add `captureAi(...)` to send AI events such as `$ai_generation` to PostHog's AI endpoint (`/i/v1/ai/events`), which accepts events up to 8 MiB. It returns the event UUID. |
There was a problem hiding this comment.
The 4.0 guide has no section for captureAi yet. Can this PR add one covering the AI endpoint, the 8 MiB event limit, the 60-second timeout and the new 5 MiB analytics batch cap? How wrapper SDKs such as Flutter expose AI capture is still being worked out for all of them at once, so for now the guide can describe only the native iOS API.
3791af1 to
d5a1cbf
Compare
cf4e222 to
1e4bdfd
Compare
captureAi(...) takes capture's arguments, runs the same enrichment and beforeSend, and queues the event on its own disk queue for /i/v1/ai/events. It reuses the capture V1 transport (headers, per-event results, retry identity per endpoint, redirects, Retry-After), with no /batch fallback. The AI queue batches by bytes (5 MiB) as well as count, keeps at most 100 events, and drops events over 8 MiB + 64 KiB logging only the name and size. capture() never reroutes $ai_ events. The compliance adapter gains /capture_ai and the capture_ai_v1 capability. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The AI queue now drops its oldest events once they take more than 50 MiB on disk, on top of the 100-event limit. captureAi returns the queued event's UUID, or nil when the event isn't queued. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…lose() - close() flushes the AI queue along with the others, and aiQueue is guarded by setupLock like the other queues. - The AI queue flushes once a batch's worth of bytes (5 MiB) is queued, so multi-MB events are sent before the 50 MiB cap evicts them. - A flush reads about one byte-sized batch from disk instead of the whole window, so large AI events aren't all held in memory at once. - The compliance adapter's /capture_ai accepts bodies up to 9 MB and passes the harness timestamp. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The harness reads the action result's success field, as posthog-python's adapter returns it. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Capture V1 rejects requests over 10 MiB, and analytics batches were capped by count only. Give the analytics endpoint the same 5 MiB soft byte limit as the AI lane (renamed to captureV1MaxBatchBytes). An event over the limit is still sent alone, and 413 halving is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Document the AI lane's fixed limits on captureAi. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Add captureAiCompression, captureAiMaxQueueSize, captureAiTimeout and captureAiMaxEventBytes, matching the AI lane settings in the backend v1 SDKs. Defaults keep the current behavior: gzip, 100 events, 60 s, 8 MiB. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
4dd9dc5 to
5ba4a9a
Compare
| encode: { event in | ||
| guard let data = toJSONData(event.toJSON()) else { return nil } | ||
| // Name and size only: AI properties can hold prompts and media. | ||
| if data.count > maxEventBytes { |
There was a problem hiding this comment.
P2 · Enforce the configured properties limit separately from envelope overhead
With captureAiMaxEventBytes = 1024, an event containing 32 KiB of properties still queues and returns a UUID: maxEventBytes includes a 64 KiB envelope allowance that properties can consume. This violates the public option's documented properties cap and defeats callers' lower payload limits. Validate serialized properties against the configured cap separately.
💡 Motivation and Context
Adds
captureAi(...), which sends AI observability events ($ai_generation,$ai_span,$ai_trace,$ai_embedding, ...) to the capture V1 AI endpoint (POST /i/v1/ai/events). That endpoint accepts events up to 8 MiB. It follows thecapture_ailanes in posthog-rs (PostHog/posthog-rs#250), posthog-go (PostHog/posthog-go#323) and posthog-python (PostHog/posthog-python#1017, #1022), and Ingestion's goal of shippingcaptureAiwith every V1 release. Stacked on #938.captureAitakes the same arguments ascaptureand goes through the same enrichment andbeforeSend. Only the delivery route differs.capturenever reroutes$ai_*events.Retry-Aftercap. Retry identity is now tracked per endpoint, so the two lanes don't reset each other's attempt numbers. A 404 is terminal for AI: there is no/batchfallback.posthog.aiFolder). It is flushed byflush()andclose(), and created with the other queues so events left on disk by a previous launch still send.captureAireturns the queued event's UUID (@discardableResult), ornilwhen the event isn't queued: opted out, disabled, dropped bybeforeSend, or over the size limit.QueueEndpoint.maxBatchBytes). It drops an event over 8 MiB + 64 KiB when it is queued, logging only the event name and size. The queue also flushes as soon as it holds 5 MiB, so large events are sent before the disk cap evicts them, and a flush reads about one batch from disk at a time.QueueEndpoint.maxQueueBytes, enforced inPostHogFileBackedQueueon add and on load). Without the byte cap, 100 events at the size limit could take ~800 MiB.POST /capture_aiand advertisescapture_ai_v1.Differences from the spec and the other SDKs
The
capture-aispec only applies to server SDKs. Where iOS differs:uuidargument: iOScapturedoesn't take one either.captureAidoes return the UUID, like the spec.config.compression: the server SDKs send AI requests uncompressed by default.💚 How did you test it?
MockPostHogServer's new/i/v1/ai/eventsstub:beforeSendand options/batchrequestcaptureAireturns the sent event's UUID, andnilfor an event that isn't queuedflushAtPostHogFileBackedQueuetests for the byte cap (evicts the oldest, keeps an oversized newest record, trims on load) and for a byte-bounded read.main, includingtest-ios-simulator,test-no-traitsandPublic API snapshot.make lintpasses, andmake teston the top of the stack (feat!: remove the /batch fallback from capture V1 #940) ran 1185 tests. The only failures are the 13 known group-container issues, which also fail on the base branch locally.capture_ai_v1suite: 97/101. The 4 failures:capture_ai_keeps_supplied_uuid:captureAidoesn't take a UUID, likecapture. The test aborts before flushing, so its request lands in the next test and failsevent_has_required_root_fields_batchtoo.max_retries_respectedandunset_options_omitted: the same mobile failures as in the analytics suite.captureAion posthog.com / LLM analytics.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Built with Claude Code. The design follows the sdk-specs
capture-aispec, the V1 AI route inrust/capture/src/v1, and the posthog-rs, posthog-go and posthog-python AI lanes. Maintainer decisions: a separate AI lane with a small disk queue and no/batchfallback, PR'd separately from #938.🤖 Generated with Claude Code