Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-gpt-56-responses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"workers-ai-provider": patch
---

Route OpenAI gpt-5.6 models through the Responses API.
10 changes: 6 additions & 4 deletions packages/workers-ai-provider/src/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import type { ProviderPlugin } from "./gateway-delegate";
*
* Requires `@ai-sdk/openai` (an optional peer dependency — install it yourself).
*
* Uses `.chat()` (Chat Completions) deliberately: the bare `openai()` call
* (AI SDK v6+) defaults to the Responses API, which the AI Gateway run catalog
* does not serve.
* Uses `.chat()` (Chat Completions) for the catalog's chat-completions models.
* The gpt-5.6 class only accepts the Responses API, so those models use
* `.responses()` instead.
*/
export const openai: ProviderPlugin = {
wireFormat: "openai",
Expand All @@ -22,5 +22,7 @@ export const openai: ProviderPlugin = {
// and the delegate strips the Authorization header on the gateway path.
// baseURL (set by the registry for non-OpenAI openai-wire providers) makes
// the generated URL host-strip to the right gateway-native endpoint.
createOpenAI({ apiKey: "unused", fetch, ...(baseURL ? { baseURL } : {}) }).chat(modelId),
createOpenAI({ apiKey: "unused", fetch, ...(baseURL ? { baseURL } : {}) })[
/^gpt-5\.6(?:-|$)/.test(modelId) ? "responses" : "chat"
](modelId),
};
37 changes: 37 additions & 0 deletions packages/workers-ai-provider/test/openai.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, it, vi } from "vitest";
import { openai } from "../src/openai";

async function requestedUrl(modelId: string, baseURL?: string) {
const fetch = vi.fn(async (input: RequestInfo | URL) => {
throw new Error(String(input));
});

const model = openai.create({
modelId,
fetch: fetch as typeof globalThis.fetch,
...(baseURL ? { baseURL } : {}),
});

await expect(model.doGenerate({ prompt: [] } as never)).rejects.toThrow();
return String(fetch.mock.calls[0]?.[0]);
}

describe("openai provider plugin", () => {
it("uses the Responses API for gpt-5.6 models", async () => {
for (const modelId of ["gpt-5.6", "gpt-5.6-luna", "gpt-5.6-mini"]) {
expect(await requestedUrl(modelId)).toContain("/responses");
}
});

it("keeps Chat Completions for ordinary OpenAI models", async () => {
for (const modelId of ["gpt-5.4-mini", "gpt-5.5"]) {
expect(await requestedUrl(modelId)).toContain("/chat/completions");
}
});

it("preserves the Responses endpoint when a Gateway base URL is configured", async () => {
expect(await requestedUrl("gpt-5.6-luna", "https://gateway.example/v1")).toBe(
"https://gateway.example/v1/responses",
);
});
});