diff --git a/src/agents/model-auth.profiles.test.ts b/src/agents/model-auth.profiles.test.ts index 6ce255a6498..a828501ec3f 100644 --- a/src/agents/model-auth.profiles.test.ts +++ b/src/agents/model-auth.profiles.test.ts @@ -131,6 +131,53 @@ async function expectBedrockAuthSource(params: { }); } +function buildOllamaStore(keys: string[]) { + return { + version: 1 as const, + profiles: Object.fromEntries( + keys.map((key, index) => [ + index === 0 ? "ollama:default" : `ollama:${index + 1}`, + { + type: "api_key" as const, + provider: "ollama" as const, + key, + }, + ]), + ), + }; +} + +function buildOllamaProviderCfg(apiKey: string) { + return { + models: { + providers: { + ollama: { + baseUrl: "https://ollama.com", + api: "ollama", + apiKey, + models: [], + }, + }, + }, + }; +} + +async function resolveOllamaApiKey(params: { + envApiKey: string | undefined; + storedKeys: string[]; + configuredApiKey: string; +}) { + let resolved!: Awaited>; + await withEnvAsync({ OLLAMA_API_KEY: params.envApiKey }, async () => { + resolved = await resolveApiKeyForProvider({ + provider: "ollama", + store: buildOllamaStore(params.storedKeys), + cfg: buildOllamaProviderCfg(params.configuredApiKey), + }); + }); + return resolved; +} + describe("getApiKeyForModel", () => { it("reads oauth auth-profiles entries from auth-profiles.json via explicit profile", async () => { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-oauth-")); @@ -463,173 +510,58 @@ describe("getApiKeyForModel", () => { }); it("prefers explicit OLLAMA_API_KEY over the stored ollama-local profile", async () => { - await withEnvAsync({ OLLAMA_API_KEY: "env-ollama-key" }, async () => { - const resolved = await resolveApiKeyForProvider({ - provider: "ollama", - store: { - version: 1, - profiles: { - "ollama:default": { - type: "api_key", - provider: "ollama", - key: "ollama-local", - }, - }, - }, - cfg: { - models: { - providers: { - ollama: { - baseUrl: "https://ollama.com", - api: "ollama", - apiKey: "OLLAMA_API_KEY", - models: [], - }, - }, - }, - }, - }); - expect(resolved.apiKey).toBe("env-ollama-key"); - expect(resolved.source).toContain("OLLAMA_API_KEY"); - expect(resolved.profileId).toBeUndefined(); + const resolved = await resolveOllamaApiKey({ + envApiKey: "env-ollama-key", + storedKeys: ["ollama-local"], + configuredApiKey: "OLLAMA_API_KEY", }); + expect(resolved.apiKey).toBe("env-ollama-key"); + expect(resolved.source).toContain("OLLAMA_API_KEY"); + expect(resolved.profileId).toBeUndefined(); }); it("prefers explicit configured ollama apiKey over the stored ollama-local profile", async () => { - await withEnvAsync({ OLLAMA_API_KEY: undefined }, async () => { - const resolved = await resolveApiKeyForProvider({ - provider: "ollama", - store: { - version: 1, - profiles: { - "ollama:default": { - type: "api_key", - provider: "ollama", - key: "ollama-local", - }, - }, - }, - cfg: { - models: { - providers: { - ollama: { - baseUrl: "https://ollama.com", - api: "ollama", - apiKey: "config-ollama-key", - models: [], - }, - }, - }, - }, - }); - expect(resolved.apiKey).toBe("config-ollama-key"); - expect(resolved.source).toBe("models.json"); - expect(resolved.profileId).toBeUndefined(); + const resolved = await resolveOllamaApiKey({ + envApiKey: undefined, + storedKeys: ["ollama-local"], + configuredApiKey: "config-ollama-key", }); + expect(resolved.apiKey).toBe("config-ollama-key"); + expect(resolved.source).toBe("models.json"); + expect(resolved.profileId).toBeUndefined(); }); it("falls back to the stored ollama-local profile when no real ollama auth exists", async () => { - await withEnvAsync({ OLLAMA_API_KEY: undefined }, async () => { - const resolved = await resolveApiKeyForProvider({ - provider: "ollama", - store: { - version: 1, - profiles: { - "ollama:default": { - type: "api_key", - provider: "ollama", - key: "ollama-local", - }, - }, - }, - cfg: { - models: { - providers: { - ollama: { - baseUrl: "https://ollama.com", - api: "ollama", - apiKey: "OLLAMA_API_KEY", - models: [], - }, - }, - }, - }, - }); - expect(resolved.apiKey).toBe("ollama-local"); - expect(resolved.source).toBe("profile:ollama:default"); - expect(resolved.profileId).toBe("ollama:default"); + const resolved = await resolveOllamaApiKey({ + envApiKey: undefined, + storedKeys: ["ollama-local"], + configuredApiKey: "OLLAMA_API_KEY", }); + expect(resolved.apiKey).toBe("ollama-local"); + expect(resolved.source).toBe("profile:ollama:default"); + expect(resolved.profileId).toBe("ollama:default"); }); it("keeps a real stored ollama profile ahead of env auth", async () => { - await withEnvAsync({ OLLAMA_API_KEY: "env-ollama-key" }, async () => { - const resolved = await resolveApiKeyForProvider({ - provider: "ollama", - store: { - version: 1, - profiles: { - "ollama:default": { - type: "api_key", - provider: "ollama", - key: "stored-ollama-key", - }, - }, - }, - cfg: { - models: { - providers: { - ollama: { - baseUrl: "https://ollama.com", - api: "ollama", - apiKey: "OLLAMA_API_KEY", - models: [], - }, - }, - }, - }, - }); - expect(resolved.apiKey).toBe("stored-ollama-key"); - expect(resolved.source).toBe("profile:ollama:default"); - expect(resolved.profileId).toBe("ollama:default"); + const resolved = await resolveOllamaApiKey({ + envApiKey: "env-ollama-key", + storedKeys: ["stored-ollama-key"], + configuredApiKey: "OLLAMA_API_KEY", }); + expect(resolved.apiKey).toBe("stored-ollama-key"); + expect(resolved.source).toBe("profile:ollama:default"); + expect(resolved.profileId).toBe("ollama:default"); }); it("defers every stored ollama-local profile until real auth sources are checked", async () => { - await withEnvAsync({ OLLAMA_API_KEY: "env-ollama-key" }, async () => { - const resolved = await resolveApiKeyForProvider({ - provider: "ollama", - store: { - version: 1, - profiles: { - "ollama:default": { - type: "api_key", - provider: "ollama", - key: "ollama-local", - }, - "ollama:secondary": { - type: "api_key", - provider: "ollama", - key: "ollama-local", - }, - }, - }, - cfg: { - models: { - providers: { - ollama: { - baseUrl: "https://ollama.com", - api: "ollama", - apiKey: "OLLAMA_API_KEY", - models: [], - }, - }, - }, - }, - }); - expect(resolved.apiKey).toBe("env-ollama-key"); - expect(resolved.source).toContain("OLLAMA_API_KEY"); - expect(resolved.profileId).toBeUndefined(); + const resolved = await resolveOllamaApiKey({ + envApiKey: "env-ollama-key", + storedKeys: ["ollama-local", "ollama-local"], + configuredApiKey: "OLLAMA_API_KEY", }); + expect(resolved.apiKey).toBe("env-ollama-key"); + expect(resolved.source).toContain("OLLAMA_API_KEY"); + expect(resolved.profileId).toBeUndefined(); }); it("defers plugin-owned synthetic profile markers without core provider branching", async () => {