Skip to content

Commit 8d5f6c8

Browse files
committed
perf: reuse preflight transcript scan size
1 parent 1395d71 commit 8d5f6c8

2 files changed

Lines changed: 63 additions & 43 deletions

File tree

src/auto-reply/reply/agent-runner-memory.test.ts

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,7 @@ describe("runMemoryFlushIfNeeded", () => {
16141614
usage: { input: 40_000, output: 2_000 },
16151615
},
16161616
}),
1617-
].join("\n"),
1617+
].join("\n") + "\n",
16181618
"utf8",
16191619
);
16201620
registerMemoryFlushPlanResolverForTest(() => ({
@@ -1631,36 +1631,50 @@ describe("runMemoryFlushIfNeeded", () => {
16311631
updatedAt: Date.now(),
16321632
totalTokensFresh: false,
16331633
};
1634+
const originalStat = fsCore.promises.stat.bind(fsCore.promises);
1635+
const statSpy = vi
1636+
.spyOn(fsCore.promises, "stat")
1637+
.mockImplementation(async (target, options) => originalStat(target, options));
16341638

1635-
const entry = await runPreflightCompactionIfNeeded({
1636-
cfg: {
1637-
agents: {
1638-
defaults: {
1639-
compaction: {
1640-
memoryFlush: {},
1641-
truncateAfterCompaction: true,
1642-
maxActiveTranscriptBytes: "10mb",
1639+
let entry: SessionEntry | undefined;
1640+
let directTranscriptStats: unknown[] = [];
1641+
try {
1642+
entry = await runPreflightCompactionIfNeeded({
1643+
cfg: {
1644+
agents: {
1645+
defaults: {
1646+
compaction: {
1647+
memoryFlush: {},
1648+
truncateAfterCompaction: true,
1649+
maxActiveTranscriptBytes: "10mb",
1650+
},
16431651
},
16441652
},
16451653
},
1646-
},
1647-
followupRun: createTestFollowupRun({
1648-
sessionId: "session",
1649-
sessionFile,
1654+
followupRun: createTestFollowupRun({
1655+
sessionId: "session",
1656+
sessionFile,
1657+
sessionKey: "main",
1658+
}),
1659+
defaultModel: "anthropic/claude-opus-4-6",
1660+
agentCfgContextTokens: 100_000,
1661+
sessionEntry,
1662+
sessionStore: { main: sessionEntry },
16501663
sessionKey: "main",
1651-
}),
1652-
defaultModel: "anthropic/claude-opus-4-6",
1653-
agentCfgContextTokens: 100_000,
1654-
sessionEntry,
1655-
sessionStore: { main: sessionEntry },
1656-
sessionKey: "main",
1657-
storePath: path.join(rootDir, "sessions.json"),
1658-
isHeartbeat: false,
1659-
replyOperation: createReplyOperation(),
1660-
});
1664+
storePath: path.join(rootDir, "sessions.json"),
1665+
isHeartbeat: false,
1666+
replyOperation: createReplyOperation(),
1667+
});
1668+
directTranscriptStats = statSpy.mock.calls.filter(
1669+
([target]) => String(target) === sessionFile,
1670+
);
1671+
} finally {
1672+
statSpy.mockRestore();
1673+
}
16611674

16621675
expect(entry).toBe(sessionEntry);
16631676
expect(compactEmbeddedAgentSessionMock).not.toHaveBeenCalled();
1677+
expect(directTranscriptStats).toEqual([]);
16641678
});
16651679

16661680
it("triggers preflight compaction when the active transcript exceeds the configured byte threshold", async () => {

src/auto-reply/reply/agent-runner-memory.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ function estimatePostUsageTrailingBytes(lines: string[]): number {
571571
type TranscriptTokenEstimate = {
572572
promptTokens: number;
573573
outputTokens?: number;
574+
transcriptByteSize?: number;
574575
transcriptBytesTokens?: number;
575576
};
576577

@@ -620,6 +621,7 @@ async function estimatePromptTokensFromSessionTranscript(params: {
620621
return {
621622
promptTokens: Math.ceil(promptTokens),
622623
outputTokens: Math.ceil(outputTokens),
624+
transcriptByteSize: snapshot.byteSize,
623625
transcriptBytesTokens,
624626
};
625627
}
@@ -648,6 +650,7 @@ async function estimatePromptTokensFromSessionTranscript(params: {
648650
typeof outputTokens === "number" && Number.isFinite(outputTokens) && outputTokens > 0
649651
? Math.ceil(outputTokens)
650652
: undefined,
653+
transcriptByteSize: snapshot.byteSize,
651654
transcriptBytesTokens,
652655
};
653656
}
@@ -657,6 +660,7 @@ async function estimatePromptTokensFromSessionTranscript(params: {
657660
}
658661
return {
659662
promptTokens: Math.ceil(estimatedTokens),
663+
transcriptByteSize: snapshot.byteSize,
660664
transcriptBytesTokens,
661665
};
662666
} catch {
@@ -740,29 +744,11 @@ export async function runPreflightCompactionIfNeeded(params: {
740744
typeof persistedTotalTokens === "number" &&
741745
Number.isFinite(persistedTotalTokens) &&
742746
persistedTotalTokens > 0;
743-
const maxActiveTranscriptBytes = resolveMaxActiveTranscriptBytes(params.cfg);
744-
const shouldCheckActiveTranscriptBytes = typeof maxActiveTranscriptBytes === "number";
745-
const transcriptSizeSnapshot = shouldCheckActiveTranscriptBytes
746-
? await readSessionLogSnapshot({
747-
sessionId: entry.sessionId,
748-
sessionEntry:
749-
entry.sessionFile || !params.followupRun.run.sessionFile
750-
? entry
751-
: { ...entry, sessionFile: params.followupRun.run.sessionFile },
752-
sessionKey: params.sessionKey ?? params.followupRun.run.sessionKey,
753-
opts: { storePath: params.storePath },
754-
includeByteSize: true,
755-
includeUsage: false,
756-
})
757-
: undefined;
758-
const activeTranscriptBytes = transcriptSizeSnapshot?.byteSize;
759-
const shouldCompactByTranscriptBytes =
760-
typeof activeTranscriptBytes === "number" &&
761-
typeof maxActiveTranscriptBytes === "number" &&
762-
activeTranscriptBytes >= maxActiveTranscriptBytes;
763747
const promptTokenEstimate = estimatePromptTokensForMemoryFlush(
764748
params.promptForEstimate ?? params.followupRun.prompt,
765749
);
750+
const maxActiveTranscriptBytes = resolveMaxActiveTranscriptBytes(params.cfg);
751+
const shouldCheckActiveTranscriptBytes = typeof maxActiveTranscriptBytes === "number";
766752
const transcriptUsageTokens =
767753
typeof freshPersistedTokens === "number"
768754
? undefined
@@ -773,6 +759,26 @@ export async function runPreflightCompactionIfNeeded(params: {
773759
sessionFile: entry.sessionFile ?? params.followupRun.run.sessionFile,
774760
storePath: params.storePath,
775761
});
762+
const transcriptSizeSnapshot =
763+
shouldCheckActiveTranscriptBytes && transcriptUsageTokens?.transcriptByteSize === undefined
764+
? await readSessionLogSnapshot({
765+
sessionId: entry.sessionId,
766+
sessionEntry:
767+
entry.sessionFile || !params.followupRun.run.sessionFile
768+
? entry
769+
: { ...entry, sessionFile: params.followupRun.run.sessionFile },
770+
sessionKey: params.sessionKey ?? params.followupRun.run.sessionKey,
771+
opts: { storePath: params.storePath },
772+
includeByteSize: true,
773+
includeUsage: false,
774+
})
775+
: undefined;
776+
const activeTranscriptBytes =
777+
transcriptUsageTokens?.transcriptByteSize ?? transcriptSizeSnapshot?.byteSize;
778+
const shouldCompactByTranscriptBytes =
779+
typeof activeTranscriptBytes === "number" &&
780+
typeof maxActiveTranscriptBytes === "number" &&
781+
activeTranscriptBytes >= maxActiveTranscriptBytes;
776782
const stalePersistedPromptTokens = hasPersistedTotalTokens
777783
? Math.floor(persistedTotalTokens)
778784
: undefined;

0 commit comments

Comments
 (0)