From 3fc3caeb98e01332e14bbc0ed4d95bcc4436db32 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Wed, 7 Oct 2026 00:06:07 +0530 Subject: [PATCH] fix(relay): native Anthropic stream spans keep their input and cache token counts On the native Anthropic stream, every Relay LLM span exported prompt, cache-read and cache-write token counts as missing; only output tokens survived. The SDK's MessageDeltaUsage serializes its unset fields (input_tokens, cache_read_input_tokens, cache_creation_input_tokens) as null, and AnthropicStreamAccumulator's message_delta merge let those nulls overwrite the real values captured from message_start. The Anthropic SDK's own accumulator merges only non-null delta fields; ours now does the same. Hermes' own session accounting was unaffected (it reads the SDK snapshot); the loss was the Relay trace, which is what cache-reuse analysis of exported traces runs on. --- agent/relay_llm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent/relay_llm.py b/agent/relay_llm.py index 85f52be258..270cbc9a66 100644 --- a/agent/relay_llm.py +++ b/agent/relay_llm.py @@ -648,7 +648,9 @@ class AnthropicStreamAccumulator: if "usage" in payload: usage, current_usage = payload["usage"], self._message.get("usage") if isinstance(current_usage, dict) and isinstance(usage, dict): - usage = {**current_usage, **usage} + # The SDK's MessageDeltaUsage serializes unset fields as null; they must not + # erase message_start's input/cache counts. + usage = {**current_usage, **{k: v for k, v in usage.items() if v is not None}} self._message["usage"] = usage _EVENT_HANDLERS = {