From b933c14913f94567e7c71ef83b15da1b7a4acdc0 Mon Sep 17 00:00:00 2001 From: wyn Date: Wed, 9 Sep 2026 09:38:01 +0700 Subject: [PATCH] feat(telemetry): forward cache_write_tokens to conductor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usage already computed cache_write internally (app/agent/usage.py), but the conductor telemetry hook only ever read cache_read off the usage dict — cache-write activity was silently dropped instead of reaching conductor's cache-accounting pipeline. Forwards it the same way cache_read already is, and extends the queueing test to assert it lands in the enqueued event. Companion change on the conductor side (evo-conductor repo) adds the cache_write_tokens field this now populates, plus the storage/aggregate work that uses it. --- app/agent/hooks/conductor_telemetry.py | 3 +++ app/conductor/constants/telemetry.py | 2 ++ tests/conductor/test_telemetry.py | 3 +++ 3 files changed, 8 insertions(+) diff --git a/app/agent/hooks/conductor_telemetry.py b/app/agent/hooks/conductor_telemetry.py index 74d04c23..8e940760 100644 --- a/app/agent/hooks/conductor_telemetry.py +++ b/app/agent/hooks/conductor_telemetry.py @@ -125,6 +125,7 @@ async def wrap_model_call( tokens_in=_counter(usage.get("input")), tokens_out=_counter(usage.get("output")), cache_read_tokens=_counter(usage.get("cache")), + cache_write_tokens=_counter(usage.get("cache_write")), reasoning_tokens=_counter(usage.get("thoughts")), tool_use_tokens=_counter(usage.get("tool_use")), estimated_cost_usd_micros=_cost_micros(usage), @@ -267,6 +268,7 @@ def _record( tokens_in: int = 0, tokens_out: int = 0, cache_read_tokens: int = 0, + cache_write_tokens: int = 0, reasoning_tokens: int = 0, tool_use_tokens: int = 0, tool_name: str | None = None, @@ -303,6 +305,7 @@ def _record( TelemetryField.TOKENS_IN: tokens_in, TelemetryField.TOKENS_OUT: tokens_out, TelemetryField.CACHE_READ_TOKENS: cache_read_tokens, + TelemetryField.CACHE_WRITE_TOKENS: cache_write_tokens, TelemetryField.REASONING_TOKENS: reasoning_tokens, TelemetryField.TOOL_USE_TOKENS: tool_use_tokens, TelemetryField.TOOL_NAME: tool_name, diff --git a/app/conductor/constants/telemetry.py b/app/conductor/constants/telemetry.py index 1cdd9ec5..890962e8 100644 --- a/app/conductor/constants/telemetry.py +++ b/app/conductor/constants/telemetry.py @@ -76,6 +76,7 @@ class TelemetryField(StrEnum): MODEL = "model" RESPONSE_MODEL = "response_model" CACHE_READ_TOKENS = "cache_read_tokens" + CACHE_WRITE_TOKENS = "cache_write_tokens" REASONING_TOKENS = "reasoning_tokens" TOOL_USE_TOKENS = "tool_use_tokens" TOOL_NAME = "tool_name" @@ -108,6 +109,7 @@ class TelemetryBatchField(StrEnum): TelemetryField.TOKENS_IN.value, TelemetryField.TOKENS_OUT.value, TelemetryField.CACHE_READ_TOKENS.value, + TelemetryField.CACHE_WRITE_TOKENS.value, TelemetryField.REASONING_TOKENS.value, TelemetryField.TOOL_USE_TOKENS.value, } diff --git a/tests/conductor/test_telemetry.py b/tests/conductor/test_telemetry.py index 509510e7..8aa63063 100644 --- a/tests/conductor/test_telemetry.py +++ b/tests/conductor/test_telemetry.py @@ -117,6 +117,7 @@ async def model_handler(_request: ModelRequest) -> AssistantMessage: "input": 120, "output": 40, "cache": 20, + "cache_write": 15, "thoughts": 10, "cost": {"estimated_usd": 0.00125}, }, @@ -143,6 +144,8 @@ async def tool_handler(_ctx, _state, _call) -> str: assert events[0][TelemetryField.MODEL] == "gpt-5.1" assert events[0][TelemetryField.TOKENS_IN] == 120 assert events[0][TelemetryField.TOKENS_OUT] == 40 + assert events[0][TelemetryField.CACHE_READ_TOKENS] == 20 + assert events[0][TelemetryField.CACHE_WRITE_TOKENS] == 15 assert events[0][TelemetryField.RESPONSE_MODEL] == "openai:gpt-5.1" assert events[0][TelemetryField.ESTIMATED_COST_USD_MICROS] == 1250 assert events[0][TelemetryField.COST_SOURCE] == "evoflux_catalog"