From f8bf715abc8a89ec0c6370b40403c509b1ce2870 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Wed, 10 Jun 2026 10:06:27 +0900 Subject: feat(metrics): per-turn + per-step token/timing metrics bubbles Consume wire@0.4.0 / transport-contract@0.4.0 metrics: usage.stepId, step-complete (ttft/decode/genTotal), done.durationMs/usage, and the durable GET /conversations/:id/metrics endpoint. - core/metrics: pure live-fold + durable-merge reducer; decode-rate TPS; head-aligned, stable placement; progressive per-step rows (each shown as its step ends) with the turn-total row gated on the done event. - features/chat: store folds metric events + hydrates durable TurnMetrics; ChatView renders inline step bubbles + a turn-total bubble. - app: MetricsSync HTTP effect (tolerates 404) injected into chat stores. - scripts/live-probe: drives the metrics path; live-verified 17/17 vs bin/up. - docs: regenerate .dispatch wire/transport mirrors to 0.4.0; glossary terms (turn/step metrics, TTFT, decode time, TPS, metrics bubble); trim handoff. --- src/app/App.svelte | 2 +- src/app/store.svelte.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/app') diff --git a/src/app/App.svelte b/src/app/App.svelte index 61b4cb9..857a1e5 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -62,7 +62,7 @@
{#key store.activeConversationId} - + {/key}
diff --git a/src/app/store.svelte.ts b/src/app/store.svelte.ts index 760c390..fe3c55c 100644 --- a/src/app/store.svelte.ts +++ b/src/app/store.svelte.ts @@ -2,6 +2,7 @@ import type { ChatDeltaMessage, ChatErrorMessage, ConversationHistoryResponse, + ConversationMetricsResponse, ModelsResponse, } from "@dispatch/transport-contract"; import type { SurfaceServerMessage, SurfaceSpec } from "@dispatch/ui-contract"; @@ -17,7 +18,7 @@ import { subscribe as protocolSubscribe, unsubscribe as protocolUnsubscribe, } from "../core/protocol"; -import type { ChatStore } from "../features/chat"; +import type { ChatStore, MetricsSync } from "../features/chat"; import { createChatStore } from "../features/chat"; import type { ConversationCache } from "../features/conversation-cache"; import { createConversationCache } from "../features/conversation-cache"; @@ -73,6 +74,15 @@ function createHistorySync( }; } +function createMetricsSync(httpBase: string, fetchImpl: typeof fetch): MetricsSync { + return async (conversationId: string) => { + const url = `${httpBase}/conversations/${encodeURIComponent(conversationId)}/metrics`; + const res = await fetchImpl(url); + if (!res.ok) return { turns: [] }; + return (await res.json()) as ConversationMetricsResponse; + }; +} + export function createAppStore(opts?: CreateAppStoreOptions): AppStore { let protocol = $state(protocolInitialState()); let selectedId = $state(null); @@ -112,6 +122,7 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore { ); const historySync = createHistorySync(httpBase, fetchImpl); + const metricsSync = createMetricsSync(httpBase, fetchImpl); const chatStores = new Map(); @@ -125,6 +136,7 @@ export function createAppStore(opts?: CreateAppStoreOptions): AppStore { }, }, historySync, + metricsSync, cache, }); } -- cgit v1.2.3