mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:00:42 +00:00
perf(memory): avoid duplicate session store reads
This commit is contained in:
@@ -53,6 +53,13 @@ function writeSessionJsonl(fileName: string, records: readonly unknown[]): strin
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function buildSessionEntryWithoutStoreClassification(filePath: string) {
|
||||
return buildSessionEntry(filePath, {
|
||||
generatedByCronRun: false,
|
||||
generatedByDreamingNarrative: false,
|
||||
});
|
||||
}
|
||||
|
||||
describe("listSessionFilesForAgent", () => {
|
||||
it("includes reset and deleted transcripts in session file listing", async () => {
|
||||
const sessionsDir = path.join(tmpDir, "agents", "main", "sessions");
|
||||
@@ -104,7 +111,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
|
||||
// The content should have 3 lines (3 message records)
|
||||
@@ -131,7 +138,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "empty-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry!.content).toBe("");
|
||||
expect(entry!.lineMap).toEqual([]);
|
||||
@@ -149,7 +156,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "gaps.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry!.lineMap).toEqual([3, 5]);
|
||||
expect(entry!.messageTimestampsMs).toEqual([0, 0]);
|
||||
@@ -174,7 +181,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "timestamps.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry!.messageTimestampsMs).toEqual([
|
||||
Date.parse("2026-04-05T10:00:00.000Z"),
|
||||
@@ -215,7 +222,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "enveloped-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
|
||||
const contentLines = entry!.content.split("\n");
|
||||
@@ -253,7 +260,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "enveloped-session-array.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry!.content).toBe("User: Actual user text");
|
||||
});
|
||||
@@ -271,7 +278,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "wrapped-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
|
||||
const contentLines = entry!.content.split("\n");
|
||||
@@ -293,7 +300,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "hard-wrapped-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
|
||||
const contentLines = entry!.content.split("\n");
|
||||
@@ -317,7 +324,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "surrogate-safe-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
|
||||
const contentLines = entry!.content.split("\n");
|
||||
@@ -344,7 +351,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "assistant-sentinel.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry!.content).toBe(`Assistant: ${assistantText}`);
|
||||
});
|
||||
@@ -367,7 +374,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "dreaming-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry?.generatedByDreamingNarrative).toBe(true);
|
||||
@@ -478,7 +485,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "dreaming-prompt-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry?.generatedByDreamingNarrative).toBeUndefined();
|
||||
@@ -595,7 +602,7 @@ describe("buildSessionEntry", () => {
|
||||
|
||||
for (const testCase of cases) {
|
||||
const filePath = writeSessionJsonl(testCase.fileName, testCase.records);
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
|
||||
expect(entry, testCase.name).not.toBeNull();
|
||||
expect(entry?.content, testCase.name).toBe(testCase.content);
|
||||
@@ -626,7 +633,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "spoof-attempt-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry?.content).toContain(
|
||||
@@ -644,8 +651,8 @@ describe("buildSessionEntry", () => {
|
||||
fsSync.writeFileSync(deletedPath, content);
|
||||
fsSync.writeFileSync(checkpointPath, content);
|
||||
|
||||
const deletedEntry = await buildSessionEntry(deletedPath);
|
||||
const checkpointEntry = await buildSessionEntry(checkpointPath);
|
||||
const deletedEntry = await buildSessionEntryWithoutStoreClassification(deletedPath);
|
||||
const checkpointEntry = await buildSessionEntryWithoutStoreClassification(checkpointPath);
|
||||
|
||||
expect(deletedEntry).not.toBeNull();
|
||||
expect(deletedEntry?.content).toBe("");
|
||||
@@ -672,7 +679,7 @@ describe("buildSessionEntry", () => {
|
||||
const filePath = path.join(tmpDir, "substring-marker-session.jsonl");
|
||||
fsSync.writeFileSync(filePath, jsonlLines.join("\n"));
|
||||
|
||||
const entry = await buildSessionEntry(filePath);
|
||||
const entry = await buildSessionEntryWithoutStoreClassification(filePath);
|
||||
|
||||
expect(entry).not.toBeNull();
|
||||
expect(entry?.generatedByDreamingNarrative).toBeUndefined();
|
||||
|
||||
@@ -221,18 +221,18 @@ export function loadSessionTranscriptClassificationForAgent(
|
||||
);
|
||||
}
|
||||
|
||||
function isDreamingNarrativeTranscriptFromSessionStore(absPath: string): boolean {
|
||||
function classifySessionTranscriptFromSessionStore(absPath: string): {
|
||||
generatedByDreamingNarrative: boolean;
|
||||
generatedByCronRun: boolean;
|
||||
} {
|
||||
const sessionsDir = path.dirname(absPath);
|
||||
const normalizedAbsPath = normalizeComparablePath(absPath);
|
||||
const classification = loadSessionTranscriptClassificationForSessionsDir(sessionsDir);
|
||||
return classification.dreamingNarrativeTranscriptPaths.has(normalizedAbsPath);
|
||||
}
|
||||
|
||||
function isCronRunTranscriptFromSessionStore(absPath: string): boolean {
|
||||
const sessionsDir = path.dirname(absPath);
|
||||
const normalizedAbsPath = normalizeComparablePath(absPath);
|
||||
const classification = loadSessionTranscriptClassificationForSessionsDir(sessionsDir);
|
||||
return classification.cronRunTranscriptPaths.has(normalizedAbsPath);
|
||||
return {
|
||||
generatedByDreamingNarrative:
|
||||
classification.dreamingNarrativeTranscriptPaths.has(normalizedAbsPath),
|
||||
generatedByCronRun: classification.cronRunTranscriptPaths.has(normalizedAbsPath),
|
||||
};
|
||||
}
|
||||
|
||||
export async function listSessionFilesForAgent(agentId: string): Promise<string[]> {
|
||||
@@ -473,10 +473,16 @@ export async function buildSessionEntry(
|
||||
const collected: string[] = [];
|
||||
const lineMap: number[] = [];
|
||||
const messageTimestampsMs: number[] = [];
|
||||
const sessionStoreClassification =
|
||||
opts.generatedByDreamingNarrative === undefined || opts.generatedByCronRun === undefined
|
||||
? classifySessionTranscriptFromSessionStore(absPath)
|
||||
: null;
|
||||
let generatedByDreamingNarrative =
|
||||
opts.generatedByDreamingNarrative ?? isDreamingNarrativeTranscriptFromSessionStore(absPath);
|
||||
opts.generatedByDreamingNarrative ??
|
||||
sessionStoreClassification?.generatedByDreamingNarrative ??
|
||||
false;
|
||||
const generatedByCronRun =
|
||||
opts.generatedByCronRun ?? isCronRunTranscriptFromSessionStore(absPath);
|
||||
opts.generatedByCronRun ?? sessionStoreClassification?.generatedByCronRun ?? false;
|
||||
for (let jsonlIdx = 0; jsonlIdx < lines.length; jsonlIdx++) {
|
||||
const line = lines[jsonlIdx];
|
||||
if (!line.trim()) {
|
||||
|
||||
Reference in New Issue
Block a user