History

perf(server): scan only appended transcript bytes for usage summaries (#9024)

Wout Stiens committed 2 months ago

Commit 8f1ef8b1 parent 0bfb6df6 files · +963 −90Browse files

Showing the changes to apps/server/src/usage/usageTranscriptReader.test.ts (1 of 6 files). Show all

Unified · Split

apps/server/src/usage/usageTranscriptReader.test.ts+210−0
@@ -0,0 +1,210 @@
1+// @effect-diagnostics nodeBuiltinImport:off - resume coverage writes, appends
2+// to, and truncates real transcript files byte-exactly, mirroring the reader's
3+// own deliberate node:fs usage.
4+import * as NodeFSP from "node:fs/promises";
5+import * as NodeOS from "node:os";
6+import * as NodePath from "node:path";
7+
8+import { afterEach, assert, beforeEach, describe, it } from "@effect/vitest";
9+
10+import { readTranscriptRecords } from "./usageTranscriptReader.ts";
11+
12+let dir: string;
13+
14+beforeEach(async () => {
15+ dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "usage-reader-test-"));
16+});
17+
18+afterEach(async () => {
19+ await NodeFSP.rm(dir, { recursive: true, force: true });
20+});
21+
22+function claudeLine(id: number, outputTokens: number): string {
23+ return `${JSON.stringify({
24+ type: "assistant",
25+ timestamp: "2026-08-01T10:00:00Z",
26+ requestId: `req_${id}`,
27+ sessionId: "session-1",
28+ message: {
29+ id: `msg_${id}`,
30+ model: "claude-fable-5",
31+ usage: { input_tokens: 10, output_tokens: outputTokens },
32+ },
33+ })}\n`;
34+}
35+
36+function codexMetaLine(): string {
37+ return `${JSON.stringify({
38+ type: "session_meta",
39+ timestamp: "2026-08-01T10:00:00Z",
40+ payload: { type: "session_meta", id: "codex-session-1" },
41+ })}\n`;
42+}
43+
44+function codexModelLine(model: string): string {
45+ return `${JSON.stringify({
46+ type: "turn_context",
47+ timestamp: "2026-08-01T10:00:01Z",
48+ payload: { type: "turn_context", model },
49+ })}\n`;
50+}
51+
52+function codexUsageLine(outputTokens: number, secondsOffset: number): string {
53+ return `${JSON.stringify({
54+ type: "event_msg",
55+ timestamp: `2026-08-01T10:00:${String(secondsOffset).padStart(2, "0")}Z`,
56+ payload: {
57+ type: "token_count",
58+ info: { last_token_usage: { input_tokens: 100, output_tokens: outputTokens } },
59+ },
60+ })}\n`;
61+}
62+
63+describe("readTranscriptRecords resume", () => {
64+ it("parses only appended lines when resuming a grown file", async () => {
65+ const path = NodePath.join(dir, "claude.jsonl");
66+ await NodeFSP.writeFile(path, claudeLine(1, 5) + claudeLine(2, 7));
67+ const first = await readTranscriptRecords(path, "claude");
68+ assert.isNotNull(first);
69+ assert.strictEqual(first.records.length, 2);
70+ assert.isFalse(first.resumed);
71+
72+ await NodeFSP.appendFile(path, claudeLine(3, 11));
73+ const second = await readTranscriptRecords(path, "claude", first.position);
74+ assert.isNotNull(second);
75+ assert.isTrue(second.resumed);
76+ assert.strictEqual(second.records.length, 1);
77+ assert.strictEqual(second.records[0]?.totals.outputTokens, 11);
78+
79+ // The stitched result matches a from-scratch parse of the whole file.
80+ const full = await readTranscriptRecords(path, "claude");
81+ assert.isNotNull(full);
82+ assert.deepStrictEqual([...first.records, ...second.records], [...full.records]);
83+ });
84+
85+ it("carries the Codex reducer state across the resume boundary", async () => {
86+ const path = NodePath.join(dir, "rollout.jsonl");
87+ await NodeFSP.writeFile(path, codexMetaLine() + codexModelLine("gpt-5.2-codex"));
88+ const first = await readTranscriptRecords(path, "codex");
89+ assert.isNotNull(first);
90+ assert.strictEqual(first.records.length, 0);
91+
92+ // The appended usage event has no turn_context or session_meta of its own;
93+ // model and session must come from the state captured before the boundary.
94+ await NodeFSP.appendFile(path, codexUsageLine(9, 5));
95+ const second = await readTranscriptRecords(path, "codex", first.position);
96+ assert.isNotNull(second);
97+ assert.isTrue(second.resumed);
98+ assert.strictEqual(second.records.length, 1);
99+ assert.strictEqual(second.records[0]?.model, "gpt-5.2-codex");
100+ assert.strictEqual(second.records[0]?.sessionId, "codex-session-1");
101+ });
102+
103+ it("suppresses a Codex duplicate usage event that straddles the boundary", async () => {
104+ const path = NodePath.join(dir, "rollout.jsonl");
105+ await NodeFSP.writeFile(
106+ path,
107+ codexMetaLine() + codexModelLine("gpt-5.2-codex") + codexUsageLine(9, 5),
108+ );
109+ const first = await readTranscriptRecords(path, "codex");
110+ assert.isNotNull(first);
111+ assert.strictEqual(first.records.length, 1);
112+
113+ // Codex re-emits an unchanged token_count on stream boundaries; the copy
114+ // lands after the resume point and must still be dropped.
115+ await NodeFSP.appendFile(path, codexUsageLine(9, 5) + codexUsageLine(21, 8));
116+ const second = await readTranscriptRecords(path, "codex", first.position);
117+ assert.isNotNull(second);
118+ assert.isTrue(second.resumed);
119+ assert.deepStrictEqual(
120+ second.records.map((record) => record.totals.outputTokens),
121+ [21],
122+ );
123+ });
124+
125+ it("defers an unterminated trailing line to tailRecords, then consumes it once terminated", async () => {
126+ const path = NodePath.join(dir, "claude.jsonl");
127+ const unterminated = claudeLine(2, 7).trimEnd();
128+ await NodeFSP.writeFile(path, claudeLine(1, 5) + unterminated);
129+ const first = await readTranscriptRecords(path, "claude");
130+ assert.isNotNull(first);
131+ assert.strictEqual(first.records.length, 1);
132+ assert.strictEqual(first.tailRecords.length, 1);
133+ assert.strictEqual(first.tailRecords[0]?.totals.outputTokens, 7);
134+
135+ // Completing the line and appending another re-reads from the resume
136+ // point, so the once-tail record arrives exactly once as a line record.
137+ await NodeFSP.appendFile(path, `\n${claudeLine(3, 11)}`);
138+ const second = await readTranscriptRecords(path, "claude", first.position);
139+ assert.isNotNull(second);
140+ assert.isTrue(second.resumed);
141+ assert.deepStrictEqual(
142+ second.records.map((record) => record.totals.outputTokens),
143+ [7, 11],
144+ );
145+ assert.strictEqual(second.tailRecords.length, 0);
146+ });
147+
148+ it("re-parses from the start when the guard bytes no longer match", async () => {
149+ const path = NodePath.join(dir, "claude.jsonl");
150+ await NodeFSP.writeFile(path, claudeLine(1, 5));
151+ const first = await readTranscriptRecords(path, "claude");
152+ assert.isNotNull(first);
153+
154+ // Same path, larger size, different content: a replaced file, not growth.
155+ await NodeFSP.writeFile(path, claudeLine(4, 13) + claudeLine(5, 17));
156+ const second = await readTranscriptRecords(path, "claude", first.position);
157+ assert.isNotNull(second);
158+ assert.isFalse(second.resumed);
159+ assert.deepStrictEqual(
160+ second.records.map((record) => record.totals.outputTokens),
161+ [13, 17],
162+ );
163+ });
164+
165+ it("re-parses from the start when the file shrank below the resume point", async () => {
166+ const path = NodePath.join(dir, "claude.jsonl");
167+ await NodeFSP.writeFile(path, claudeLine(1, 5) + claudeLine(2, 7));
168+ const first = await readTranscriptRecords(path, "claude");
169+ assert.isNotNull(first);
170+
171+ await NodeFSP.writeFile(path, claudeLine(3, 11));
172+ const second = await readTranscriptRecords(path, "claude", first.position);
173+ assert.isNotNull(second);
174+ assert.isFalse(second.resumed);
175+ assert.deepStrictEqual(
176+ second.records.map((record) => record.totals.outputTokens),
177+ [11],
178+ );
179+ });
180+
181+ it("parses a line larger than one stream chunk", async () => {
182+ // Tool-heavy transcripts carry multi-megabyte single lines; they arrive
183+ // split across many chunks and must reassemble into one record.
184+ const path = NodePath.join(dir, "claude.jsonl");
185+ const bigLine = `${JSON.stringify({
186+ type: "assistant",
187+ timestamp: "2026-08-01T10:00:00Z",
188+ requestId: "req_big",
189+ sessionId: "session-1",
190+ padding: "x".repeat(512 * 1024),
191+ message: {
192+ id: "msg_big",
193+ model: "claude-fable-5",
194+ usage: { input_tokens: 10, output_tokens: 42 },
195+ },
196+ })}\n`;
197+ await NodeFSP.writeFile(path, bigLine + claudeLine(2, 7));
198+
199+ const parsed = await readTranscriptRecords(path, "claude");
200+ assert.isNotNull(parsed);
201+ assert.deepStrictEqual(
202+ parsed.records.map((record) => record.totals.outputTokens),
203+ [42, 7],
204+ );
205+ });
206+
207+ it("returns null for an unreadable file", async () => {
208+ assert.isNull(await readTranscriptRecords(NodePath.join(dir, "missing.jsonl"), "claude"));
209+ });
210+});