diff --git a/mem0-ts/src/oss/src/embeddings/ollama.ts b/mem0-ts/src/oss/src/embeddings/ollama.ts index 41dea7bcf..1bafbeaa9 100644 --- a/mem0-ts/src/oss/src/embeddings/ollama.ts +++ b/mem0-ts/src/oss/src/embeddings/ollama.ts @@ -27,9 +27,12 @@ export class OllamaEmbedder implements Embedder { } catch (err) { logger.error(`Error ensuring model exists: ${err}`); } + // Ollama's Go server requires prompt to be a string. Coerce defensively + // since callers may pass values parsed from untrusted LLM JSON output. + const prompt = typeof text === "string" ? text : JSON.stringify(text); const response = await this.ollama.embeddings({ model: this.model, - prompt: text, + prompt, }); return response.embedding; } diff --git a/mem0-ts/src/oss/src/memory/index.ts b/mem0-ts/src/oss/src/memory/index.ts index 5d86e4b3b..46ca8bf2a 100644 --- a/mem0-ts/src/oss/src/memory/index.ts +++ b/mem0-ts/src/oss/src/memory/index.ts @@ -15,6 +15,7 @@ import { HistoryManagerFactory, } from "../utils/factory"; import { + FactRetrievalSchema, getFactRetrievalMessages, getUpdateMemoryMessages, parseMessages, @@ -261,7 +262,8 @@ export class Memory { const cleanResponse = removeCodeBlocks(response as string); let facts: string[] = []; try { - facts = JSON.parse(cleanResponse).facts || []; + const parsed = FactRetrievalSchema.parse(JSON.parse(cleanResponse)); + facts = parsed.facts; } catch (e) { console.error( "Failed to parse facts from LLM response:", diff --git a/mem0-ts/src/oss/src/prompts/index.ts b/mem0-ts/src/oss/src/prompts/index.ts index ef8c79756..e9694b3e5 100644 --- a/mem0-ts/src/oss/src/prompts/index.ts +++ b/mem0-ts/src/oss/src/prompts/index.ts @@ -1,9 +1,18 @@ import { z } from "zod"; +// Accepts a string directly, or an object with a "fact" or "text" key +// (common malformed shapes from smaller LLMs like llama3.1:8b). +const factItem = z.union([ + z.string(), + z.object({ fact: z.string() }).transform((o) => o.fact), + z.object({ text: z.string() }).transform((o) => o.text), +]); + // Define Zod schema for fact retrieval output export const FactRetrievalSchema = z.object({ facts: z - .array(z.string()) + .array(factItem) + .transform((arr) => arr.filter((s) => s.length > 0)) .describe("An array of distinct facts extracted from the conversation."), });