fix(ai): honor scoped env in compat API key injection
This commit is contained in:
@@ -100,7 +100,7 @@ function withEnvApiKey<TOptions extends StreamOptions>(
|
|||||||
options: TOptions | undefined,
|
options: TOptions | undefined,
|
||||||
): TOptions | undefined {
|
): TOptions | undefined {
|
||||||
if (hasExplicitApiKey(options?.apiKey)) return options;
|
if (hasExplicitApiKey(options?.apiKey)) return options;
|
||||||
const apiKey = getEnvApiKey(model.provider);
|
const apiKey = getEnvApiKey(model.provider, options?.env);
|
||||||
if (!apiKey) return options;
|
if (!apiKey) return options;
|
||||||
return { ...options, apiKey } as TOptions;
|
return { ...options, apiKey } as TOptions;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
import { complete, registerApiProvider, resetApiProviders } from "../src/compat.ts";
|
||||||
|
import type { AssistantMessage, Context, Model } from "../src/types.ts";
|
||||||
|
import { AssistantMessageEventStream } from "../src/utils/event-stream.ts";
|
||||||
|
|
||||||
|
const context: Context = { messages: [{ role: "user", content: "hi", timestamp: Date.now() }] };
|
||||||
|
|
||||||
|
const model: Model<"openai-responses"> = {
|
||||||
|
id: "test-model",
|
||||||
|
name: "Test Model",
|
||||||
|
api: "openai-responses",
|
||||||
|
provider: "openai",
|
||||||
|
baseUrl: "https://example.test/v1",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||||
|
contextWindow: 128000,
|
||||||
|
maxTokens: 4096,
|
||||||
|
};
|
||||||
|
|
||||||
|
function message(): AssistantMessage {
|
||||||
|
return {
|
||||||
|
role: "assistant",
|
||||||
|
content: [{ type: "text", text: "ok" }],
|
||||||
|
api: model.api,
|
||||||
|
provider: model.provider,
|
||||||
|
model: model.id,
|
||||||
|
usage: {
|
||||||
|
input: 0,
|
||||||
|
output: 0,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
totalTokens: 0,
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||||
|
},
|
||||||
|
stopReason: "stop",
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("compat env API key injection", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
resetApiProviders();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses request-scoped env when injecting provider API keys", async () => {
|
||||||
|
let capturedApiKey: string | undefined;
|
||||||
|
registerApiProvider({
|
||||||
|
api: "openai-responses",
|
||||||
|
stream: (_model, _context, options) => {
|
||||||
|
capturedApiKey = options?.apiKey;
|
||||||
|
const stream = new AssistantMessageEventStream();
|
||||||
|
const output = message();
|
||||||
|
stream.push({ type: "start", partial: output });
|
||||||
|
stream.push({ type: "done", reason: "stop", message: output });
|
||||||
|
stream.end(output);
|
||||||
|
return stream;
|
||||||
|
},
|
||||||
|
streamSimple: (_model, _context, options) => {
|
||||||
|
capturedApiKey = options?.apiKey;
|
||||||
|
const stream = new AssistantMessageEventStream();
|
||||||
|
const output = message();
|
||||||
|
stream.push({ type: "start", partial: output });
|
||||||
|
stream.push({ type: "done", reason: "stop", message: output });
|
||||||
|
stream.end(output);
|
||||||
|
return stream;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await complete(model, context, { env: { OPENAI_API_KEY: "scoped-key" } });
|
||||||
|
|
||||||
|
expect(capturedApiKey).toBe("scoped-key");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user