106 lines · 3.3 KB · TypeScript
RawDownload
/** * Session-load cost curve: how long foldSessionEvents takes as a transcript * grows. Event shapes copied from pi-event-applier.test.ts so the reducer * actually folds them. Run: bun run scripts/bench/session-fold.bench.ts (from frontend/, for the path aliases). */import { foldSessionEvents } from "../../frontend/src/features/agent/runtime/pi-event-applier"; type Event = Record<string, unknown>; const textPart = (text: string) => ({ type: "text", text }); /** * One turn shaped like a real rollout: the user's prompt echo, a streamed * assistant message that grows over `updates` frames, a tool call, and the * settled assistant message. */function buildTurn(turn: number, updates: number): Event[] {  const events: Event[] = [];  events.push({    type: "message_end",    message: { role: "user", content: [textPart(`prompt number ${turn}`)] },  });   let text = "";  events.push({    type: "message_start",    message: { role: "assistant", content: [textPart("")] },  });  for (let update = 0; update < updates; update += 1) {    text += `token${update} `;    events.push({      type: "message_update",      message: { role: "assistant", content: [textPart(text)] },    });  }  events.push({    type: "message_update",    message: {      role: "assistant",      content: [        textPart(text),        {          type: "toolCall",          toolCallId: `call-${turn}`,          toolName: "shell",          args: { command: "ls -la" },        },      ],    },  });  events.push({    type: "message_end",    message: {      role: "assistant",      content: [        textPart(text),        {          type: "toolCall",          toolCallId: `call-${turn}`,          toolName: "shell",          args: { command: "ls -la" },          result: "a\nb\nc\n".repeat(20),        },      ],    },  });  return events;} function buildLog(turns: number, updates: number): Event[] {  const events: Event[] = [{ type: "session", timestamp: new Date(0).toISOString() }];  for (let turn = 0; turn < turns; turn += 1) events.push(...buildTurn(turn, updates));  return events;} const median = (values: number[]): number =>  [...values].sort((a, b) => a - b)[Math.floor(values.length / 2)]; const SIZES = [25, 50, 100, 200, 400, 800];const UPDATES = 40;const RUNS = 5; console.log(`fold cost — ${UPDATES} updates/turn, median of ${RUNS} runs\n`);console.log("turns\tevents\tmsgs\tms\tµs/event\tscaling"); let previous: { turns: number; ms: number } | null = null;for (const turns of SIZES) {  const log = buildLog(turns, UPDATES);  let messages = 0;  const timings: number[] = [];  for (let run = 0; run < RUNS; run += 1) {    const started = performance.now();    const folded = foldSessionEvents(log);    timings.push(performance.now() - started);    messages = folded.messages.length;  }  const ms = median(timings);  // >1 means cost per unit of work is climbing — superlinear in transcript size.  const scaling =    previous === null ? "—" : `${(ms / previous.ms / (turns / previous.turns)).toFixed(2)}x`;  console.log(    `${turns}\t${log.length}\t${messages}\t${ms.toFixed(1)}\t${((ms * 1000) / log.length).toFixed(2)}\t\t${scaling}`,  );  previous = { turns, ms };}console.log("\nscaling >1 = superlinear in transcript length; ~1 = linear");