From f8c6ccf5feab1402e8359cbe8a4f269e92d0ab1b Mon Sep 17 00:00:00 2001 From: kartik-mem0 Date: Mon, 6 Apr 2026 16:10:25 +0530 Subject: [PATCH] refactor: adjust CLI config commands, provider defaults, and README docs --- openclaw/README.md | 104 +++++++++++++++------------------------ openclaw/cli/commands.ts | 103 ++++++++++++++++++++++++++------------ openclaw/providers.ts | 32 +++++++++++- 3 files changed, 143 insertions(+), 96 deletions(-) diff --git a/openclaw/README.md b/openclaw/README.md index a815a624f..b2826d372 100644 --- a/openclaw/README.md +++ b/openclaw/README.md @@ -12,7 +12,7 @@ openclaw plugins install @mem0/openclaw-mem0 ### Platform (Mem0 Cloud) -Get an API key from [app.mem0.ai](https://app.mem0.ai): +Get an API key from [app.mem0.ai](https://app.mem0.ai/dashboard/api-keys): ```bash openclaw mem0 init --api-key --user-id @@ -32,7 +32,9 @@ Or configure manually in `openclaw.json`: ### Open-Source (Self-hosted) -No Mem0 key needed. Requires `OPENAI_API_KEY` for default embeddings and LLM. +No Mem0 key needed. Requires `OPENAI_API_KEY` for default embeddings and LLM. Vectors are stored locally in SQLite at `~/.mem0/vector_store.db` — no external database required. + +Defaults: `text-embedding-3-small` for embeddings, `gpt-5.4` for fact extraction. ```json5 "openclaw-mem0": { @@ -53,7 +55,7 @@ Customize the embedder, vector store, or LLM via the `oss` block: "oss": { "embedder": { "provider": "openai", "config": { "model": "text-embedding-3-small" } }, "vectorStore": { "provider": "qdrant", "config": { "host": "localhost", "port": 6333 } }, - "llm": { "provider": "openai", "config": { "model": "gpt-4o" } } + "llm": { "provider": "openai", "config": { "model": "gpt-5.4" } } } } ``` @@ -74,79 +76,57 @@ Both run silently. No prompting, no manual calls required. ### Memory Scopes -| Scope | Description | -|-------|-------------| -| **Session (short-term)** | Memories scoped to the current conversation via `run_id`. Automatically recalled alongside long-term memories. | -| **User (long-term)** | Persistent memories that span all sessions. Stored via `memory_add` with `longTerm: true` (the default). | - -During auto-recall, both scopes are searched and presented separately — long-term first, then session — so the agent has full context. +- **Session (short-term)** — Scoped to the current conversation via `run_id`. Recalled alongside long-term memories. +- **User (long-term)** — Persistent across all sessions. Default for `memory_add`. ### Multi-Agent Isolation -In multi-agent setups, each agent gets its own memory namespace automatically. Session keys matching `agent::` route memories to `userId:agent:`. Single-agent deployments are unaffected. - -All memory tools accept an optional `agentId` parameter for cross-agent queries: - -``` -memory_search({ query: "user's tech stack", agentId: "researcher" }) -``` +Each agent gets its own memory namespace automatically via session key routing (`agent::` maps to `userId:agent:`). Single-agent setups are unaffected. ## Agent Tools -Seven tools are available to the agent during conversations: +Eight tools are registered for agent use: | Tool | Description | -|------|-------------| -| **`memory_search`** | Search memories by natural language query. Supports `scope` (`session`, `long-term`, `all`) and `agentId` filtering. | -| **`memory_add`** | Save a fact to memory. Supports `category`, `importance`, `longTerm`, and `agentId`. | -| **`memory_get`** | Retrieve a specific memory by ID. | -| **`memory_list`** | List stored memories with optional `userId`, `agentId`, and `limit` filters. | -| **`memory_update`** | Update an existing memory's text in place. Preserves edit history. | -| **`memory_delete`** | Delete by ID, search query, or bulk (`all: true`). Requires `confirm: true` for bulk. | -| **`memory_history`** | View the edit history of a specific memory. | +| ---- | ----------- | +| `memory_search` | Search by natural language query. Supports `scope` (`session`, `long-term`, `all`), `categories`, `filters`, and `agentId`. | +| `memory_add` | Store facts. Accepts `text` or `facts` array, `category`, `importance`, `longTerm`, `metadata`. | +| `memory_get` | Retrieve a single memory by ID. | +| `memory_list` | List all memories. Filter by `userId`, `agentId`, `scope`. | +| `memory_update` | Update a memory's text in place. Preserves history. | +| `memory_delete` | Delete by `memoryId`, `query` (search-and-delete), or `all: true` (requires `confirm: true`). | +| `memory_event_list` | List recent background processing events. Platform mode only. | +| `memory_event_status` | Get status of a specific event by ID. Platform mode only. | ## CLI -All commands follow the pattern `openclaw mem0 `. - -### Memory Operations +All commands: `openclaw mem0 `. ```bash -# Add a memory +# Memory operations openclaw mem0 add "User prefers TypeScript over JavaScript" - -# Search memories openclaw mem0 search "what languages does the user know" openclaw mem0 search "preferences" --scope long-term -openclaw mem0 search "context" --scope session - -# Get, list, update, delete openclaw mem0 get openclaw mem0 list --user-id alice --top-k 20 openclaw mem0 update "Updated preference text" openclaw mem0 delete openclaw mem0 delete --all --user-id alice --confirm +openclaw mem0 import memories.json -# View edit history -openclaw mem0 history -``` - -### Management - -```bash -# Authenticate and configure +# Management openclaw mem0 init openclaw mem0 init --api-key --user-id alice - -# Check connectivity openclaw mem0 status - -# Manage configuration openclaw mem0 config show openclaw mem0 config get api_key openclaw mem0 config set user_id alice -# Memory consolidation (review, merge, prune) +# Events (platform only) +openclaw mem0 event list +openclaw mem0 event status + +# Memory consolidation openclaw mem0 dream openclaw mem0 dream --dry-run ``` @@ -156,9 +136,9 @@ openclaw mem0 dream --dry-run ### General | Key | Type | Default | Description | -|-----|------|---------|-------------| +| --- | ---- | ------- | ----------- | | `mode` | `"platform"` \| `"open-source"` | `"platform"` | Backend mode | -| `userId` | `string` | `"default"` | Unique identifier for the user. You define this — it's not found in any dashboard. All memories are scoped to this value. | +| `userId` | `string` | OS username | User identifier. All memories scoped to this value. | | `autoRecall` | `boolean` | `true` | Inject relevant memories before each turn | | `autoCapture` | `boolean` | `true` | Extract and store facts after each turn | | `topK` | `number` | `5` | Max memories returned per recall | @@ -167,32 +147,30 @@ openclaw mem0 dream --dry-run ### Platform Mode | Key | Type | Default | Description | -|-----|------|---------|-------------| +| --- | ---- | ------- | ----------- | | `apiKey` | `string` | — | **Required.** Mem0 API key (supports `${MEM0_API_KEY}`) | | `orgId` | `string` | — | Organization ID | | `projectId` | `string` | — | Project ID | -| `enableGraph` | `boolean` | `false` | Enable entity graph for relationship tracking | -| `customInstructions` | `string` | *(built-in)* | Custom extraction rules for what to store and how to format | -| `customCategories` | `object` | *(12 defaults)* | Category name to description map for memory tagging | +| `enableGraph` | `boolean` | `false` | Entity graph for relationship tracking | +| `customInstructions` | `string` | *(built-in)* | Custom extraction rules | +| `customCategories` | `object` | *(12 defaults)* | Category name to description map | ### Open-Source Mode -All fields below are optional. Defaults use OpenAI embeddings, in-memory vector store, and OpenAI LLM. +All fields optional. Defaults: `text-embedding-3-small` embeddings, local SQLite vector store (`~/.mem0/vector_store.db`), `gpt-5.4` LLM. | Key | Type | Default | Description | -|-----|------|---------|-------------| -| `customPrompt` | `string` | *(built-in)* | Extraction prompt for memory processing | +| --- | ---- | ------- | ----------- | +| `customPrompt` | `string` | *(built-in)* | Extraction prompt | | `oss.embedder.provider` | `string` | `"openai"` | Embedding provider | | `oss.embedder.config` | `object` | — | Provider config (`apiKey`, `model`, `baseURL`) | -| `oss.vectorStore.provider` | `string` | `"memory"` | Vector store provider | -| `oss.vectorStore.config` | `object` | — | Provider config (`host`, `port`, `collectionName`) | +| `oss.vectorStore.provider` | `string` | `"memory"` | Vector store provider (see list above) | +| `oss.vectorStore.config` | `object` | — | Provider config (`host`, `port`, `collectionName`, `dbPath`) | | `oss.llm.provider` | `string` | `"openai"` | LLM provider | | `oss.llm.config` | `object` | — | Provider config (`apiKey`, `model`, `baseURL`) | -| `oss.historyDbPath` | `string` | — | SQLite path for memory edit history | -| `oss.disableHistory` | `boolean` | `false` | Skip history DB initialization | - -Supported providers: `openai`, `anthropic`, `ollama`, `lmstudio`, `qdrant`, `chroma`, and more. See the [Mem0 OSS docs](https://docs.mem0.ai/open-source/node-quickstart) for the full list. +| `oss.historyDbPath` | `string` | — | SQLite path for edit history | +| `oss.disableHistory` | `boolean` | `false` | Skip history DB | ## License -Apache 2.0 +[Apache 2.0](LICENSE) diff --git a/openclaw/cli/commands.ts b/openclaw/cli/commands.ts index e7c62ac46..015279e3a 100644 --- a/openclaw/cli/commands.ts +++ b/openclaw/cli/commands.ts @@ -548,7 +548,7 @@ export function registerCliCommands( "\n Skipped. You can add it later via:", ); console.log( - " openclaw mem0 config set oss.embedder.config.apiKey ", + " openclaw mem0 config set embedder_key ", ); console.log( " Or set OPENAI_API_KEY in your environment.\n", @@ -949,6 +949,7 @@ export function registerCliCommands( const CONFIG_KEYS: Record = { // Short aliases (matches Python CLI) api_key: "apiKey", + email: "userEmail", base_url: "baseUrl", user_id: "userId", org_id: "orgId", @@ -958,29 +959,34 @@ export function registerCliCommands( auto_capture: "autoCapture", top_k: "topK", mode: "mode", - "platform.api_key": "apiKey", - "platform.email": "userEmail", - "defaults.user_id": "userId", - "defaults.org_id": "orgId", - "defaults.project_id": "projectId", - "defaults.enable_graph": "enableGraph", - "defaults.auto_recall": "autoRecall", - "defaults.auto_capture": "autoCapture", - "defaults.top_k": "topK", + embedder_provider: "oss.embedder.provider", + embedder_model: "oss.embedder.config.model", + embedder_key: "oss.embedder.config.apiKey", + llm_provider: "oss.llm.provider", + llm_model: "oss.llm.config.model", + llm_key: "oss.llm.config.apiKey", + vector_provider: "oss.vectorStore.provider", + vector_host: "oss.vectorStore.config.host", + vector_port: "oss.vectorStore.config.port", + collection_name: "oss.vectorStore.config.collectionName", + vector_db_path: "oss.vectorStore.config.dbPath", + history_db_path: "oss.historyDbPath", + disable_history: "oss.disableHistory", }; // Keys that contain secrets — redact in show/get output - const SECRET_KEYS = new Set(["apiKey"]); + const SECRET_KEYS = new Set(["apiKey", "oss.embedder.config.apiKey", "oss.llm.config.apiKey"]); // Boolean config fields — coerce "true"/"1"/"yes" on set const BOOLEAN_KEYS = new Set([ "enableGraph", "autoRecall", "autoCapture", + "oss.disableHistory", ]); // Integer config fields — coerce to number on set - const INTEGER_KEYS = new Set(["topK"]); + const INTEGER_KEYS = new Set(["topK", "oss.vectorStore.config.port"]); /** Resolve a user-facing key to the internal camelCase field name. */ function resolveConfigKey(key: string): string | null { @@ -989,6 +995,15 @@ export function registerCliCommands( /** Read a config value by internal field name. */ function getConfigValue(field: string): unknown { + if (field.startsWith("oss.")) { + const parts = field.split("."); + let current: unknown = cfg.oss; + for (let i = 1; i < parts.length && current != null; i++) { + current = (current as Record)[parts[i]]; + } + return current; + } + const auth = readPluginAuth(); const values: Record = { apiKey: auth.apiKey ?? cfg.apiKey, @@ -1027,20 +1042,37 @@ export function registerCliCommands( .command("show") .description("Show current configuration") .action(() => { - // Display order matching Python CLI: platform first, then defaults - const entries: Array<[string, string, string]> = [ - ["platform.api_key", "apiKey", ""], - ["platform.email", "userEmail", ""], - ["defaults.user_id", "userId", ""], - ["defaults.org_id", "orgId", ""], - ["defaults.project_id", "projectId", ""], - ["defaults.enable_graph", "enableGraph", ""], - ["defaults.auto_recall", "autoRecall", ""], - ["defaults.auto_capture", "autoCapture", ""], - ["defaults.top_k", "topK", ""], - ["mode", "mode", ""], + // Display order: general first, then mode-specific + const entries: Array<[string, string]> = [ + ["mode", "mode"], + ["user_id", "userId"], + ["auto_recall", "autoRecall"], + ["auto_capture", "autoCapture"], + ["top_k", "topK"], ]; + if (cfg.mode === "platform") { + entries.push( + ["api_key", "apiKey"], + ["email", "userEmail"], + ["org_id", "orgId"], + ["project_id", "projectId"], + ["enable_graph", "enableGraph"], + ); + } else { + entries.push( + ["embedder_provider", "oss.embedder.provider"], + ["embedder_model", "oss.embedder.config.model"], + ["embedder_key", "oss.embedder.config.apiKey"], + ["llm_provider", "oss.llm.provider"], + ["llm_model", "oss.llm.config.model"], + ["llm_key", "oss.llm.config.apiKey"], + ["vector_provider", "oss.vectorStore.provider"], + ["history_db_path", "oss.historyDbPath"], + ["disable_history", "oss.disableHistory"], + ); + } + // Calculate column widths const maxKeyLen = Math.max( ...entries.map(([k]) => k.length), @@ -1068,17 +1100,21 @@ export function registerCliCommands( console.log(" openclaw mem0 config set "); console.log(""); console.log(" Examples:"); - console.log(" openclaw mem0 config set mode open-source"); - console.log(" openclaw mem0 config set mode platform"); - console.log(" openclaw mem0 config set auto_recall false"); - console.log(" openclaw mem0 config set top_k 10"); + if (cfg.mode === "platform") { + console.log(" openclaw mem0 config set mode open-source"); + console.log(" openclaw mem0 config set auto_recall false"); + } else { + console.log(" openclaw mem0 config set vector_provider qdrant"); + console.log(" openclaw mem0 config set llm_model gpt-4o"); + console.log(" openclaw mem0 config set embedder_provider openai"); + } console.log(""); }); configCmd .command("get") .description("Get a config value") - .argument("", "Config key (e.g. user_id, platform.api_key)") + .argument("", "Config key (e.g. user_id, api_key, llm_model)") .action((key: string) => { const field = resolveConfigKey(key); if (!field) { @@ -1094,7 +1130,7 @@ export function registerCliCommands( configCmd .command("set") .description("Set a config value") - .argument("", "Config key (e.g. user_id, platform.api_key)") + .argument("", "Config key (e.g. user_id, api_key, llm_model)") .argument("", "New value") .action((key: string, rawValue: string) => { const field = resolveConfigKey(key); @@ -1121,7 +1157,12 @@ export function registerCliCommands( value = parsed; } - writePluginAuth({ [field]: value } as PluginAuthConfig); + // Nested OSS fields use dot-path writer; flat fields use auth writer + if (field.startsWith("oss.")) { + writePluginConfigField(field.split("."), value); + } else { + writePluginAuth({ [field]: value } as PluginAuthConfig); + } console.log( `${key} = ${displayValue(field, value)}`, ); diff --git a/openclaw/providers.ts b/openclaw/providers.ts index b33de702d..22a56226c 100644 --- a/openclaw/providers.ts +++ b/openclaw/providers.ts @@ -254,10 +254,38 @@ class OSSProvider implements Mem0Provider { const config: Record = { version: "v1.1" }; - if (this.ossConfig?.embedder) config.embedder = this.ossConfig.embedder; + const defaultEmbedder = { provider: "openai", config: { model: "text-embedding-3-small" } }; + const defaultLlm = { provider: "openai", config: { model: "gpt-5.4" } }; + + // Helper: strip empty-string values so they don't clobber defaults + const stripEmpty = (obj: Record) => { + const out = { ...obj }; + for (const k of Object.keys(out)) { if (out[k] === "") delete out[k]; } + return out; + }; + + if (this.ossConfig?.embedder) { + const ec = stripEmpty(this.ossConfig.embedder.config ?? {}); + config.embedder = { + provider: this.ossConfig.embedder.provider || defaultEmbedder.provider, + config: { ...defaultEmbedder.config, ...ec }, + }; + } else { + config.embedder = defaultEmbedder; + } + + if (this.ossConfig?.llm) { + const lc = stripEmpty(this.ossConfig.llm.config ?? {}); + config.llm = { + provider: this.ossConfig.llm.provider || defaultLlm.provider, + config: { ...defaultLlm.config, ...lc }, + }; + } else { + config.llm = defaultLlm; + } + if (this.ossConfig?.vectorStore) config.vectorStore = this.ossConfig.vectorStore; - if (this.ossConfig?.llm) config.llm = this.ossConfig.llm; if (this.ossConfig?.historyDbPath) { const dbPath = this.resolvePath