Skip to content

[Kiro] Event stream already carries per-request credit metering — adapter drops meteringEvent #5948

Closed

Description

@Smartnewb

Area: Provider adapters

What are you trying to accomplish?

Accurate per-request cost accounting for the kiro provider. Every Kiro request row in usage.jsonl currently records estimated: true token counts and cacheProvenance: "unknown", so there is no real usage/cost signal for Kiro traffic at all — not even the one the upstream already sends.

What prevents this today?

The Kiro event stream already emits a meteringEvent frame carrying the actual credits charged for the request, but KNOWN_EVENT_TYPES in src/adapters/kiro-events.ts does not include it, so parseKiroEvent() returns null and the frame is dropped silently — ocx debug provider on does not log it either.

Live capture (fresh free-tier Builder ID account, opencodex 2.65.0, raw decodeEventStream probe against runtime.us-east-1.kiro.dev):

glm-5:

#1 :event-type=initial-response        {"conversationId": "..."}      <- dropped
#2 :event-type=assistantResponseEvent  {"content": "...", "modelId"}
#3 :event-type=metadataEvent           {"stopReason": "END_TURN"}      <- no tokenUsage
#4 :event-type=contextUsageEvent       {"contextUsagePercentage": 1.94}
#5 :event-type=meteringEvent           {"unit": "credit", "unitPlural": "credits",
                                        "usage": 0.04582331509121062}  <- dropped

claude-sonnet-4.5 (same account/endpoint):

#1 initial-response, #2 assistantResponseEvent, #3 metadataEvent {stopReason},
#4 contextUsageEvent {contextUsagePercentage: 2.03},
#5 meteringEvent {"unit":"credit","usage":0.009624912835820896}

q.us-east-1.amazonaws.com/generateAssistantResponse emits the same meteringEvent shape.

Two facts worth stating plainly:

  • metadataEvent carried no tokenUsage in any observed frame, on either model, on either endpoint — the token-count absence is real upstream behavior, not an adapter miss.
  • The meteringEvent credit value is present every request and is currently invisible to opencodex.

What should OpenCodex do?

  1. Recognize meteringEvent (and defensively usageEvent/metricsEvent/tokenMetrics wrappers) and record the reported credit amount on the request log/usage row — e.g. a creditUsage field alongside OcxUsage, so usage.jsonl carries real spend instead of only heuristic tokens.
  2. Log dropped/unknown :event-type names via debugProviderDiagnostic when provider debug is on — today they leave no trace even in debug mode, which hides exactly this class of upstream data.
  3. Optionally: initial-response also carries conversationId and is dropped; when messageMetadataEvent is absent (it was on both captures above), the conversation id is lost.

Example

Current row:

"usage": {"inputTokens": 8, "outputTokens": 1, "contextTotalTokens": 3701, "estimated": true},
"cacheProvenance": "unknown"

Desired (exact field naming up to you):

"usage": { ..., "estimated": true },
"creditUsage": {"unit": "credit", "amount": 0.04582331509121062}

Alternatives or workarounds

The heuristic estimator (devlog 142/160) is the right answer for input_tokens/output_tokens display and Codex auto-compact — keep it. This proposal only adds the billing signal next to it. Another implementation of the same subscription surface, 0a00/Kiro-Go-Plus (proxy/eventstream_payload.go, proxy/kiro.go), already classifies meteringEvent/usageEvent/metricsEvent frames and recursively hunts nested usage envelopes, including Anthropic-style cacheReadInputTokens/cacheCreation splits when present — useful reference for the priority order.

Additional context

Activity

  1. Smartnewb commented on Sep 26, 2026

    @Smartnewb
    ContributorAuthor

    Follow-up evidence from live probing (same free-tier account, today). Three things that sharpen the picture:

    1. Server-side prompt caching demonstrably exists — it's just invisible

    Same ~4KB prefix re-sent seconds apart, glm-5, runtime.us-east-1.kiro.dev:

    request first byte credits (meteringEvent)
    A1 same prefix 6799ms 0.054826968490878945
    A2 same prefix 1042ms 0.054826968490878945 (identical)
    B1 different prefix, same size 6769ms 0.05617810613598675
    A3 same prefix 1114ms 0.054826968490878945

    The cold→warm→cold→warm pattern by prefix content (not request order — B1 was 4th and still cold) shows a real server-side prefix cache. Credits are charged in full either way — caching affects latency, not billing. So the practical signal pair is: credits identical + large TTFT drop = cache hit, which an operator could only notice if the credit field is captured at all.

    2. meteringEvent scales with input size — it's a real usage signal, not a flat fee

    • ~10-token prompt, glm-5: 0.0319–0.0458 credits
    • ~4KB prefix, glm-5: 0.0548 credits
    • ~10-token prompt, claude-sonnet-4.5: 0.0096 credits

    Model and size dependent → worth recording as the per-request cost field.

    3. kiro-cli's own v3 engine shows what richer usage looks like

    kiro-cli chat --v3 --output-format stream-json runs a local KAS (Kiro Agent Server) and emits session_info_update notifications carrying:

    {"contextUsage":{"usagePercentage":1.97,
      "breakdown":{"tools":{"tokens":5122,"percent":0.5},
                   "yourPrompts":{"tokens":4100,"percent":0.4},
                   "contextFiles":{"tokens":0},"kiroResponses":{"tokens":0},
                   "sessionFiles":{"tokens":0}}},
     "promptTurnSummaries":[{"unit":"credit","usage":0.0624}],"requestIds":["..."]}

    Per-category token counts here are computed by the local agent server (it tokenizes the context it assembles) — not upstream-reported — but it's proof the information is obtainable client-side. Meanwhile the wire stream's metadataEvent.tokenUsage exists in the schema yet was absent from every frame we observed; kiro-cli's TUI nonetheless has cachedReadTokens/cachedWriteTokens fields in its /usage panel, so the upstream can populate it under some conditions (see also kirodotdev/Kiro#9992: ACP only emits credits since kiro-cli 2.10).

    Independent implementation cross-check: pi-provider-kiro declares 12 event keys (assistantResponseEvent, codeReferenceEvent, contextUsageEvent, documentCitationEvent, error, metadataEvent, meteringEvent, reasoningContentEvent, serviceUnavailableError, throttlingError, toolResultEvent, toolUseEvent, validationError) — vs the 7 in KNOWN_EVENT_TYPES. 0a00/Kiro-Go-Plus additionally hunts tokenUsage/usage/tokenMetrics envelopes recursively inside any frame, preserving cacheRead/cacheCreation5m/1h splits when present.

    Net ask stands: capture meteringEvent (and any usage-bearing envelope) into the request log — it is the only authoritative per-request usage the wire actually sends today, and it also makes server-side cache hits indirectly observable via the credits-vs-TTFT pattern.

  2. lidge-jun commented on Sep 26, 2026

    @lidge-jun
    Owner

    Fixed on dev by #5951, carried in bug-PR merge train batch 9B, #5985 (merge bf04176). Kiro meteringEvent frames are now parsed, the latest credit reading is kept for the turn, and it is written to the usage ledger. Closing; please reopen if a build that includes bf04176 still drops the credits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions