重建会话记录逻辑

This commit is contained in:
2026-06-04 15:26:23 +08:00
parent 0ecb2babf3
commit 0188240d62
9 changed files with 375 additions and 42 deletions
+76
View File
@@ -0,0 +1,76 @@
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { MemoryStore } from "../../src/memory/store.js";
describe("MemoryStore", () => {
let tempDir: string;
let backupDir: string;
let store: MemoryStore;
beforeEach(async () => {
tempDir = await mkdtemp(join(tmpdir(), "tjwater-memory-"));
backupDir = await mkdtemp(join(tmpdir(), "tjwater-memory-backup-"));
store = new MemoryStore(tempDir, backupDir);
await store.initialize();
});
afterEach(async () => {
await rm(tempDir, { force: true, recursive: true });
await rm(backupDir, { force: true, recursive: true });
});
it("dedupes exact duplicate memories", async () => {
const first = await store.upsert("workspace", "project-1", {
content: "DMA-2 nightly leakage analysis should compare against adjacent zones first.",
source: "tool",
});
const second = await store.upsert("workspace", "project-1", {
content: "DMA-2 nightly leakage analysis should compare against adjacent zones first.",
source: "tool",
});
expect(first.changed).toBe(true);
expect(second.changed).toBe(false);
expect(second.detail).toBe("memory already existed");
});
it("rejects rewritten memories that are too similar to an existing one", async () => {
await store.upsert("workspace", "project-1", {
content: "保存记忆前先查看当前 workspace memory,避免重复写入相同约束。",
source: "tool",
});
const result = await store.upsert("workspace", "project-1", {
content: "写入前先看一遍当前 workspace 记忆,避免把同样的约束重复保存进去。",
source: "tool",
});
expect(result.changed).toBe(false);
expect(result.detail).toBe("similar memory already exists");
expect(result.entry?.content).toBe("保存记忆前先查看当前 workspace memory,避免重复写入相同约束。");
});
it("rejects replace when the new content overlaps a similar existing memory", async () => {
const first = await store.upsert("user", "actor-1", {
content: "回答时默认使用中文,并保持结论先行。",
source: "tool",
});
const second = await store.upsert("user", "actor-1", {
content: "回答要包含必要的文件路径引用。",
source: "tool",
});
const result = await store.replace("user", "actor-1", second.entry?.id ?? "", {
content: "默认使用中文回答,结论放在最前面。",
source: "tool",
});
expect(first.changed).toBe(true);
expect(second.changed).toBe(true);
expect(result.changed).toBe(false);
expect(result.detail).toBe("replacement would overlap with a similar existing memory");
});
});
+22
View File
@@ -2,6 +2,7 @@ import { describe, expect, it } from "bun:test";
import {
buildPromptWithLearningContext,
extractLatestFrontendTurn,
generateSessionTitle,
shouldGenerateSessionTitle,
} from "../../src/routes/chatSession.js";
@@ -161,3 +162,24 @@ describe("buildPromptWithLearningContext", () => {
expect(prompt).toBe("基于刚才结果继续分析");
});
});
describe("extractLatestFrontendTurn", () => {
it("extracts the latest valid frontend user and assistant turn", () => {
const turn = extractLatestFrontendTurn([
{ role: "user", content: "检查 DMA-2 漏损" },
{
role: "assistant",
content: "DMA-2 夜间最小流量持续抬升。",
progress: [{ id: "tool-dma", phase: "tool" }],
},
{ role: "user", content: "继续分析相邻分区" },
{ role: "assistant", content: "⚠️ **请求已中断**", isError: true },
]);
expect(turn).toEqual({
assistantMessage: "DMA-2 夜间最小流量持续抬升。",
toolCallCount: 1,
userMessage: "检查 DMA-2 漏损",
});
});
});
+33
View File
@@ -135,4 +135,37 @@ describe("SessionTranscriptStore", () => {
expect(forkRecentTurns).toHaveLength(1);
expect(forkRecentTurns[0]?.assistantMessage).toBe("第一轮回复");
});
it("does not duplicate the latest turn when the frontend state is saved again", async () => {
await store.appendTurn(
{
actorKey: "actor-3",
clientSessionId: "thread-3",
projectKey: "project-3",
sessionId: "thread-3",
},
{
assistantMessage: "已完成压力波动分析。",
toolCallCount: 1,
userMessage: "分析压力波动。",
},
);
const transcript = await store.appendTurn(
{
actorKey: "actor-3",
clientSessionId: "thread-3",
projectKey: "project-3",
sessionId: "thread-3",
},
{
assistantMessage: "已完成压力波动分析。",
toolCallCount: 2,
userMessage: "分析压力波动。",
},
);
expect(transcript.turns).toHaveLength(1);
expect(transcript.turns[0]?.toolCallCount).toBe(2);
});
});