fix(ai): disable OpenAI reasoning where supported

This commit is contained in:
Mario Zechner
2026-05-07 22:52:54 +02:00
parent 29dea9a4e6
commit 783e96a144
4 changed files with 160 additions and 13 deletions

View File

@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed OpenAI Responses requests for models that support disabling reasoning to send `reasoning.effort: "none"` when thinking is off.
## [0.74.0] - 2026-05-07
## [0.73.1] - 2026-05-07

View File

@@ -86,6 +86,16 @@ const DEEPSEEK_V4_THINKING_LEVEL_MAP = {
xhigh: "max",
} as const;
const OPENAI_RESPONSES_NONE_REASONING_MODELS = new Set([
"gpt-5.1",
"gpt-5.2",
"gpt-5.3-codex",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-5.5",
]);
function mergeThinkingLevelMap(model: Model<any>, map: NonNullable<Model<any>["thinkingLevelMap"]>): void {
model.thinkingLevelMap = { ...model.thinkingLevelMap, ...map };
}
@@ -122,6 +132,13 @@ function applyThinkingLevelMetadata(model: Model<any>): void {
) {
mergeThinkingLevelMap(model, { off: null });
}
if (
model.api === "openai-responses" &&
model.provider === "openai" &&
OPENAI_RESPONSES_NONE_REASONING_MODELS.has(model.id)
) {
mergeThinkingLevelMap(model, { off: "none" });
}
if (supportsOpenAiXhigh(model.id)) {
mergeThinkingLevelMap(model, { xhigh: "xhigh" });
}

View File

