summaryrefslogtreecommitdiffhomepage
path: root/scripts/bench-pure.ts
diff options
context:
space:
mode:
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),
+});