From 6db12ff70acb3333d05a5020ab66da4172a5225a Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Wed, 10 Jun 2026 08:29:59 +0900 Subject: feat(metrics): durable per-turn/step token+timing metrics (observability spans + persisted replay) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two-part token-data improvement: #2 Observability spans (kernel run-turn): turn & step span-close now stamp ALL four Usage fields — added usage.cacheReadTokens/cacheWriteTokens (were silently dropped) and normalized usage_* -> usage.* to match the provider.request span (consistent D9 GROUP BY). No contract change. #3 Persisted replay metrics (conversation-store + read endpoint): new StepMetrics/TurnMetrics wire types; conversation-store persists per-turn metrics in a separate key space (appendMetrics/loadMetrics, turn-append order); session-orchestrator accumulates per-step+turn metrics from the event stream (pure metrics.ts) and persists after seal; transport-http serves GET /conversations/:id/metrics -> ConversationMetricsResponse. Contracts: @dispatch/wire + @dispatch/transport-contract bumped 0.3.0->0.4.0 (additive). GLOSSARY: turn metrics / step metrics. typecheck EXIT 0, biome clean, 546 vitest + 89 bun = 635 tests. --- packages/kernel/src/runtime/run-turn.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'packages/kernel/src/runtime/run-turn.ts') diff --git a/packages/kernel/src/runtime/run-turn.ts b/packages/kernel/src/runtime/run-turn.ts index 06069a2..d5db1bf 100644 --- a/packages/kernel/src/runtime/run-turn.ts +++ b/packages/kernel/src/runtime/run-turn.ts @@ -50,6 +50,20 @@ function addUsage(a: Usage, b: Usage): Usage { return { inputTokens, outputTokens }; } +function usageAttrs(usage: Usage): Record { + const attrs: Record = { + "usage.inputTokens": usage.inputTokens, + "usage.outputTokens": usage.outputTokens, + }; + if (usage.cacheReadTokens !== undefined) { + attrs["usage.cacheReadTokens"] = usage.cacheReadTokens; + } + if (usage.cacheWriteTokens !== undefined) { + attrs["usage.cacheWriteTokens"] = usage.cacheWriteTokens; + } + return attrs; +} + function appendTextDelta(chunks: Chunk[], delta: string): void { const lastIdx = chunks.length - 1; const last = chunks[lastIdx]; @@ -409,8 +423,7 @@ async function executeStep(ctx: StepContext): Promise { stepSpan.end({ attrs: { finishReason, - usage_inputTokens: stepUsage.inputTokens, - usage_outputTokens: stepUsage.outputTokens, + ...usageAttrs(stepUsage), }, }); } catch { @@ -533,8 +546,7 @@ export async function runTurn(input: RunTurnInput): Promise { turnSpan.end({ attrs: { finishReason, - usage_inputTokens: totalUsage.inputTokens, - usage_outputTokens: totalUsage.outputTokens, + ...usageAttrs(totalUsage), }, }); } catch { -- cgit v1.2.3