summaryrefslogtreecommitdiffhomepage
path: root/scripts/bench-pure.ts
blob: 0bff5a1152673c457298b65f47476d42bc64ba35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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),
});