summaryrefslogtreecommitdiffhomepage
path: root/scripts/bench-pure.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-10-10 01:36:39 +0900
committerAdam Malczewski <[email protected]>2026-10-10 01:36:39 +0900
commit551a7a050e8cd25e795026a1060c9d5b5ea028bf (patch)
treecba29eb5ada26778c09aea7cc767e2ddf665bce8 /scripts/bench-pure.ts
parentd8381e201cb1407a939b496642a0803aec58a47d (diff)
downloaddispatch-web-551a7a050e8cd25e795026a1060c9d5b5ea028bf.tar.gz
dispatch-web-551a7a050e8cd25e795026a1060c9d5b5ea028bf.zip
perf(chat): fast open of long in-flight turns
- Batch live chat events: fold a burst in one pass per animation frame (rAF + timeout fallback for hidden tabs) instead of one reactive cycle per WS frame. Flushed before send/resync to keep ordering. - De-dup the late-join replay against committed history: provisional chunks of the in-flight turn already persisted (CR-6 per-step persistence) are hidden by aligned-prefix matching (fail-safe: mismatch shows, never hides). - Memoize chunks/turnMetrics getters by state identity. - LazyCollapse: tool/thinking card bodies mount only on first open. - content-visibility:auto on transcript rows. - scripts/probe-replay.ts + bench-pure.ts for measuring the replay path.
Diffstat (limited to 'scripts/bench-pure.ts')
-rw-r--r--scripts/bench-pure.ts71
1 files changed, 71 insertions, 0 deletions
diff --git a/scripts/bench-pure.ts b/scripts/bench-pure.ts
new file mode 100644
index 0000000..0bff5a1
--- /dev/null
+++ b/scripts/bench-pure.ts
@@ -0,0 +1,71 @@
+/**
+ * Bench the PURE pipeline a tab runs when opening a conversation mid-turn:
+ * history fetch → applyHistory → fold every replayed event (one render-pass
+ * worth of derivations per event, like the UI does) .
+ * bun scripts/bench-pure.ts <conversationId> [scale]
+ * `scale` synthesizes a longer turn by repeating the replay's step events.
+ */
+import {
+ applyHistory,
+ foldEvent,
+ groupRenderedChunks,
+ initialState,
+ initialWindowSize,
+ selectChunks,
+ windowTranscript,
+} from "../src/core/chunks/index.ts";
+import { interleaveTurnMetrics } from "../src/core/metrics/index.ts";
+import { renderMarkdown } from "../src/features/markdown/logic/markdown.ts";
+
+const conv = process.argv[2] ?? "";
+const HTTP = process.env.PROBE_HTTP ?? "http://localhost:24991";
+const replay = JSON.parse(await Bun.file(`/tmp/replay-${conv}.json`).text()) as { type: string }[];
+const hist = (await (await fetch(`${HTTP}/conversations/${conv}?sinceSeq=0`)).json()) as {
+ chunks: { seq: number; turnId?: string; chunk: { type: string } }[];
+};
+const turnId = (replay.find((e) => e.type === "turn-start") as { turnId: string }).turnId;
+const committedThisTurn = hist.chunks.filter((c) => c.turnId === turnId).length;
+console.log(
+ `history chunks=${hist.chunks.length} (of which current turn=${committedThisTurn}), replay events=${replay.length}`,
+);
+
+let state = windowTranscript(
+ applyHistory(initialState(), hist.chunks as never),
+ initialWindowSize(256),
+);
+let derive = 0;
+let md = 0;
+const t0 = performance.now();
+for (const ev of replay) {
+ state = foldEvent(state, ev as never);
+ // What each per-event render recomputes in ChatView:
+ const d0 = performance.now();
+ const chunks = selectChunks(state);
+ interleaveTurnMetrics(groupRenderedChunks(chunks), []);
+ derive += performance.now() - d0;
+ const last = chunks[chunks.length - 1];
+ if (last?.streaming && last.chunk.type === "text") {
+ const m0 = performance.now();
+ renderMarkdown(last.chunk.text, { streaming: true });
+ md += performance.now() - m0;
+ }
+}
+const total = performance.now() - t0;
+const rendered = selectChunks(state);
+const dupTool = new Map<string, number>();
+for (const r of rendered) {
+ if (r.chunk.type === "tool-call" || r.chunk.type === "tool-result") {
+ const k = `${r.chunk.type}:${(r.chunk as { toolCallId: string }).toolCallId}`;
+ dupTool.set(k, (dupTool.get(k) ?? 0) + 1);
+ }
+}
+const dups = [...dupTool.values()].filter((n) => n > 1).length;
+console.log({
+ renderedChunks: rendered.length,
+ committed: state.committed.length,
+ provisional: state.provisional.length,
+ duplicatedToolChunks: dups,
+ totalMs: Math.round(total),
+ deriveMs: Math.round(derive),
+ markdownFullRerenderMs: Math.round(md),
+});