Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 46ea320

Browse files
committed
fix: respect supportsTemperature flag in OpenAI-compatible providers
When models like Claude Opus 4.7 are proxied through OpenAI-compatible gateways, sending the temperature parameter causes a 400 error because the model has deprecated it. This change: - Uses the existing supportsTemperature ModelInfo field in model-params.ts to omit temperature when supportsTemperature === false - Adds the same guard in OpenAiHandler, BaseOpenAiCompatibleProvider, and OpenAICompatibleHandler - Adds a "Supports Temperature" checkbox in the OpenAI Compatible settings UI so users can toggle it off for models that need it - Adds test coverage for the new behavior Closes #12162
1 parent 2bb8260 commit 46ea320

7 files changed

Lines changed: 125 additions & 13 deletions

File tree

src/api/providers/base-openai-compatible-provider.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
8484
format: "openai",
8585
}) ?? undefined
8686

87-
const temperature = this.options.modelTemperature ?? info.defaultTemperature ?? this.defaultTemperature
87+
const temperature =
88+
info.supportsTemperature === false
89+
? undefined
90+
: (this.options.modelTemperature ?? info.defaultTemperature ?? this.defaultTemperature)
8891

8992
const params: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
9093
model,

src/api/providers/openai-compatible.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,12 @@ export abstract class OpenAICompatibleHandler extends BaseProvider implements Si
166166
const aiSdkTools = convertToolsForAiSdk(openAiTools) as ToolSet | undefined
167167

168168
// Build the request options
169+
const supportsTemp = this.config.modelInfo.supportsTemperature !== false
169170
const requestOptions: Parameters<typeof streamText>[0] = {
170171
model: languageModel,
171172
system: systemPrompt,
172173
messages: aiSdkMessages,
173-
temperature: model.temperature ?? this.config.temperature ?? 0,
174+
temperature: supportsTemp ? (model.temperature ?? this.config.temperature ?? 0) : undefined,
174175
maxOutputTokens: this.getMaxOutputTokens(),
175176
tools: aiSdkTools,
176177
toolChoice: this.mapToolChoice(metadata?.tool_choice),
@@ -200,11 +201,12 @@ export abstract class OpenAICompatibleHandler extends BaseProvider implements Si
200201
async completePrompt(prompt: string): Promise<string> {
201202
const languageModel = this.getLanguageModel()
202203

204+
const supportsTemp = this.config.modelInfo.supportsTemperature !== false
203205
const { text } = await generateText({
204206
model: languageModel,
205207
prompt,
206208
maxOutputTokens: this.getMaxOutputTokens(),
207-
temperature: this.config.temperature ?? 0,
209+
temperature: supportsTemp ? (this.config.temperature ?? 0) : undefined,
208210
})
209211

210212
return text

src/api/providers/openai.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
154154

155155
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
156156
model: modelId,
157-
temperature: this.options.modelTemperature ?? (deepseekReasoner ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0),
157+
temperature:
158+
modelInfo.supportsTemperature === false
159+
? undefined
160+
: (this.options.modelTemperature ?? (deepseekReasoner ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0)),
158161
messages: convertedMessages,
159162
stream: true as const,
160163
...(isGrokXAI ? {} : { stream_options: { include_usage: true } }),

src/api/transform/__tests__/model-params.spec.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,4 +994,82 @@ describe("getModelParams", () => {
994994
expect(result.reasoningBudget).toBe(8192) // Default thinking tokens
995995
})
996996
})
997+
describe("supportsTemperature", () => {
998+
it("should set temperature to undefined for openai format when supportsTemperature is false", () => {
999+
const model: ModelInfo = {
1000+
...baseModel,
1001+
supportsTemperature: false,
1002+
}
1003+
1004+
const result = getModelParams({
1005+
...openaiParams,
1006+
settings: { modelTemperature: 0.5 },
1007+
model,
1008+
})
1009+
1010+
expect(result.temperature).toBeUndefined()
1011+
})
1012+
1013+
it("should keep temperature for openai format when supportsTemperature is true", () => {
1014+
const model: ModelInfo = {
1015+
...baseModel,
1016+
supportsTemperature: true,
1017+
}
1018+
1019+
const result = getModelParams({
1020+
...openaiParams,
1021+
settings: { modelTemperature: 0.5 },
1022+
model,
1023+
})
1024+
1025+
expect(result.temperature).toBe(0.5)
1026+
})
1027+
1028+
it("should keep temperature for openai format when supportsTemperature is undefined", () => {
1029+
const result = getModelParams({
1030+
...openaiParams,
1031+
settings: { modelTemperature: 0.5 },
1032+
model: baseModel,
1033+
})
1034+
1035+
expect(result.temperature).toBe(0.5)
1036+
})
1037+
1038+
it("should set temperature to undefined for openrouter format when supportsTemperature is false", () => {
1039+
const model: ModelInfo = {
1040+
...baseModel,
1041+
supportsTemperature: false,
1042+
}
1043+
1044+
const result = getModelParams({
1045+
...openrouterParams,
1046+
settings: { modelTemperature: 0.5 },
1047+
model,
1048+
})
1049+
1050+
expect(result.temperature).toBeUndefined()
1051+
})
1052+
1053+
it("should still remove temperature for o1 model IDs in openai format", () => {
1054+
const result = getModelParams({
1055+
...openaiParams,
1056+
modelId: "o1-preview",
1057+
settings: {},
1058+
model: baseModel,
1059+
})
1060+
1061+
expect(result.temperature).toBeUndefined()
1062+
})
1063+
1064+
it("should still remove temperature for o3-mini model IDs in openai format", () => {
1065+
const result = getModelParams({
1066+
...openaiParams,
1067+
modelId: "o3-mini",
1068+
settings: {},
1069+
model: baseModel,
1070+
})
1071+
1072+
expect(result.temperature).toBeUndefined()
1073+
})
1074+
})
9971075
})

