Harden legacy extraction and recall JSON parsing

This commit is contained in:
Hao19911125
2026-04-05 15:24:01 +08:00
parent 03b535f5a4
commit ca3fc8fc2f
4 changed files with 287 additions and 8 deletions

View File

@@ -2105,6 +2105,62 @@ async function testExtractorFailsOnUnknownOperation() {
}
}
async function testExtractorSupportsLegacyFlatNodeOperations() {
const graph = createEmptyGraph();
const restoreOverrides = pushTestOverrides({
llm: {
async callLLMForJSON() {
return {
operations: [
{
type: "event",
id: "evt-legacy",
title: "夜间喂食",
summary: "角色完成了一次深夜喂食。",
participants: "悟岳, 访客",
status: "resolved",
importance: 6,
},
{
type: "character",
id: "char-legacy",
name: "悟岳",
state: "放松下来",
},
],
};
},
},
});
try {
const result = await extractMemories({
graph,
messages: [{ seq: 7, role: "assistant", content: "测试旧版扁平提取输出" }],
startSeq: 7,
endSeq: 7,
schema,
embeddingConfig: null,
settings: {},
});
assert.equal(result.success, true);
assert.equal(result.newNodes, 2);
assert.equal(graph.lastProcessedSeq, 7);
const eventNode = graph.nodes.find((node) => node.type === "event");
const characterNode = graph.nodes.find((node) => node.type === "character");
assert.ok(eventNode);
assert.ok(characterNode);
assert.equal(eventNode.fields?.title, "夜间喂食");
assert.equal(eventNode.fields?.summary, "角色完成了一次深夜喂食。");
assert.equal(characterNode.fields?.name, "悟岳");
assert.equal(characterNode.fields?.state, "放松下来");
} finally {
restoreOverrides();
}
}
async function testConsolidatorMergeUpdatesSeqRange() {
const graph = createEmptyGraph();
const target = createNode({
@@ -5023,6 +5079,7 @@ async function testLlmOutputRegexCleansResponseBeforeJsonParse() {
await testCompressorMigratesEdgesToCompressedNode();
await testVectorIndexKeepsDirtyOnDirectPartialEmbeddingFailure();
await testExtractorFailsOnUnknownOperation();
await testExtractorSupportsLegacyFlatNodeOperations();
await testConsolidatorMergeUpdatesSeqRange();
await testConsolidatorMergeFallbackKeepsNodeWhenTargetMissing();
await testBatchJournalVectorDeltaCapturesRecoveryFields();

View File

@@ -329,6 +329,54 @@ assert.equal(state.llmOptions[0].returnFailureDetails, true);
assert.equal(state.llmOptions[0].maxRetries, 2);
assert.equal(state.llmOptions[0].maxCompletionTokens, 512);
state.vectorCalls.length = 0;
state.diffusionCalls.length = 0;
state.llmCalls.length = 0;
state.llmOptions.length = 0;
state.llmCandidateCount = 0;
state.llmResponse = { selectedIds: ["rule-1"] };
const llmCamelCaseResult = await retrieve({
graph,
userMessage: "换个 JSON 键名也应该兼容",
recentMessages: [],
embeddingConfig: {},
schema,
options: {
topK: 4,
maxRecallNodes: 2,
enableVectorPrefilter: true,
enableGraphDiffusion: false,
enableLLMRecall: true,
llmCandidatePool: 2,
},
});
assert.deepEqual(Array.from(llmCamelCaseResult.selectedNodeIds), ["rule-1"]);
assert.equal(llmCamelCaseResult.meta.retrieval.llm.status, "llm");
state.vectorCalls.length = 0;
state.diffusionCalls.length = 0;
state.llmCalls.length = 0;
state.llmOptions.length = 0;
state.llmCandidateCount = 0;
state.llmResponse = { data: { selected_ids: ["rule-2"] } };
const llmNestedResult = await retrieve({
graph,
userMessage: "嵌套 JSON 结构也应该兼容",
recentMessages: [],
embeddingConfig: {},
schema,
options: {
topK: 4,
maxRecallNodes: 2,
enableVectorPrefilter: true,
enableGraphDiffusion: false,
enableLLMRecall: true,
llmCandidatePool: 2,
},
});
assert.deepEqual(Array.from(llmNestedResult.selectedNodeIds), ["rule-2"]);
assert.equal(llmNestedResult.meta.retrieval.llm.status, "llm");
state.vectorCalls.length = 0;
state.diffusionCalls.length = 0;
state.llmCalls.length = 0;