From 55a18bc5982925032d01fc8c16a771e2d3e2c5ca Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sun, 15 Mar 2026 12:47:22 +0800 Subject: [PATCH 1/2] fix: doctor false API key warning for ollama memory search provider (#46584) --- src/commands/doctor-memory-search.test.ts | 51 +++++++++++++++++++++++ src/commands/doctor-memory-search.ts | 22 ++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/commands/doctor-memory-search.test.ts b/src/commands/doctor-memory-search.test.ts index 0c01c1c7688..45fe8b125d1 100644 --- a/src/commands/doctor-memory-search.test.ts +++ b/src/commands/doctor-memory-search.test.ts @@ -290,6 +290,57 @@ describe("noteMemorySearchHealth", () => { const providersChecked = providerCalls.map(([arg]) => arg.provider); expect(providersChecked).toEqual(["openai", "google", "voyage", "mistral"]); }); + + it("does not warn when ollama provider is set and gateway probe is ready", async () => { + resolveMemorySearchConfig.mockReturnValue({ + provider: "ollama", + local: {}, + remote: {}, + }); + + await noteMemorySearchHealth(cfg, { + gatewayMemoryProbe: { checked: true, ready: true }, + }); + + expect(note).not.toHaveBeenCalled(); + }); + + it("shows informational note when ollama provider is set and gateway probe is not ready", async () => { + resolveMemorySearchConfig.mockReturnValue({ + provider: "ollama", + local: {}, + remote: {}, + }); + + await noteMemorySearchHealth(cfg, { + gatewayMemoryProbe: { checked: true, ready: false, error: "connection refused" }, + }); + + expect(note).toHaveBeenCalledTimes(1); + const message = String(note.mock.calls[0]?.[0] ?? ""); + expect(message).toContain("ollama"); + expect(message).toContain("does not require an API key"); + expect(message).toContain("connection refused"); + expect(message).not.toContain("API key was not found"); + expect(note.mock.calls[0]?.[1]).toBe("Memory search"); + }); + + it("shows informational note when ollama provider is set and no gateway probe", async () => { + resolveMemorySearchConfig.mockReturnValue({ + provider: "ollama", + local: {}, + remote: {}, + }); + + await noteMemorySearchHealth(cfg); + + expect(note).toHaveBeenCalledTimes(1); + const message = String(note.mock.calls[0]?.[0] ?? ""); + expect(message).toContain("ollama"); + expect(message).toContain("does not require an API key"); + expect(message).not.toContain("API key was not found"); + expect(note.mock.calls[0]?.[1]).toBe("Memory search"); + }); }); describe("detectLegacyWorkspaceDirs", () => { diff --git a/src/commands/doctor-memory-search.ts b/src/commands/doctor-memory-search.ts index 4dd2914613f..d5f62adf4cc 100644 --- a/src/commands/doctor-memory-search.ts +++ b/src/commands/doctor-memory-search.ts @@ -79,6 +79,28 @@ export async function noteMemorySearchHealth( ); return; } + if (resolved.provider === "ollama") { + // Ollama runs locally and does not require an API key. + // If a gateway probe confirmed embeddings are ready, all good. + if (opts?.gatewayMemoryProbe?.checked && opts.gatewayMemoryProbe.ready) { + return; + } + // No probe or probe not ready — nudge the user to verify the service. + const detail = opts?.gatewayMemoryProbe?.error?.trim(); + note( + [ + 'Memory search provider is set to "ollama".', + "Ollama does not require an API key, but the ollama service must be running.", + detail ? `Gateway probe: ${detail}` : null, + "", + `Verify: ${formatCliCommand("openclaw memory status --deep")}`, + ] + .filter(Boolean) + .join("\n"), + "Memory search", + ); + return; + } // Remote provider — check for API key if (hasRemoteApiKey || (await hasApiKeyForProvider(resolved.provider, cfg, agentDir))) { return; From 960b567f53dce04b12d190ec10297083be333d1e Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sun, 15 Mar 2026 12:54:49 +0800 Subject: [PATCH 2/2] fix: doctor false API key warning for ollama memory search provider (#46584) --- src/commands/doctor-memory-search.test.ts | 11 ++------ src/commands/doctor-memory-search.ts | 34 +++++++++++------------ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/commands/doctor-memory-search.test.ts b/src/commands/doctor-memory-search.test.ts index 45fe8b125d1..7020c3664ef 100644 --- a/src/commands/doctor-memory-search.test.ts +++ b/src/commands/doctor-memory-search.test.ts @@ -320,12 +320,12 @@ describe("noteMemorySearchHealth", () => { const message = String(note.mock.calls[0]?.[0] ?? ""); expect(message).toContain("ollama"); expect(message).toContain("does not require an API key"); - expect(message).toContain("connection refused"); + expect(message).toContain("Gateway probe: connection refused"); expect(message).not.toContain("API key was not found"); expect(note.mock.calls[0]?.[1]).toBe("Memory search"); }); - it("shows informational note when ollama provider is set and no gateway probe", async () => { + it("does not warn when ollama provider is set and no gateway probe is available", async () => { resolveMemorySearchConfig.mockReturnValue({ provider: "ollama", local: {}, @@ -334,12 +334,7 @@ describe("noteMemorySearchHealth", () => { await noteMemorySearchHealth(cfg); - expect(note).toHaveBeenCalledTimes(1); - const message = String(note.mock.calls[0]?.[0] ?? ""); - expect(message).toContain("ollama"); - expect(message).toContain("does not require an API key"); - expect(message).not.toContain("API key was not found"); - expect(note.mock.calls[0]?.[1]).toBe("Memory search"); + expect(note).not.toHaveBeenCalled(); }); }); diff --git a/src/commands/doctor-memory-search.ts b/src/commands/doctor-memory-search.ts index d5f62adf4cc..f8608476d07 100644 --- a/src/commands/doctor-memory-search.ts +++ b/src/commands/doctor-memory-search.ts @@ -81,24 +81,24 @@ export async function noteMemorySearchHealth( } if (resolved.provider === "ollama") { // Ollama runs locally and does not require an API key. - // If a gateway probe confirmed embeddings are ready, all good. - if (opts?.gatewayMemoryProbe?.checked && opts.gatewayMemoryProbe.ready) { - return; + // Only warn when the gateway probe explicitly reports not-ready; + // if no probe ran we cannot tell whether the service is up, so + // stay silent (consistent with the "local" branch above). + if (opts?.gatewayMemoryProbe?.checked && !opts.gatewayMemoryProbe.ready) { + const detail = opts.gatewayMemoryProbe.error?.trim(); + note( + [ + 'Memory search provider is set to "ollama".', + "Ollama does not require an API key, but the ollama service must be running.", + detail ? `Gateway probe: ${detail}` : null, + "", + `Verify: ${formatCliCommand("openclaw memory status --deep")}`, + ] + .filter(Boolean) + .join("\n"), + "Memory search", + ); } - // No probe or probe not ready — nudge the user to verify the service. - const detail = opts?.gatewayMemoryProbe?.error?.trim(); - note( - [ - 'Memory search provider is set to "ollama".', - "Ollama does not require an API key, but the ollama service must be running.", - detail ? `Gateway probe: ${detail}` : null, - "", - `Verify: ${formatCliCommand("openclaw memory status --deep")}`, - ] - .filter(Boolean) - .join("\n"), - "Memory search", - ); return; } // Remote provider — check for API key