From 40425db0f169d810ddba59c2c12f4139477c9021 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:51:47 -0600 Subject: [PATCH] feat(memory): document Voyage embeddings + VOYAGE_API_KEY (#7078) (thanks @mcinteerj) (#10699) --- CHANGELOG.md | 1 + docs/concepts/memory.md | 6 ++++-- docs/reference/api-usage-costs.md | 3 ++- src/agents/model-auth.test.ts | 24 ++++++++++++++++++++++++ src/agents/model-auth.ts | 1 + 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83a5192d0..1f16b1f73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai ### Changes - Agents: bump pi-mono packages to 0.52.5. (#9949) Thanks @gumadeiras. +- Memory: add native Voyage embeddings provider (including batching) for vector memory search. (#7078) Thanks @mcinteerj. - Models: default Anthropic model to `anthropic/claude-opus-4-6`. (#9853) Thanks @TinyTb. - Models/Onboarding: refresh provider defaults, update OpenAI/OpenAI Codex wizard defaults, and harden model allowlist initialization for first-time configs with matching docs/tests. (#9911) Thanks @gumadeiras. - Telegram: auto-inject forum topic `threadId` in message tool and subagent announce so media, buttons, and subagent results land in the correct topic instead of General. (#7235) Thanks @Lukavyi. diff --git a/docs/concepts/memory.md b/docs/concepts/memory.md index ea5c08002..0358d60af 100644 --- a/docs/concepts/memory.md +++ b/docs/concepts/memory.md @@ -88,7 +88,8 @@ Defaults: 1. `local` if a `memorySearch.local.modelPath` is configured and the file exists. 2. `openai` if an OpenAI key can be resolved. 3. `gemini` if a Gemini key can be resolved. - 4. Otherwise memory search stays disabled until configured. + 4. `voyage` if a Voyage key can be resolved. + 5. Otherwise memory search stays disabled until configured. - Local mode uses node-llama-cpp and may require `pnpm approve-builds`. - Uses sqlite-vec (when available) to accelerate vector search inside SQLite. @@ -96,7 +97,8 @@ Remote embeddings **require** an API key for the embedding provider. OpenClaw resolves keys from auth profiles, `models.providers.*.apiKey`, or environment variables. Codex OAuth only covers chat/completions and does **not** satisfy embeddings for memory search. For Gemini, use `GEMINI_API_KEY` or -`models.providers.google.apiKey`. When using a custom OpenAI-compatible endpoint, +`models.providers.google.apiKey`. For Voyage, use `VOYAGE_API_KEY` or +`models.providers.voyage.apiKey`. When using a custom OpenAI-compatible endpoint, set `memorySearch.remote.apiKey` (and optional `memorySearch.remote.headers`). ### QMD backend (experimental) diff --git a/docs/reference/api-usage-costs.md b/docs/reference/api-usage-costs.md index 02d8200b0..5450b63a4 100644 --- a/docs/reference/api-usage-costs.md +++ b/docs/reference/api-usage-costs.md @@ -66,7 +66,8 @@ Semantic memory search uses **embedding APIs** when configured for remote provid - `memorySearch.provider = "openai"` → OpenAI embeddings - `memorySearch.provider = "gemini"` → Gemini embeddings -- Optional fallback to OpenAI if local embeddings fail +- `memorySearch.provider = "voyage"` → Voyage embeddings +- Optional fallback to a remote provider if local embeddings fail You can keep it local with `memorySearch.provider = "local"` (no API usage). diff --git a/src/agents/model-auth.test.ts b/src/agents/model-auth.test.ts index 7a0af0d18..f6d669aa8 100644 --- a/src/agents/model-auth.test.ts +++ b/src/agents/model-auth.test.ts @@ -463,4 +463,28 @@ describe("getApiKeyForModel", () => { } } }); + + it("accepts VOYAGE_API_KEY for voyage", async () => { + const previous = process.env.VOYAGE_API_KEY; + + try { + process.env.VOYAGE_API_KEY = "voyage-test-key"; + + vi.resetModules(); + const { resolveApiKeyForProvider } = await import("./model-auth.js"); + + const resolved = await resolveApiKeyForProvider({ + provider: "voyage", + store: { version: 1, profiles: {} }, + }); + expect(resolved.apiKey).toBe("voyage-test-key"); + expect(resolved.source).toContain("VOYAGE_API_KEY"); + } finally { + if (previous === undefined) { + delete process.env.VOYAGE_API_KEY; + } else { + process.env.VOYAGE_API_KEY = previous; + } + } + }); }); diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 4a84ce97a..324547c1d 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -287,6 +287,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null { const envMap: Record = { openai: "OPENAI_API_KEY", google: "GEMINI_API_KEY", + voyage: "VOYAGE_API_KEY", groq: "GROQ_API_KEY", deepgram: "DEEPGRAM_API_KEY", cerebras: "CEREBRAS_API_KEY",