rust/capture/docs/ai-endpoint-parity.md
This document compares the /i/v0/ai endpoint with the /i/v0/e (events) endpoint to identify feature gaps that need to be addressed to bring the AI endpoint to parity.
/i/v0/e handler: rust/capture/src/v0_endpoint.rs:118/i/v0/ai handler: rust/capture/src/ai_endpoint.rs:71rust/capture/src/limiters.rsrust/common/limiters/src/token_dropper.rs/i/v0/e (Events Endpoint)| Feature | Description | Priority | Status |
|---|---|---|---|
| Quota limiting and billing checks | Apply quota limiter checks via state.quota_limiter.check_and_filter(). Drop events if billing limits are exceeded. | HIGH | ✅ DONE |
| Token dropper filtering | Filter events based on token dropper rules (rate limiting per token + distinct_id basis) | HIGH | ✅ DONE |
| Historical rerouting | Apply historical rerouting logic based on event timestamp age | MEDIUM | Not needed |
/i/v0/ai (AI Endpoint)| Feature | Description | Notes |
|---|---|---|
| Multipart form-data parsing | Parse and validate multipart requests with events + blobs | AI-specific |
| Authorization header requirement | Require Bearer <token> in Authorization header | AI-specific |
| AI event type validation | Validate against 6 allowed AI event types: $ai_generation, $ai_trace, $ai_span, $ai_embedding, $ai_metric, $ai_feedback | AI-specific |
$ai_model property validation | Ensure $ai_model is present and non-empty | AI-specific |
| Blob part handling | Handle blob parts and generate S3 placeholders | AI-specific |
| Byte range calculation | Calculate byte ranges for blob parts in concatenated format | AI-specific |
| Content-Type validation per part | Validate Content-Type for each multipart part | AI-specific |
| Strict size limits per part type | 32KB event, 960KB properties + event, configurable total | AI-specific |
| Duplicate property name detection | Prevent duplicate property names across parts | AI-specific |
| Feature | Implementation Status |
|---|---|
| Gzip decompression | ✅ Both |
| Token validation | ✅ Both (format check, reject personal API keys) |
| Timestamp computation with clock skew correction | ✅ Both |
| Client IP extraction | ✅ Both |
| UUID handling | ✅ Both (generate v7 if missing) |
| Internal event IP redaction | ✅ Both (capture_internal property) |
| Kafka/sink publishing | ✅ Both |
| Metrics tracking middleware | ✅ Both |
| CORS handling | ✅ Both |
| User agent tracking | ✅ Both |
Implementation: The AI endpoint now enforces quota limits using the LLMEvents quota resource.
How it works:
state.quota_limiter.check_and_filter() with a single-element vectoris_llm_event() predicate matches all $ai_* eventscapture_quota_limit_exceeded and capture_events_dropped_totalKey changes:
HasEventName trait to common_types for generic quota checkingcheck_and_filter to be generic over T: HasEventNameHasEventName for EventMetadata struct in AI endpointImplementation: The AI endpoint now checks the token dropper before processing blob parts.
How it works:
retrieve_event_metadata()), checks state.token_dropper.should_drop(token, distinct_id)report_dropped_events("token_dropper", 1)Optimization: The token dropper check happens before parsing blob parts, saving processing time when events are dropped.
AI endpoint hardcodes historical_migration: false. This is intentional because:
/e uses denylist (blocks $performance_event), AI uses allowlist (only accepts 6 $ai_* events)/e supports batches