diff --git a/openclaw/index.ts b/openclaw/index.ts index 4b4c890414..ac30db1000 100644 --- a/openclaw/index.ts +++ b/openclaw/index.ts @@ -25,7 +25,11 @@ import type { AddOptions, SearchOptions, } from "./types.ts"; -import { createProvider, providerToBackend } from "./providers.ts"; +import { + createProvider, + customCategoryMapToList, + providerToBackend, +} from "./providers.ts"; import { mem0ConfigSchema } from "./config.ts"; import type { FileConfig } from "./config.ts"; import { createPublicArtifactsProvider } from "./public-artifacts.ts"; @@ -273,7 +277,8 @@ const memoryPlugin = definePluginEntry({ if (runId) opts.run_id = runId; // Pass customInstructions and customCategories to control what Mem0 extracts if (cfg.customInstructions) opts.custom_instructions = cfg.customInstructions; - if (cfg.customCategories) opts.custom_categories = cfg.customCategories; + const customCategories = customCategoryMapToList(cfg.customCategories); + if (customCategories) opts.custom_categories = customCategories; return opts; } diff --git a/openclaw/providers.ts b/openclaw/providers.ts index 6911adedc7..b345aa5024 100644 --- a/openclaw/providers.ts +++ b/openclaw/providers.ts @@ -13,6 +13,16 @@ import type { AddResult, } from "./types.ts"; +export function customCategoryMapToList( + categories?: Record, +): Array> | undefined { + if (!categories || typeof categories !== "object") return undefined; + const items = Object.entries(categories).map(([name, description]) => ({ + [name]: description, + })); + return items.length ? items : undefined; +} + // ============================================================================ // Result Normalizers // ============================================================================ diff --git a/openclaw/tests/providers.test.ts b/openclaw/tests/providers.test.ts index 024bf3d91b..b48f5ad5b1 100644 --- a/openclaw/tests/providers.test.ts +++ b/openclaw/tests/providers.test.ts @@ -6,7 +6,11 @@ */ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { providerToBackend } from "../providers.ts"; +import { + createProvider, + customCategoryMapToList, + providerToBackend, +} from "../providers.ts"; // --------------------------------------------------------------------------- // Mock provider factory @@ -37,6 +41,77 @@ beforeEach(() => { vi.resetAllMocks(); }); +// --------------------------------------------------------------------------- +// customCategoryMapToList +// --------------------------------------------------------------------------- + +describe("customCategoryMapToList", () => { + it("converts category maps to the Mem0 SDK list shape", () => { + expect( + customCategoryMapToList({ + preference: "User preferences", + work: "Work context", + }), + ).toEqual([ + { preference: "User preferences" }, + { work: "Work context" }, + ]); + }); + + it("returns undefined for empty or missing category maps", () => { + expect(customCategoryMapToList()).toBeUndefined(); + expect(customCategoryMapToList({})).toBeUndefined(); + }); +}); + +// --------------------------------------------------------------------------- +// PlatformProvider +// --------------------------------------------------------------------------- + +describe("PlatformProvider", () => { + it("passes custom_categories as a list to the Mem0 SDK", async () => { + let addOptions: Record | undefined; + + vi.doMock("mem0ai", () => ({ + default: class MockMemoryClient { + async add(_messages: unknown, opts: Record) { + addOptions = opts; + return { results: [] }; + } + }, + })); + + const provider = createProvider( + { + mode: "platform", + apiKey: "test-key", + userId: "test-user", + autoCapture: true, + autoRecall: true, + customInstructions: "Store durable facts.", + customCategories: {}, + searchThreshold: 0.1, + topK: 5, + }, + { resolvePath: (p: string) => p } as any, + ); + + await provider.add([{ role: "user", content: "Remember this" }], { + user_id: "test-user", + custom_categories: customCategoryMapToList({ + preference: "User preferences", + }), + source: "OPENCLAW", + }); + + expect(addOptions?.customCategories).toEqual([ + { preference: "User preferences" }, + ]); + + vi.doUnmock("mem0ai"); + }); +}); + // --------------------------------------------------------------------------- // search // --------------------------------------------------------------------------- diff --git a/openclaw/types.ts b/openclaw/types.ts index 5c7b1ac888..29d5523966 100644 --- a/openclaw/types.ts +++ b/openclaw/types.ts @@ -36,7 +36,7 @@ export interface AddOptions { user_id: string; run_id?: string; custom_instructions?: string; - custom_categories?: Record; + custom_categories?: Array>; source?: string; // Agentic harness additions infer?: boolean;