summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/store.svelte.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-07 18:41:27 +0900
committerAdam Malczewski <[email protected]>2026-06-07 18:41:27 +0900
commit9a4fc0aa0b56e7bb083ecc615647ed8f6b2ef4c2 (patch)
tree1fd5b4081736cf48c7b916abcdbeab267b4322d6 /src/features/chat/store.svelte.ts
parentdcf5a2993c9cbeb266f2629cf33fbc43f52c7ca8 (diff)
downloaddispatch-web-9a4fc0aa0b56e7bb083ecc615647ed8f6b2ef4c2.tar.gz
dispatch-web-9a4fc0aa0b56e7bb083ecc615647ed8f6b2ef4c2.zip
feat(chat): live turn metrics — telemetry reducer + rendering
Consume wire/transport-contract 0.3.0 (step-complete event + timing fields on usage/tool-result/done). Pure core/telemetry module: foldMetricEvent (reducer) + derived selectors (stepTps, turnTps, etc). TelemetryState is pure data, no active-turn tracking — consumers pass turnId to selectors. ChatStore wires foldMetricEvent into handleDelta and exposes telemetry + currentTurnId. ChatView shows step-metrics footer (time/TPS/tokens) on assistant text bubbles and durationMs badge on tool cards. New TurnSummary component renders turn-level stats (wall-clock, tokens, steps, TPS) in a DaisyUI stats block. Extended live-probe to verify telemetry events against bin/up (pending backend restart). 336 tests, typecheck 0, biome clean, build ok.
Diffstat (limited to 'src/features/chat/store.svelte.ts')
-rw-r--r--src/features/chat/store.svelte.ts12
1 files changed, 12 insertions, 0 deletions
diff --git a/src/features/chat/store.svelte.ts b/src/features/chat/store.svelte.ts
index 1d8ab17..58c165f 100644
--- a/src/features/chat/store.svelte.ts
+++ b/src/features/chat/store.svelte.ts
@@ -13,6 +13,8 @@ import {
selectChunks,
selectMessages,
} from "../../core/chunks";
+import type { TelemetryState } from "../../core/telemetry";
+import { foldMetricEvent, initialState as telemetryInitialState } from "../../core/telemetry";
import type { ConversationCache } from "../conversation-cache";
import type { ChatTransport, HistorySync } from "./ports";
@@ -30,6 +32,8 @@ export interface ChatStore {
readonly pendingSync: boolean;
readonly error: string | null;
readonly model: string | undefined;
+ readonly telemetry: TelemetryState;
+ readonly currentTurnId: string | null;
handleDelta(msg: ChatDeltaMessage | ChatErrorMessage): void;
send(text: string): void;
setModel(model: string): void;
@@ -42,6 +46,7 @@ export function createChatStore(deps: ChatStoreDependencies): ChatStore {
let _pendingSync = $state(false);
let _error = $state<string | null>(null);
let _model = $state<string | undefined>(deps.model);
+ let telemetry = $state<TelemetryState>(telemetryInitialState());
let disposed = false;
async function syncTail(): Promise<void> {
@@ -76,6 +81,12 @@ export function createChatStore(deps: ChatStoreDependencies): ChatStore {
get model(): string | undefined {
return _model;
},
+ get telemetry(): TelemetryState {
+ return telemetry;
+ },
+ get currentTurnId(): string | null {
+ return transcript.currentTurnId;
+ },
handleDelta(msg: ChatDeltaMessage | ChatErrorMessage): void {
if (msg.type === "chat.error") {
@@ -89,6 +100,7 @@ export function createChatStore(deps: ChatStoreDependencies): ChatStore {
return;
}
transcript = foldEvent(transcript, msg.event);
+ telemetry = foldMetricEvent(telemetry, msg.event);
if (transcript.sealedTurnId !== null) {
void syncTail();
}