@@ -4708,6 +4708,24 @@ export const MODELS = {
contextWindow: 1000000,
maxTokens: 64000,
} satisfies Model<"google-generative-ai">,
"gemini-3.1-flash-lite": {
id: "gemini-3.1-flash-lite",
name: "Gemini 3.1 Flash Lite",
api: "google-generative-ai",
provider: "google",
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
reasoning: true,
thinkingLevelMap: {"off":null},
input: ["text", "image"],
cost: {
input: 0.25,
output: 1.5,
cacheRead: 0.025,
cacheWrite: 1,
},
contextWindow: 1048576,
maxTokens: 65536,
} satisfies Model<"google-generative-ai">,
"gemini-3.1-flash-lite-preview": {
id: "gemini-3.1-flash-lite-preview",
name: "Gemini 3.1 Flash Lite Preview",
@@ -6948,7 +6966,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null},
thinkingLevelMap: {"off":"none"},
input: ["text", "image"],
cost: {
input: 1.25,
@@ -7038,7 +7056,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null,"xhigh":"xhigh"},
thinkingLevelMap: {"off":"none","xhigh":"xhigh"},
input: ["text", "image"],
cost: {
input: 1.75,
@@ -7128,7 +7146,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null,"xhigh":"xhigh"},
thinkingLevelMap: {"off":"none","xhigh":"xhigh"},
input: ["text", "image"],
cost: {
input: 1.75,
@@ -7164,7 +7182,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null,"xhigh":"xhigh"},
thinkingLevelMap: {"off":"none","xhigh":"xhigh"},
input: ["text", "image"],
cost: {
input: 2.5,
@@ -7182,7 +7200,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null,"xhigh":"xhigh"},
thinkingLevelMap: {"off":"none","xhigh":"xhigh"},
input: ["text", "image"],
cost: {
input: 0.75,
@@ -7200,7 +7218,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null,"xhigh":"xhigh"},
thinkingLevelMap: {"off":"none","xhigh":"xhigh"},
input: ["text", "image"],
cost: {
input: 0.2,
@@ -7236,7 +7254,7 @@ export const MODELS = {
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
thinkingLevelMap: {"off":null,"xhigh":"xhigh"},
thinkingLevelMap: {"off":"none","xhigh":"xhigh"},
input: ["text", "image"],
cost: {
input: 5,
@@ -9405,6 +9423,23 @@ export const MODELS = {
contextWindow: 1048576,
maxTokens: 65536,
} satisfies Model<"openai-completions">,
"google/gemini-3.1-flash-lite": {
id: "google/gemini-3.1-flash-lite",
name: "Google: Gemini 3.1 Flash Lite",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.25,
output: 1.5,
cacheRead: 0.024999999999999998,
cacheWrite: 0.08333333333333334,
},
contextWindow: 1048576,
maxTokens: 65536,
} satisfies Model<"openai-completions">,
"google/gemini-3.1-flash-lite-preview": {
id: "google/gemini-3.1-flash-lite-preview",
name: "Google: Gemini 3.1 Flash Lite Preview",
@@ -9592,18 +9627,18 @@ export const MODELS = {
contextWindow: 128000,
maxTokens: 50000,
} satisfies Model<"openai-completions">,
"inclusionai/ling-2.6-1t:free": {
id: "inclusionai/ling-2.6-1t:free",
name: "inclusionAI: Ling-2.6-1T (free)",
"inclusionai/ling-2.6-1t": {
id: "inclusionai/ling-2.6-1t",
name: "inclusionAI: Ling-2.6-1T",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: false,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
input: 0.3,
output: 2.5,
cacheRead: 0.06,
cacheWrite: 0,
},
contextWindow: 262144,
@@ -13994,6 +14029,23 @@ export const MODELS = {
contextWindow: 1000000,
maxTokens: 64000,
} satisfies Model<"anthropic-messages">,
"google/gemini-3.1-flash-lite": {
id: "google/gemini-3.1-flash-lite",
name: "Gemini 3.1 Flash Lite",
api: "anthropic-messages",
provider: "vercel-ai-gateway",
baseUrl: "https://ai-gateway.vercel.sh",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.25,
output: 1.5,
cacheRead: 0.03,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 65000,
} satisfies Model<"anthropic-messages">,
"google/gemini-3.1-flash-lite-preview": {
id: "google/gemini-3.1-flash-lite-preview",
name: "Gemini 3.1 Flash Lite Preview",

View File

@@ -91,6 +91,80 @@ describe("openai-responses provider defaults", () => {
});
});
it.each(["gpt-5.1", "gpt-5.2", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-5.5"] as const)(
"sends none reasoning effort for OpenAI %s when no reasoning is requested",
async (modelId) => {
const model = getModel("openai", modelId);
let capturedPayload: unknown;
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response("data: [DONE]\n\n", {
status: 200,
headers: { "content-type": "text/event-stream" },
}),
);
const stream = streamOpenAIResponses(
model,
{
systemPrompt: "sys",
messages: [{ role: "user", content: "hi", timestamp: Date.now() }],
},
{
apiKey: "test-key",
onPayload: (payload) => {
capturedPayload = payload;
},
},
);
for await (const event of stream) {
if (event.type === "done" || event.type === "error") break;
}
expect(capturedPayload).toMatchObject({
reasoning: { effort: "none" },
});
},
);
it.each(["gpt-5", "gpt-5-mini", "gpt-5-nano", "gpt-5-pro", "gpt-5.2-pro", "gpt-5.4-pro", "gpt-5.5-pro"] as const)(
"omits reasoning effort for OpenAI %s when off is unsupported",
async (modelId) => {
const model = getModel("openai", modelId);
let capturedPayload: unknown;
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response("data: [DONE]\n\n", {
status: 200,
headers: { "content-type": "text/event-stream" },
}),
);
const stream = streamOpenAIResponses(
model,
{
systemPrompt: "sys",
messages: [{ role: "user", content: "hi", timestamp: Date.now() }],
},
{
apiKey: "test-key",
onPayload: (payload) => {
capturedPayload = payload;
},
},
);
for await (const event of stream) {
if (event.type === "done" || event.type === "error") break;
}
expect(capturedPayload).not.toMatchObject({
reasoning: expect.anything(),
});
},
);
it("sets cache-affinity headers for official OpenAI Responses requests with a sessionId", async () => {
const captured = await captureOpenAIResponseHeaders({ sessionId: "session-123" });