pi-mono/packages/coding-agent/test/model-runtime-cloudflare-compat.test.ts
David Brailovsky ab30693d64
fix(ai): upgrade openai SDK to 7.19.0 (#10044)
* fix(ai): upgrade openai SDK to 7.19.0

Adds the "fast" service tier to the SDK types, needed to price
GPT-6 Fast mode requests correctly. Drops the local
prompt_cache_options type, which the SDK now defines.

* fix(coding-agent): check Cloudflare compat request via fetch instead of SDK mock

pi-ai now resolves its own nested openai 7.x while evals keeps 6.x at the
root, so vi.mock("openai") in coding-agent no longer reached pi-ai's client.
Capture the outgoing request with a fake fetch and assert the URL and headers.

* fix(ai): drop any casts for stream_options and max_tokens in openai-completions

stream_options is typed by the SDK, so assign it directly. max_tokens is
deprecated by OpenAI but still needed for OpenAI-compatible providers
that reject max_completion_tokens; use a narrow cast instead of any to
avoid the deprecation warning.
2026-09-25 17:27:12 +02:00

90 lines
3.5 KiB
TypeScript

import { complete, resetApiProviders } from "@earendil-works/pi-ai/compat";
import { describe, expect, it } from "vitest";
import { AuthStorage } from "../src/core/auth-storage.ts";
import { ModelRegistry } from "../src/core/model-registry.ts";
import { ModelRuntime } from "../src/core/model-runtime.ts";
interface CapturedRequest {
url: string;
headers: Headers;
}
function createCapturingFetch(): { fetch: typeof globalThis.fetch; requests: CapturedRequest[] } {
const requests: CapturedRequest[] = [];
const fetch: typeof globalThis.fetch = async (input, init) => {
const request = new Request(input, init);
requests.push({ url: request.url, headers: request.headers });
const chunk = {
id: "chatcmpl-test",
object: "chat.completion.chunk",
created: 0,
model: "test",
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
};
return new Response(`data: ${JSON.stringify(chunk)}\n\ndata: [DONE]\n\n`, {
status: 200,
headers: { "content-type": "text/event-stream" },
});
};
return { fetch, requests };
}
async function createCloudflareRuntime(): Promise<{ modelRuntime: ModelRuntime; modelRegistry: ModelRegistry }> {
const authStorage = AuthStorage.inMemory();
await authStorage.modify("cloudflare-ai-gateway", async () => ({
type: "api_key",
key: "test-token",
env: {
CLOUDFLARE_ACCOUNT_ID: "test-account",
CLOUDFLARE_GATEWAY_ID: "test-gateway",
},
}));
const modelRuntime = await ModelRuntime.create({ credentials: authStorage, modelsPath: null });
return { modelRuntime, modelRegistry: new ModelRegistry(modelRuntime) };
}
const CLOUDFLARE_COMPAT_URL = "https://gateway.ai.cloudflare.com/v1/test-account/test-gateway/compat/chat/completions";
describe("ModelRegistry Cloudflare compat streaming", () => {
it("materializes the Cloudflare endpoint through ModelRuntime streaming", async () => {
const { modelRuntime } = await createCloudflareRuntime();
const model = modelRuntime.getModel("cloudflare-ai-gateway", "workers-ai/@cf/moonshotai/kimi-k2.6");
expect(model).toBeDefined();
resetApiProviders();
const { fetch, requests } = createCapturingFetch();
const result = await modelRuntime.completeSimple(model!, { messages: [] }, { fetch });
expect(result.stopReason).toBe("stop");
expect(requests).toHaveLength(1);
expect(requests[0].url).toBe(CLOUDFLARE_COMPAT_URL);
expect(requests[0].headers.get("cf-aig-authorization")).toBe("Bearer test-token");
});
it("materializes the Cloudflare endpoint after extension-style auth resolution", async () => {
const { modelRegistry } = await createCloudflareRuntime();
const model = modelRegistry.find("cloudflare-ai-gateway", "workers-ai/@cf/moonshotai/kimi-k2.6");
expect(model).toBeDefined();
resetApiProviders();
const auth = await modelRegistry.getApiKeyAndHeaders(model!);
expect(auth.ok).toBe(true);
if (!auth.ok) throw new Error(auth.error);
expect(auth.headers).toMatchObject({
"cf-aig-authorization": "Bearer test-token",
Authorization: null,
"x-api-key": null,
});
const { fetch, requests } = createCapturingFetch();
const result = await complete(model!, { messages: [] }, { ...auth, fetch });
expect(result.stopReason).toBe("stop");
expect(requests).toHaveLength(1);
expect(requests[0].url).toBe(CLOUDFLARE_COMPAT_URL);
expect(requests[0].headers.get("cf-aig-authorization")).toBe("Bearer test-token");
expect(requests[0].headers.has("authorization")).toBe(false);
expect(requests[0].headers.has("x-api-key")).toBe(false);
});
});