From b24052b895d9fc78aa4f4c76a77a8e8d512e002f Mon Sep 17 00:00:00 2001 From: roboomp Date: Fri, 3 Jul 2026 03:41:50 +0000 Subject: [PATCH] fix(mnemopi): preserve empty structured extraction Short-circuit successfully parsed structured extractor output even when all extraction arrays are empty, so the JSON response body is not stored as a fallback memory. Added coverage for plain and fenced empty extraction JSON. Fixes #4390 --- packages/mnemopi/src/core/extraction.ts | 4 +--- packages/mnemopi/test/extraction.test.ts | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/mnemopi/src/core/extraction.ts b/packages/mnemopi/src/core/extraction.ts index 231f66464..9679f40bb 100644 --- a/packages/mnemopi/src/core/extraction.ts +++ b/packages/mnemopi/src/core/extraction.ts @@ -115,9 +115,7 @@ export function parseFacts(rawOutput: string | null | undefined): string[] { } } } - if (out.length > 0) { - return out.slice(0, 5); - } + return out.slice(0, 5); } } catch { const matches = [...raw.matchAll(/"([^"]{10,})"/g)].map(m => m[1]).filter((v): v is string => v !== undefined); diff --git a/packages/mnemopi/test/extraction.test.ts b/packages/mnemopi/test/extraction.test.ts index d3ff7f5ad..02c4aa702 100644 --- a/packages/mnemopi/test/extraction.test.ts +++ b/packages/mnemopi/test/extraction.test.ts @@ -52,6 +52,13 @@ describe("structured extraction", () => { expect(parseFacts("NO_FACTS")).toEqual([]); }); + it("treats a valid empty structured extraction as no facts", () => { + expect(parseFacts('{"facts": [], "instructions": [], "preferences": [], "timelines": [], "kg": []}')).toEqual([]); + expect( + parseFacts('```json\n{"facts": [], "instructions": [], "preferences": [], "timelines": [], "kg": []}\n```'), + ).toEqual([]); + }); + it("uses deterministic heuristic extraction when no LLM is configured", async () => { process.env.MNEMOPI_LLM_ENABLED = "false"; const facts = await extractFactsSafe("My name is Ada. I work at Example Corp and I prefer dark mode.");