src/api/transform/model-params.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ export function getModelParams({
153153
reasoning: getAnthropicReasoning({ model, reasoningBudget, reasoningEffort, settings }),
154154
}
155155
} else if (format === "openai") {
156-
// Special case for o1 and o3-mini, which don't support temperature.
157-
// TODO: Add a `supportsTemperature` field to the model info.
158-
if (modelId.startsWith("o1") || modelId.startsWith("o3-mini")) {
156+
// Omit temperature for models that don't support it (e.g. o1, o3-mini,
157+
// claude-opus-4-7 proxied via OpenAI-compatible gateways).
158+
if (model.supportsTemperature === false || modelId.startsWith("o1") || modelId.startsWith("o3-mini")) {
159159
params.temperature = undefined
160160
}
161161

@@ -172,12 +172,8 @@ export function getModelParams({
172172
reasoning: getGeminiReasoning({ model, reasoningBudget, reasoningEffort, settings }),
173173
}
174174
} else {
175-
// Special case for o1-pro, which doesn't support temperature.
176-
// Note that OpenRouter's `supported_parameters` field includes
177-
// `temperature`, which is probably a bug.
178-
// TODO: Add a `supportsTemperature` field to the model info and populate
179-
// it appropriately in the OpenRouter fetcher.
180-
if (modelId === "openai/o1-pro") {
175+
// Omit temperature for models that don't support it.
176+
if (model.supportsTemperature === false || modelId === "openai/o1-pro") {
181177
params.temperature = undefined
182178
}
183179

webview-ui/src/components/settings/providers/OpenAICompatible.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,32 @@ export const OpenAICompatible = ({
412412
</div>
413413
</div>
414414

415+
<div>
416+
<div className="flex items-center gap-1">
417+
<Checkbox
418+
checked={apiConfiguration?.openAiCustomModelInfo?.supportsTemperature ?? true}
419+
onChange={handleInputChange("openAiCustomModelInfo", (checked) => {
420+
return {
421+
...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults),
422+
supportsTemperature: checked,
423+
}
424+
})}>
425+
<span className="font-medium">
426+
{t("settings:providers.customModel.temperatureSupport.label")}
427+
</span>
428+
</Checkbox>
429+
<StandardTooltip content={t("settings:providers.customModel.temperatureSupport.description")}>
430+
<i
431+
className="codicon codicon-info text-vscode-descriptionForeground"
432+
style={{ fontSize: "12px" }}
433+
/>
434+
</StandardTooltip>
435+
</div>
436+
<div className="text-sm text-vscode-descriptionForeground pt-1">
437+
{t("settings:providers.customModel.temperatureSupport.description")}
438+
</div>
439+
</div>
440+
415441
<div>
416442
<VSCodeTextField
417443
value={

webview-ui/src/i18n/locales/en/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,10 @@
567567
"label": "Prompt Caching",
568568
"description": "Is this model capable of caching prompts?"
569569
},
570+
"temperatureSupport": {
571+
"label": "Supports Temperature",
572+
"description": "Does this model accept a temperature parameter? Uncheck for models like Claude Opus 4.7 that have deprecated temperature."
573+
},
570574
"pricing": {
571575
"input": {
572576
"label": "Input Price",

0 commit comments

Comments
 (0)