Просмотр исходного кода

fix: isolate agent override clients by effective config

Ma 7 месяцев назад
Родитель
Сommit
0208fdf278

+ 66 - 0
packages/core/src/__tests__/pipeline-runner.test.ts

@@ -132,6 +132,72 @@ describe("PipelineRunner", () => {
     vi.restoreAllMocks();
   });
 
+  it("does not reuse override clients when credential sources differ", () => {
+    const previousKeyA = process.env.TEST_KEY_A;
+    const previousKeyB = process.env.TEST_KEY_B;
+    process.env.TEST_KEY_A = "key-a";
+    process.env.TEST_KEY_B = "key-b";
+
+    try {
+      const runner = new PipelineRunner({
+        client: {
+          provider: "openai",
+          apiFormat: "chat",
+          stream: false,
+          defaults: {
+            temperature: 0.7,
+            maxTokens: 4096,
+            thinkingBudget: 0,
+          },
+        } as ConstructorParameters<typeof PipelineRunner>[0]["client"],
+        model: "base-model",
+        projectRoot: process.cwd(),
+        defaultLLMConfig: {
+          provider: "custom",
+          baseUrl: "https://base.example/v1",
+          apiKey: "base-key",
+          model: "base-model",
+          temperature: 0.7,
+          maxTokens: 4096,
+          thinkingBudget: 0,
+          apiFormat: "chat",
+          stream: false,
+        },
+        modelOverrides: {
+          writer: {
+            model: "writer-model",
+            provider: "custom",
+            baseUrl: "https://shared.example/v1",
+            apiKeyEnv: "TEST_KEY_A",
+          },
+          auditor: {
+            model: "auditor-model",
+            provider: "custom",
+            baseUrl: "https://shared.example/v1",
+            apiKeyEnv: "TEST_KEY_B",
+          },
+        },
+      });
+
+      const resolveOverride = (
+        runner as unknown as {
+          resolveOverride: (agent: string) => { model: string; client: unknown };
+        }
+      ).resolveOverride.bind(runner);
+
+      const writerOverride = resolveOverride("writer");
+      const auditorOverride = resolveOverride("auditor");
+
+      expect(writerOverride.client).not.toBe(auditorOverride.client);
+    } finally {
+      if (previousKeyA === undefined) delete process.env.TEST_KEY_A;
+      else process.env.TEST_KEY_A = previousKeyA;
+
+      if (previousKeyB === undefined) delete process.env.TEST_KEY_B;
+      else process.env.TEST_KEY_B = previousKeyB;
+    }
+  });
+
   it("uses the latest revised content as the input for follow-up spot-fix revisions", async () => {
     const { root, runner, bookId } = await createRunnerFixture();
 

+ 17 - 5
packages/core/src/pipeline/runner.ts

@@ -135,23 +135,35 @@ export class PipelineRunner {
     if (!override.baseUrl) {
       return { model: override.model, client: this.config.client };
     }
-    const cacheKey = `${override.baseUrl}:${override.provider ?? "custom"}`;
+    const base = this.config.defaultLLMConfig;
+    const provider = override.provider ?? base?.provider ?? "custom";
+    const apiKeySource = override.apiKeyEnv
+      ? `env:${override.apiKeyEnv}`
+      : `base:${base?.apiKey ?? ""}`;
+    const stream = override.stream ?? base?.stream ?? true;
+    const apiFormat = base?.apiFormat ?? "chat";
+    const cacheKey = [
+      provider,
+      override.baseUrl,
+      apiKeySource,
+      `stream:${stream}`,
+      `format:${apiFormat}`,
+    ].join("|");
     let client = this.agentClients.get(cacheKey);
     if (!client) {
-      const base = this.config.defaultLLMConfig;
       const apiKey = override.apiKeyEnv
         ? process.env[override.apiKeyEnv] ?? ""
         : base?.apiKey ?? "";
       client = createLLMClient({
-        provider: override.provider ?? base?.provider ?? "custom",
+        provider,
         baseUrl: override.baseUrl,
         apiKey,
         model: override.model,
         temperature: base?.temperature ?? 0.7,
         maxTokens: base?.maxTokens ?? 8192,
         thinkingBudget: base?.thinkingBudget ?? 0,
-        apiFormat: base?.apiFormat ?? "chat",
-        stream: override.stream ?? base?.stream ?? true,
+        apiFormat,
+        stream,
       });
       this.agentClients.set(cacheKey, client);
     }