summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/ui.test.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-10-10 00:06:31 +0900
committerAdam Malczewski <[email protected]>2026-10-10 00:06:31 +0900
commit9f2dcfaab421fa84837685a0b65678acf03d9ded (patch)
treea1ef40fbbf98b136a471889c5b02795b739753be /src/features/chat/ui.test.ts
parent591e2dbe5cbf2283d58debdf92943a075edc4569 (diff)
downloaddispatch-web-9f2dcfaab421fa84837685a0b65678acf03d9ded.tar.gz
dispatch-web-9f2dcfaab421fa84837685a0b65678acf03d9ded.zip
fix(turn-metrics): exact per-turn placement via turnId, decode-only TPS, step-level retention
Placement - Chunks carry their producing turn: StoredChunk.turnId (backend [email protected]) for committed rows (persisted in the IDB cache; old cached rows are upgraded on sync) and the live event's turnId for provisional ones (the optimistic echo is tagged on turn-start). - interleaveTurnMetrics attributes every group to its turn (turnId, else the turn owning its stepId) and puts the turn total after the turn's LAST visible group. No user-message counting: sending, streaming, steering, images, errored turns and chat-limit unloading can no longer shift metrics between turns — at any point during a turn, not just after it ends. A turn cut in half by the chat limit keeps its total under its visible tail. - Legacy rows (no turnId) fall back to an order-preserving, count-guarded match that leaves a turn blank rather than show another turn's numbers. - A turn that errors re-syncs metrics (the backend now persists them). TPS - Step TPS = outputTokens / decodeMs only (no genTotalMs fallback — it includes time-to-first-token). Turn TPS sums tokens and decode time over timed steps only. Tool execution is excluded by the backend. Display - Retention = this turn's first-step cache reads / previous turn's last-step prompt (step-level; turn sums made it pin at 100%). - Turn row shows output tokens + input processed (with cached), instead of a misleading 'total tok' that summed every step's full prompt.
Diffstat (limited to 'src/features/chat/ui.test.ts')
-rw-r--r--src/features/chat/ui.test.ts17
1 files changed, 8 insertions, 9 deletions
diff --git a/src/features/chat/ui.test.ts b/src/features/chat/ui.test.ts
index 9568813..bea5e77 100644
--- a/src/features/chat/ui.test.ts
+++ b/src/features/chat/ui.test.ts
@@ -373,8 +373,8 @@ describe("ChatView", () => {
expect(screen.getByText("Hi")).toBeInTheDocument();
expect(screen.getByText("Hello!")).toBeInTheDocument();
expect(screen.getByText(/step 1/)).toBeInTheDocument();
- expect(screen.getAllByText(/150 tok/)).toHaveLength(2);
- expect(screen.getByText(/turn 1 · 150 tok \(100 in \/ 50 out\)/)).toBeInTheDocument();
+ expect(screen.getByText(/step 1 · 100 in \/ 50 out/)).toBeInTheDocument();
+ expect(screen.getByText(/turn 1 · 50 tok out · 100 in/)).toBeInTheDocument();
expect(screen.getByText(/1\.2s/)).toBeInTheDocument();
});
@@ -478,11 +478,11 @@ describe("ChatView", () => {
// Both step-metrics and turn-metrics render
expect(screen.getByText(/step 1/)).toBeInTheDocument();
- expect(screen.getByText(/turn 1 · 100 tok/)).toBeInTheDocument();
+ expect(screen.getByText(/turn 1 · 20 tok out/)).toBeInTheDocument();
// They are in separate elements (different rows)
- const stepEl = screen.getByText(/step 1 · 100 tok/).closest("div");
- const turnEl = screen.getByText(/turn 1 · 100 tok/).closest("div");
+ const stepEl = screen.getByText(/step 1 · 80 in \/ 20 out/).closest("div");
+ const turnEl = screen.getByText(/turn 1 · 20 tok out/).closest("div");
expect(stepEl).not.toBe(turnEl);
});
@@ -554,9 +554,9 @@ describe("ChatView", () => {
// Step metrics rendered
expect(screen.getByText(/step 1/)).toBeInTheDocument();
- expect(screen.getAllByText(/15 tok/)).toHaveLength(2);
+ expect(screen.getByText(/step 1 · 10 in \/ 5 out/)).toBeInTheDocument();
// Turn metrics rendered
- expect(screen.getByText(/turn 1 · 15 tok \(10 in \/ 5 out\)/)).toBeInTheDocument();
+ expect(screen.getByText(/turn 1 · 5 tok out · 10 in/)).toBeInTheDocument();
// No "null" or "undefined" in the DOM
expect(screen.queryByText("null")).toBeNull();
expect(screen.queryByText("undefined")).toBeNull();
@@ -602,8 +602,7 @@ describe("ChatView", () => {
render(ChatView, { props: { chunks, turnMetrics } });
// Step metrics should render
- expect(screen.getByText(/step 1/)).toBeInTheDocument();
- expect(screen.getByText(/150 tok/)).toBeInTheDocument();
+ expect(screen.getByText(/step 1 · 100 in \/ 50 out/)).toBeInTheDocument();
// Turn total should NOT render (total is null — turn still in progress)
expect(screen.queryByText(/^turn/)).toBeNull();