





















@@ -57,7 +57,11 @@ describe("Ollama provider", () => {
5757}
5858}
595960-async function runOllamaCatalog(params: { config?: OpenClawConfig; env?: NodeJS.ProcessEnv }) {
60+async function runOllamaCatalog(params: {
61+config?: OpenClawConfig;
62+env?: NodeJS.ProcessEnv;
63+resolveProviderApiKey?: () => { apiKey: string | undefined; discoveryApiKey?: string };
64+}) {
6165const env: NodeJS.ProcessEnv = {
6266 ...process.env,
6367VITEST: "1",
@@ -68,9 +72,11 @@ describe("Ollama provider", () => {
6872config: params.config ?? {},
6973agentDir: createAgentDir(),
7074 env,
71-resolveProviderApiKey: () => ({
72-apiKey: env.OLLAMA_API_KEY?.trim() ? env.OLLAMA_API_KEY : undefined,
73-}),
75+resolveProviderApiKey:
76+params.resolveProviderApiKey ??
77+(() => ({
78+apiKey: env.OLLAMA_API_KEY?.trim() ? env.OLLAMA_API_KEY : undefined,
79+})),
7480resolveProviderAuth: () => ({
7581apiKey: env.OLLAMA_API_KEY?.trim() ? env.OLLAMA_API_KEY : undefined,
7682mode: env.OLLAMA_API_KEY?.trim() ? "api_key" : "none",
@@ -480,6 +486,131 @@ describe("Ollama provider", () => {
480486});
481487});
482488489+it("uses resolved discovery api key when configured cloud apiKey is an env marker", async () => {
490+await withoutAmbientOllamaEnv(async () => {
491+const fetchMock = vi.fn();
492+vi.stubGlobal("fetch", withFetchPreconnect(fetchMock));
493+494+const provider = await runOllamaCatalog({
495+config: {
496+models: {
497+providers: {
498+ollama: {
499+baseUrl: "https://ollama.com/v1",
500+models: [
501+{
502+id: "gpt-oss:20b",
503+name: "GPT-OSS 20B",
504+reasoning: false,
505+input: ["text"],
506+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
507+contextWindow: 8192,
508+maxTokens: 81920,
509+},
510+],
511+apiKey: "OLLAMA_API_KEY",
512+},
513+},
514+},
515+},
516+env: { OLLAMA_API_KEY: "real-secret", VITEST: "", NODE_ENV: "development" },
517+resolveProviderApiKey: () => ({
518+apiKey: "OLLAMA_API_KEY",
519+discoveryApiKey: "real-secret",
520+}),
521+});
522+523+expect(fetchMock).not.toHaveBeenCalled();
524+expect(provider?.baseUrl).toBe("https://ollama.com");
525+expect(provider?.api).toBe("ollama");
526+expect(provider?.apiKey).toBe("real-secret");
527+expect(provider?.models).toHaveLength(1);
528+});
529+});
530+531+it("uses resolved discovery api key for configured cloud providers without apiKey", async () => {
532+await withoutAmbientOllamaEnv(async () => {
533+const fetchMock = vi.fn();
534+vi.stubGlobal("fetch", withFetchPreconnect(fetchMock));
535+536+const provider = await runOllamaCatalog({
537+config: {
538+models: {
539+providers: {
540+ollama: {
541+baseUrl: "https://ollama.com/v1",
542+models: [
543+{
544+id: "gpt-oss:20b",
545+name: "GPT-OSS 20B",
546+reasoning: false,
547+input: ["text"],
548+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
549+contextWindow: 8192,
550+maxTokens: 81920,
551+},
552+],
553+},
554+},
555+},
556+},
557+env: { OLLAMA_API_KEY: "real-secret", VITEST: "", NODE_ENV: "development" },
558+resolveProviderApiKey: () => ({
559+apiKey: "OLLAMA_API_KEY",
560+discoveryApiKey: "real-secret",
561+}),
562+});
563+564+expect(fetchMock).not.toHaveBeenCalled();
565+expect(provider?.baseUrl).toBe("https://ollama.com");
566+expect(provider?.api).toBe("ollama");
567+expect(provider?.apiKey).toBe("real-secret");
568+expect(provider?.models).toHaveLength(1);
569+});
570+});
571+572+it("keeps synthetic local auth when a local provider also has a discovery key", async () => {
573+await withoutAmbientOllamaEnv(async () => {
574+const fetchMock = vi.fn();
575+vi.stubGlobal("fetch", withFetchPreconnect(fetchMock));
576+577+const provider = await runOllamaCatalog({
578+config: {
579+models: {
580+providers: {
581+ollama: {
582+baseUrl: "http://127.0.0.1:11434/v1",
583+models: [
584+{
585+id: "gpt-oss:20b",
586+name: "GPT-OSS 20B",
587+reasoning: false,
588+input: ["text"],
589+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
590+contextWindow: 8192,
591+maxTokens: 81920,
592+},
593+],
594+apiKey: "OLLAMA_API_KEY",
595+},
596+},
597+},
598+},
599+env: { OLLAMA_API_KEY: "real-secret", VITEST: "", NODE_ENV: "development" },
600+resolveProviderApiKey: () => ({
601+apiKey: "OLLAMA_API_KEY",
602+discoveryApiKey: "real-secret",
603+}),
604+});
605+606+expect(fetchMock).not.toHaveBeenCalled();
607+expect(provider?.baseUrl).toBe("http://127.0.0.1:11434");
608+expect(provider?.api).toBe("ollama");
609+expect(provider?.apiKey).toBe(OLLAMA_LOCAL_AUTH_MARKER);
610+expect(provider?.models).toHaveLength(1);
611+});
612+});
613+483614it("should preserve explicit apiKey from configured remote providers", async () => {
484615await withoutAmbientOllamaEnv(async () => {
485616const fetchMock = vi.fn(async (input: unknown) => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。