fix(ai): disable OpenAI reasoning where supported
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user