Skip to content

Commit 513f47d

Browse files
fix(openai): omit temperature for models that don't support it (#215) (#233)
* fix(openai): omit temperature for models that don't support it (#215) claude-opus-4-7 (and similar) reject requests through the OpenAI-Compatible provider with a 400 error because 'temperature' is deprecated/unsupported. Honor the model's existing supportsTemperature flag (already respected by openai-native, gemini, lite-llm and vercel-ai-gateway) and omit temperature from the streaming request when it is explicitly set to false. undefined keeps sending temperature, preserving current behavior for all other models. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(openai): cover both temperature branches behind supportsTemperature gate (#215) Add two tests so the temperature expression's branches are fully exercised: - explicit modelTemperature (left side of the `??`) - deepseek-reasoner default of DEEP_SEEK_DEFAULT_TEMPERATURE (truthy ternary) Closes the partial-branch gap codecov/patch flagged on line 162. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 2aa944e commit 513f47d

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/api/providers/__tests__/openai.spec.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { OpenAiHandler, getOpenAiModels } from "../openai"
44
import { ApiHandlerOptions } from "../../../shared/api"
55
import { Anthropic } from "@anthropic-ai/sdk"
66
import OpenAI from "openai"
7-
import { openAiModelInfoSaneDefaults } from "@roo-code/types"
7+
import { openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types"
88
import { Package } from "../../../shared/package"
99
import axios from "axios"
1010

@@ -391,6 +391,53 @@ describe("OpenAiHandler", () => {
391391
expect(callArgs.reasoning_effort).toBeUndefined()
392392
})
393393

394+
it("should omit temperature when the model sets supportsTemperature to false", async () => {
395+
const noTempOptions: ApiHandlerOptions = {
396+
...mockOptions,
397+
openAiCustomModelInfo: {
398+
contextWindow: 128_000,
399+
supportsPromptCache: false,
400+
supportsTemperature: false,
401+
},
402+
}
403+
const noTempHandler = new OpenAiHandler(noTempOptions)
404+
const stream = noTempHandler.createMessage(systemPrompt, messages)
405+
for await (const _chunk of stream) {
406+
}
407+
expect(mockCreate).toHaveBeenCalled()
408+
const callArgs = mockCreate.mock.calls[0][0]
409+
expect(callArgs).not.toHaveProperty("temperature")
410+
})
411+
412+
it("should include temperature by default when supportsTemperature is not set", async () => {
413+
const stream = handler.createMessage(systemPrompt, messages)
414+
for await (const _chunk of stream) {
415+
}
416+
expect(mockCreate).toHaveBeenCalled()
417+
const callArgs = mockCreate.mock.calls[0][0]
418+
expect(callArgs).toHaveProperty("temperature")
419+
})
420+
421+
it("should use the configured modelTemperature when supportsTemperature is not false", async () => {
422+
const customTempHandler = new OpenAiHandler({ ...mockOptions, modelTemperature: 0.5 })
423+
const stream = customTempHandler.createMessage(systemPrompt, messages)
424+
for await (const _chunk of stream) {
425+
}
426+
expect(mockCreate).toHaveBeenCalled()
427+
const callArgs = mockCreate.mock.calls[0][0]
428+
expect(callArgs.temperature).toBe(0.5)
429+
})
430+
431+
it("should default to DEEP_SEEK_DEFAULT_TEMPERATURE for deepseek-reasoner models", async () => {
432+
const deepseekHandler = new OpenAiHandler({ ...mockOptions, openAiModelId: "deepseek-reasoner" })
433+
const stream = deepseekHandler.createMessage(systemPrompt, messages)
434+
for await (const _chunk of stream) {
435+
}
436+
expect(mockCreate).toHaveBeenCalled()
437+
const callArgs = mockCreate.mock.calls[0][0]
438+
expect(callArgs.temperature).toBe(DEEP_SEEK_DEFAULT_TEMPERATURE)
439+
})
440+
394441
it("should include max_tokens when includeMaxTokens is true", async () => {
395442
const optionsWithMaxTokens: ApiHandlerOptions = {
396443
...mockOptions,

src/api/providers/openai.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,13 @@ 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+
// Some OpenAI-Compatible models (e.g. claude-opus-4-7) reject `temperature` as
158+
// deprecated/unsupported. Honor the model's `supportsTemperature` flag and omit it
159+
// when explicitly set to false (undefined still sends temperature, preserving behavior).
160+
...(modelInfo.supportsTemperature !== false && {
161+
temperature:
162+
this.options.modelTemperature ?? (deepseekReasoner ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0),
163+
}),
158164
messages: convertedMessages,
159165
stream: true as const,
160166
...(isGrokXAI ? {} : { stream_options: { include_usage: true } }),

0 commit comments

Comments
 (0)