Skip to content

Commit db15d5c

Browse files
authored
fix(oss): validate LLM fact output via FactRetrievalSchema before embedding (#4083)
1 parent aa4a944 commit db15d5c

File tree

3 files changed

+17
-3
lines changed

3 files changed

+17
-3
lines changed

mem0-ts/src/oss/src/embeddings/ollama.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ export class OllamaEmbedder implements Embedder {
2727
} catch (err) {
2828
logger.error(`Error ensuring model exists: ${err}`);
2929
}
30+
// Ollama's Go server requires prompt to be a string. Coerce defensively
31+
// since callers may pass values parsed from untrusted LLM JSON output.
32+
const prompt = typeof text === "string" ? text : JSON.stringify(text);
3033
const response = await this.ollama.embeddings({
3134
model: this.model,
32-
prompt: text,
35+
prompt,
3336
});
3437
return response.embedding;
3538
}

mem0-ts/src/oss/src/memory/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
HistoryManagerFactory,
1616
} from "../utils/factory";
1717
import {
18+
FactRetrievalSchema,
1819
getFactRetrievalMessages,
1920
getUpdateMemoryMessages,
2021
parseMessages,
@@ -261,7 +262,8 @@ export class Memory {
261262
const cleanResponse = removeCodeBlocks(response as string);
262263
let facts: string[] = [];
263264
try {
264-
facts = JSON.parse(cleanResponse).facts || [];
265+
const parsed = FactRetrievalSchema.parse(JSON.parse(cleanResponse));
266+
facts = parsed.facts;
265267
} catch (e) {
266268
console.error(
267269
"Failed to parse facts from LLM response:",

mem0-ts/src/oss/src/prompts/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import { z } from "zod";
22

3+
// Accepts a string directly, or an object with a "fact" or "text" key
4+
// (common malformed shapes from smaller LLMs like llama3.1:8b).
5+
const factItem = z.union([
6+
z.string(),
7+
z.object({ fact: z.string() }).transform((o) => o.fact),
8+
z.object({ text: z.string() }).transform((o) => o.text),
9+
]);
10+
311
// Define Zod schema for fact retrieval output
412
export const FactRetrievalSchema = z.object({
513
facts: z
6-
.array(z.string())
14+
.array(factItem)
15+
.transform((arr) => arr.filter((s) => s.length > 0))
716
.describe("An array of distinct facts extracted from the conversation."),
817
});
918

0 commit comments

Comments
 (0)