fix(ai): finalize cloudflare gateway provider support

This commit is contained in:
Mario Zechner
2026-05-01 00:56:05 +02:00
parent 24fb6b833b
commit a45577bd00
19 changed files with 1086 additions and 768 deletions

View File

@@ -13,6 +13,7 @@ type StreamOptionsWithExtras = StreamOptions & Record<string, unknown>;
import { StringEnum } from "../src/utils/typebox-helpers.js";
import { hasAzureOpenAICredentials, resolveAzureDeploymentName } from "./azure-utils.js";
import { hasBedrockCredentials } from "./bedrock-utils.js";
import { hasCloudflareAiGatewayCredentials, hasCloudflareWorkersAICredentials } from "./cloudflare-utils.js";
import { resolveApiKey } from "./oauth.js";
const __filename = fileURLToPath(import.meta.url);
@@ -614,7 +615,7 @@ describe("Generate E2E Tests", () => {
});
});
describe.skipIf(!process.env.CLOUDFLARE_API_KEY || !process.env.CLOUDFLARE_ACCOUNT_ID)(
describe.skipIf(!hasCloudflareWorkersAICredentials())(
"Cloudflare Workers AI Provider (Kimi K2.6 via OpenAI Completions)",
() => {
const llm = getModel("cloudflare-workers-ai", "@cf/moonshotai/kimi-k2.6");
@@ -641,59 +642,98 @@ describe("Generate E2E Tests", () => {
},
);
describe.skipIf(
!process.env.CLOUDFLARE_API_KEY || !process.env.CLOUDFLARE_ACCOUNT_ID || !process.env.CLOUDFLARE_GATEWAY_ID,
)("Cloudflare AI Gateway → Workers AI (Kimi K2.6 via /compat)", () => {
const llm = getModel("cloudflare-ai-gateway", "workers-ai/@cf/moonshotai/kimi-k2.6");
describe.skipIf(!hasCloudflareAiGatewayCredentials())(
"Cloudflare AI Gateway → Workers AI (Kimi K2.6 via /compat)",
() => {
const llm = getModel("cloudflare-ai-gateway", "workers-ai/@cf/moonshotai/kimi-k2.6");
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm);
});
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm);
});
it("should handle tool calling", { retry: 3 }, async () => {
await handleToolCall(llm);
});
it("should handle tool calling", { retry: 3 }, async () => {
await handleToolCall(llm);
});
it("should handle streaming", { retry: 3 }, async () => {
await handleStreaming(llm);
});
});
it("should handle streaming", { retry: 3 }, async () => {
await handleStreaming(llm);
});
describe.skipIf(
!process.env.CLOUDFLARE_API_KEY || !process.env.CLOUDFLARE_ACCOUNT_ID || !process.env.CLOUDFLARE_GATEWAY_ID,
)("Cloudflare AI Gateway → OpenAI (gpt-5.1 via /openai responses)", () => {
const llm = getModel("cloudflare-ai-gateway", "gpt-5.1");
it("should handle thinking mode", { retry: 3 }, async () => {
await handleThinking(llm, { reasoningEffort: "medium" });
});
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm);
});
it("should handle multi-turn with thinking and tools", { retry: 3 }, async () => {
await multiTurn(llm, { reasoningEffort: "medium" });
});
},
);
it("should handle tool calling", { retry: 3 }, async () => {
await handleToolCall(llm);
});
describe.skipIf(!hasCloudflareAiGatewayCredentials() || !process.env.OPENAI_API_KEY)(
"Cloudflare AI Gateway → OpenAI BYOK (gpt-5.1 via /openai responses)",
() => {
const llm = getModel("cloudflare-ai-gateway", "gpt-5.1");
const options = { headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` } };
const thinkingOptions = {
...options,
thinkingEnabled: true,
reasoningEffort: "medium",
} satisfies StreamOptionsWithExtras;
it("should handle streaming", { retry: 3 }, async () => {
await handleStreaming(llm);
});
});
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm, options);
});
describe.skipIf(
!process.env.CLOUDFLARE_API_KEY || !process.env.CLOUDFLARE_ACCOUNT_ID || !process.env.CLOUDFLARE_GATEWAY_ID,
)("Cloudflare AI Gateway → Anthropic (claude-sonnet-4-5 via /compat)", () => {
const llm = getModel("cloudflare-ai-gateway", "anthropic/claude-sonnet-4-5");
it("should handle tool calling", { retry: 3 }, async () => {
await handleToolCall(llm, options);
});
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm);
});
it("should handle streaming", { retry: 3 }, async () => {
await handleStreaming(llm, options);
});
it("should handle tool calling", { retry: 3 }, async () => {
await handleToolCall(llm);
});
it("should handle thinking mode", { retry: 3 }, async () => {
await handleThinking(llm, thinkingOptions);
});
it("should handle streaming", { retry: 3 }, async () => {
await handleStreaming(llm);
});
});
it("should handle multi-turn with thinking and tools", { retry: 3 }, async () => {
await multiTurn(llm, thinkingOptions);
});
},
);
describe.skipIf(!hasCloudflareAiGatewayCredentials() || !process.env.ANTHROPIC_API_KEY)(
"Cloudflare AI Gateway → Anthropic BYOK (claude-sonnet-4-5 via /anthropic messages)",
() => {
const llm = getModel("cloudflare-ai-gateway", "claude-sonnet-4-5");
const options = { headers: { Authorization: `Bearer ${process.env.ANTHROPIC_API_KEY}` } };
const thinkingOptions = {
...options,
thinkingEnabled: true,
reasoningEffort: "high",
} satisfies StreamOptionsWithExtras;
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm, options);
});
it("should handle tool calling", { retry: 3 }, async () => {
await handleToolCall(llm, options);
});
it("should handle streaming", { retry: 3 }, async () => {
await handleStreaming(llm, options);
});
it("should handle thinking mode", { retry: 3 }, async () => {
await handleThinking(llm, thinkingOptions);
});
it("should handle multi-turn with thinking and tools", { retry: 3 }, async () => {
await multiTurn(llm, thinkingOptions);
});
},
);
describe.skipIf(!process.env.HF_TOKEN)("Hugging Face Provider (Kimi-K2.5 via OpenAI Completions)", () => {
const llm = getModel("huggingface", "moonshotai/Kimi-K2.5");