diff --git a/src/resources/extensions/ollama/index.ts b/src/resources/extensions/ollama/index.ts index 550771232..22102c231 100644 --- a/src/resources/extensions/ollama/index.ts +++ b/src/resources/extensions/ollama/index.ts @@ -61,8 +61,13 @@ async function probeAndRegister(pi: ExtensionAPI): Promise { const baseUrl = client.getOllamaHost(); + // Use authMode "apiKey" with a dummy key (#3440). + // authMode "none" requires a custom streamSimple handler, but Ollama uses + // the standard OpenAI-compatible streaming endpoint. Ollama ignores the + // Authorization header so the dummy key is harmless. pi.registerProvider("ollama", { - authMode: "none", + authMode: "apiKey", + apiKey: "ollama", baseUrl, api: "ollama-chat", streamSimple: streamOllamaChat, diff --git a/src/resources/extensions/ollama/ollama-auth-mode.test.ts b/src/resources/extensions/ollama/ollama-auth-mode.test.ts new file mode 100644 index 000000000..e74f2e76c --- /dev/null +++ b/src/resources/extensions/ollama/ollama-auth-mode.test.ts @@ -0,0 +1,20 @@ +/** + * Regression test for #3440: Ollama extension must register with + * authMode "apiKey" (not "none") to avoid streamSimple requirement. + */ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +test("Ollama registers with authMode apiKey, not none (#3440)", () => { + const src = readFileSync(join(__dirname, "index.ts"), "utf-8"); + // Find the registerProvider call + const registerBlock = src.slice(src.indexOf("pi.registerProvider(\"ollama\"")); + const authLine = registerBlock.match(/authMode:\s*"(\w+)"/); + assert.ok(authLine, "registerProvider must specify authMode"); + assert.equal(authLine![1], "apiKey", "authMode must be apiKey, not none"); +});