From 0cf86e3540bdeb4c06b628177ce51b4ea05506ab Mon Sep 17 00:00:00 2001 From: User Date: Tue, 14 Jul 2026 18:44:12 +0200 Subject: [PATCH] fix(backend): allow configured local Qwen provider --- backend/src/pi/provider-credentials.ts | 4 ++- backend/test/pi-process-manager.test.ts | 43 ++++++++++++----------- backend/test/provider-credentials.test.ts | 14 ++++++++ 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/backend/src/pi/provider-credentials.ts b/backend/src/pi/provider-credentials.ts index 525f82ae..ad57883f 100644 --- a/backend/src/pi/provider-credentials.ts +++ b/backend/src/pi/provider-credentials.ts @@ -42,7 +42,9 @@ const PROVIDER_KEY_ENV: Readonly> = { const COMPOUND_PROVIDERS = new Set([ "amazon-bedrock", "azure-openai-responses", "cloudflare-ai-gateway", "cloudflare-workers-ai", ]); -const LOCAL_PROVIDERS = new Set(["ollama", "lmstudio", "local", "aritmolab", "faux"]); +const LOCAL_PROVIDERS = new Set([ + "ollama", "lmstudio", "local", "aritmolab", "local-qwen", "faux", +]); export function canonicalPiProvider(provider: string | undefined): string | undefined { const value = provider?.trim().toLowerCase(); diff --git a/backend/test/pi-process-manager.test.ts b/backend/test/pi-process-manager.test.ts index fc220242..5ffb179a 100644 --- a/backend/test/pi-process-manager.test.ts +++ b/backend/test/pi-process-manager.test.ts @@ -289,26 +289,29 @@ test.each([["OpenAI", "openai"], ["gemini", "google"]])( }, ); -test("local providers spawn without a model key and scrub ambient generic credentials", async () => { - vi.stubEnv("PI_PROVIDER_API_KEY", "ambient-secret"); - vi.stubEnv("THT_MODEL_API_KEY_FILE", "/ambient/secret-path"); - vi.stubEnv("OPENAI_API_KEY", "unselected-provider-secret"); - const calls: any[][] = []; - const child = recordingChild(); - child.stderr.resume = () => {}; - const mgr = new PiProcessManager(loadConfig({ PI_BIN: "/usr/local/bin/pi" }), { - spawnFn: (...args: any[]) => { calls.push(args); return child as any; }, - }); - try { - await mgr.spawnFor("local-session", { provider: "ollama" }); - expect(calls[0][2].env).not.toHaveProperty("PI_PROVIDER_API_KEY"); - expect(calls[0][2].env).not.toHaveProperty("THT_MODEL_API_KEY_FILE"); - expect(calls[0][2].env).not.toHaveProperty("OPENAI_API_KEY"); - } finally { - mgr.teardown("local-session"); - vi.unstubAllEnvs(); - } -}); +test.each(["ollama", "local-qwen"])( + "local provider %s spawns without a model key and scrubs ambient credentials", + async (provider) => { + vi.stubEnv("PI_PROVIDER_API_KEY", "ambient-secret"); + vi.stubEnv("THT_MODEL_API_KEY_FILE", "/ambient/secret-path"); + vi.stubEnv("OPENAI_API_KEY", "unselected-provider-secret"); + const calls: any[][] = []; + const child = recordingChild(); + child.stderr.resume = () => {}; + const mgr = new PiProcessManager(loadConfig({ PI_BIN: "/usr/local/bin/pi" }), { + spawnFn: (...args: any[]) => { calls.push(args); return child as any; }, + }); + try { + await mgr.spawnFor(`local-session-${provider}`, { provider }); + expect(calls[0][2].env).not.toHaveProperty("PI_PROVIDER_API_KEY"); + expect(calls[0][2].env).not.toHaveProperty("THT_MODEL_API_KEY_FILE"); + expect(calls[0][2].env).not.toHaveProperty("OPENAI_API_KEY"); + } finally { + mgr.teardown(`local-session-${provider}`); + vi.unstubAllEnvs(); + } + }, +); test.each(["amazon-bedrock", "azure-openai-responses", "cloudflare-workers-ai", "cloudflare-ai-gateway"])( "session spawn rejects compound provider %s before spawning Pi", async (provider) => { diff --git a/backend/test/provider-credentials.test.ts b/backend/test/provider-credentials.test.ts index e8352845..ff7414ae 100644 --- a/backend/test/provider-credentials.test.ts +++ b/backend/test/provider-credentials.test.ts @@ -116,6 +116,20 @@ test("single-key providers scrub ambient compound companions before injecting th expect(env).not.toHaveProperty("CLOUDFLARE_GATEWAY_ID"); }); +test("local-qwen is an explicit local provider and needs no generic key", () => { + const env = buildPiChildEnv({ + ambient: { + PI_PROVIDER_API_KEY: "must-not-leak", + OPENAI_API_KEY: "must-not-leak", + THT_MODEL_API_KEY_FILE: "/must/not/leak", + }, + provider: "local-qwen", + }); + expect(env).not.toHaveProperty("PI_PROVIDER_API_KEY"); + expect(env).not.toHaveProperty("OPENAI_API_KEY"); + expect(env).not.toHaveProperty("THT_MODEL_API_KEY_FILE"); +}); + test("bundle value is injected without exposing bundle metadata to Pi", () => { const env = buildPiChildEnv({ ambient: {