Repository navigation
Read the canonical AI usage block and map legacy token keys at ingest - #1698
Conversation
Every producer sent its own token metadata keys, so AI usage could not be aggregated or priced the same way across producers. Shared v2.8.1 adds an optional strict `ai` block to the v2 event; Usage Monitor now accepts it. - Persist the block under reserved `_ai*` metadata keys (model requested and served, harness, seat, the six token buckets, duration, TTFT, billing mode, cost source, and `_aiSource`). Producer metadata cannot set them. - Map the legacy keys (promptTokens, completionTokens, cachedTokens, cacheWriteTokens, cacheWriteOneHourTokens, inputTokens, outputTokens, cachedInputTokens, and tokenType / token:<type> slices) onto the same keys for v1 and v2 events. A tokenType slice uses only its own quantity, so BotFleet's repeated turn totals are not counted once per slice. - Ingest cost derivation prices the canonical tokens by served model, then requested model, then keyRef, including 1-hour cache writes priced from the catalog's above_1hr rate. - A replay of a row stored before the projection existed dedupes instead of raising an idempotency collision. - Bump congress-trading-shared to v2.8.1 and document the block in docs/api-contract.md. Fixes UM-1592 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…ge-schema # Conflicts: # src/lib/external-usage-events.ts
The DSH and Clutch collectors (UM-1594) already send harness, billingMode, durationMs and the output slice's reasoningTokens in metadata. Carry them onto the reserved _ai* keys so legacy collector rows match the typed block. Refs UM-1592 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Business Rules ValidationTask: 4906370138 — OpenRouter provider: adapter + live prod row (MONET, owner-directed 2026-07-16) Intent Comparison
FindingsMUST_FIX: PR scope does not match the task scope (OpenRouter provider adapter)Requirement: "PR #375 MUST_FIX: No evidence of prod-row provisioning for OpenRouter in this PR diffRequirement: "Prod row created via session-authed API with the owner's management key (secret-handoff, never printed); first poll green: balance $25.00, MTD $0. Row shows keyPreview MUST_FIX: No evidence of catalog follow-ups required by the task in this PR diffRequirement: "Follow-ups in catalog: multi-workspace key enumeration, per-app cost attribution via api_key_hash-filtered activity." (AC #1) INFO: Mirror/read-only contract for this issueRequirement: "Edit the effort board ( Requirements Verified
Analysis performed by Kodus AI Business Rules Validator
|
…replay Review findings on #1698: - Cost derivation now tries the served model, then the requested model, then keyRef, so a served id the catalog lacks no longer hides a priceable one. - The idempotency comparison ignores the server-computed _derivedCost* stamps. A replay derived from canonical tokens (or under a newer pricing snapshot) no longer reads as a producer change and returns 409. Refs UM-1592 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kody Review CompleteGreat news! 🎉 Keep up the excellent work! 🚀 Kody Guide: Usage and ConfigurationInteracting with Kody
Current Kody ConfigurationReview OptionsThe following review options are enabled or disabled:
|
|
Missing edge-case test coverage for Kody rule violation: Every exported schema or utility ships with tests and must typecheck |
… to AiRequest Resolves the pricing conflicts in favour of UM-1591's TokenBreakdown and 1-hour rate handling, and routes legacy tokenType slices through its canonicalTokenType (cacheWrite1h, cacheCreation + cacheTtl 1h, and aliases). The UM-1590 per-request store landed, so a v2 event that carried the typed `ai` block now also gets one AiRequest row (sources "ingest", dedupe key ingest:<idempotencyKey>). Best effort: the usage event is the record of truth, so a store failure is logged and the batch is still acknowledged. Refs UM-1592 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Fixes UM-1592. Every producer sent its own token metadata keys, so Usage Monitor could not aggregate or price AI usage the same way across producers. congress-trading-shared v2.8.1 (Simple-With-Us/congress-trading-shared#378, #381) adds an optional strict
aiblock to the v2 event. This PR teaches ingest to read it._aiModelRequested,_aiModelServed,_aiHarness,_aiSeat,_aiTokensInput,_aiTokensOutput,_aiTokensCacheRead,_aiTokensCacheWrite5m,_aiTokensCacheWrite1h,_aiTokensReasoning,_aiDurationMs,_aiTtftMs,_aiBillingMode,_aiCostSource,_aiSource(blockorlegacy). Since the UM-1590 per-request store has now landed, each event that carried the typed block also gets oneAiRequestrow (sources: ingest, dedupe keyingest:<idempotencyKey>), best effort so a store failure never fails the ACK. Legacy-mapped rows get none, because collector rows are slices or window totals.promptTokens,completionTokens,cachedTokens,cacheWriteTokens,cacheWriteOneHourTokens,inputTokens,outputTokens,cachedInputTokens, andtokenType/label: token:<type>slices (names resolved through UM-1591'scanonicalTokenType, so 1-hour aliases andcacheTtl: "1h"land incacheWrite1h), plus collectorharness,seat,billingMode,durationMs,ttftMsand output-slicereasoningTokens. Prompt figures include cache, so cache counts are subtracted to get uncached input. A slice uses only its ownquantity, because BotFleet repeats the whole turn's totals on every slice.keyRef(first that resolves), with 1-hour cache writes going to UM-1591'sTokenBreakdown.cacheCreation1h. Reasoning is inside output and never priced twice. Still metadata-only and still behindINGEST_COST_DERIVATION_ENABLED._ai*projection. The idempotency comparison ignores the projection when only one side has it, so the replay dedupes instead of returning 409. When both sides have it, it is compared as usual. Server-computed_derivedCost*stamps are always ignored in that comparison.docs/api-contract.md.No prod env changes. Existing rows are not backfilled; aggregations that read
label: token:<type>are unchanged.Verification
npx tsc --noEmitclean.vitestafter merging main:ai-usage-metadata,usage-telemetry,derive-ingest-cost,model-pricing*,external-usage-events*, ingestroute, OTLP routes and mappers (18 files, 293 passed).eslinton changed files: no errors.🤖 Generated with Claude Code