diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index dd72d72fe9..cc7cbfa0eb 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -174,6 +174,7 @@ export const SECRET_STATE_KEYS = [ "awsSecretKey", "awsSessionToken", "openAiApiKey", + "ollamaApiKey", "geminiApiKey", "openAiNativeApiKey", "cerebrasApiKey", diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 4f5ca9c3ca..981d61552a 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -186,6 +186,7 @@ const openAiSchema = baseProviderSettingsSchema.extend({ const ollamaSchema = baseProviderSettingsSchema.extend({ ollamaModelId: z.string().optional(), ollamaBaseUrl: z.string().optional(), + ollamaApiKey: z.string().optional(), }) const vsCodeLmSchema = baseProviderSettingsSchema.extend({ diff --git a/src/api/providers/__tests__/ollama.spec.ts b/src/api/providers/__tests__/ollama.spec.ts index fa98a56e8d..bbd43d3b45 100644 --- a/src/api/providers/__tests__/ollama.spec.ts +++ b/src/api/providers/__tests__/ollama.spec.ts @@ -92,6 +92,17 @@ describe("OllamaHandler", () => { }) expect(handlerWithoutUrl).toBeInstanceOf(OllamaHandler) }) + + it("should use API key when provided", () => { + const handlerWithApiKey = new OllamaHandler({ + apiModelId: "llama2", + ollamaModelId: "llama2", + ollamaBaseUrl: "https://ollama.com", + ollamaApiKey: "test-api-key", + }) + expect(handlerWithApiKey).toBeInstanceOf(OllamaHandler) + // The API key will be used in the Authorization header + }) }) describe("createMessage", () => { diff --git a/src/api/providers/native-ollama.ts b/src/api/providers/native-ollama.ts index 8ab4ebe2e1..06c1c33d23 100644 --- a/src/api/providers/native-ollama.ts +++ b/src/api/providers/native-ollama.ts @@ -1,5 +1,5 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { Message, Ollama } from "ollama" +import { Message, Ollama, type Config as OllamaOptions } from "ollama" import { ModelInfo, openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types" import { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" @@ -140,10 +140,19 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio private ensureClient(): Ollama { if (!this.client) { try { - this.client = new Ollama({ + const clientOptions: OllamaOptions = { host: this.options.ollamaBaseUrl || "http://localhost:11434", // Note: The ollama npm package handles timeouts internally - }) + } + + // Add API key if provided (for Ollama cloud or authenticated instances) + if (this.options.ollamaApiKey) { + clientOptions.headers = { + Authorization: `Bearer ${this.options.ollamaApiKey}`, + } + } + + this.client = new Ollama(clientOptions) } catch (error: any) { throw new Error(`Error creating Ollama client: ${error.message}`) } diff --git a/src/api/providers/ollama.ts b/src/api/providers/ollama.ts index 54666be58d..75895908e9 100644 --- a/src/api/providers/ollama.ts +++ b/src/api/providers/ollama.ts @@ -25,10 +25,20 @@ export class OllamaHandler extends BaseProvider implements SingleCompletionHandl super() this.options = options + // Use the API key if provided (for Ollama cloud or authenticated instances) + // Otherwise use "ollama" as a placeholder for local instances + const apiKey = this.options.ollamaApiKey || "ollama" + + const headers: Record = {} + if (this.options.ollamaApiKey) { + headers["Authorization"] = `Bearer ${this.options.ollamaApiKey}` + } + this.client = new OpenAI({ baseURL: (this.options.ollamaBaseUrl || "http://localhost:11434") + "/v1", - apiKey: "ollama", + apiKey: apiKey, timeout: getApiRequestTimeout(), + defaultHeaders: headers, }) } diff --git a/webview-ui/src/components/settings/providers/Ollama.tsx b/webview-ui/src/components/settings/providers/Ollama.tsx index 263c3892f2..b09ecad5d6 100644 --- a/webview-ui/src/components/settings/providers/Ollama.tsx +++ b/webview-ui/src/components/settings/providers/Ollama.tsx @@ -86,6 +86,19 @@ export const Ollama = ({ apiConfiguration, setApiConfigurationField }: OllamaPro className="w-full"> + {apiConfiguration?.ollamaBaseUrl && ( + + +
+ {t("settings:providers.ollama.apiKeyHelp")} +
+
+ )}