From 6b87fd5cb223af8a788ab72fbdf1afc750bfb118 Mon Sep 17 00:00:00 2001 From: "Dr. Armando Vaquera (proyectoauraorg)" Date: Thu, 21 May 2026 03:06:19 -0600 Subject: [PATCH 1/2] 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 --- src/api/providers/__tests__/openai.spec.ts | 27 ++++++++++++++++++++++ src/api/providers/openai.ts | 8 ++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 73b542dbc7..e322c43c1a 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -391,6 +391,33 @@ describe("OpenAiHandler", () => { expect(callArgs.reasoning_effort).toBeUndefined() }) + it("should omit temperature when the model sets supportsTemperature to false", async () => { + const noTempOptions: ApiHandlerOptions = { + ...mockOptions, + openAiCustomModelInfo: { + contextWindow: 128_000, + supportsPromptCache: false, + supportsTemperature: false, + }, + } + const noTempHandler = new OpenAiHandler(noTempOptions) + const stream = noTempHandler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + } + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("temperature") + }) + + it("should include temperature by default when supportsTemperature is not set", async () => { + const stream = handler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + } + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).toHaveProperty("temperature") + }) + it("should include max_tokens when includeMaxTokens is true", async () => { const optionsWithMaxTokens: ApiHandlerOptions = { ...mockOptions, diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 9eafca9636..7ea33196f9 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -154,7 +154,13 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { model: modelId, - temperature: this.options.modelTemperature ?? (deepseekReasoner ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0), + // Some OpenAI-Compatible models (e.g. claude-opus-4-7) reject `temperature` as + // deprecated/unsupported. Honor the model's `supportsTemperature` flag and omit it + // when explicitly set to false (undefined still sends temperature, preserving behavior). + ...(modelInfo.supportsTemperature !== false && { + temperature: + this.options.modelTemperature ?? (deepseekReasoner ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0), + }), messages: convertedMessages, stream: true as const, ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }), From 5cbdc52af5ab05ad448ff42293e1b3a4abc85439 Mon Sep 17 00:00:00 2001 From: "Dr. Armando Vaquera (proyectoauraorg)" Date: Thu, 21 May 2026 14:01:14 -0600 Subject: [PATCH 2/2] 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 --- src/api/providers/__tests__/openai.spec.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index e322c43c1a..ccbbb870df 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -4,7 +4,7 @@ import { OpenAiHandler, getOpenAiModels } from "../openai" import { ApiHandlerOptions } from "../../../shared/api" import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -import { openAiModelInfoSaneDefaults } from "@roo-code/types" +import { openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types" import { Package } from "../../../shared/package" import axios from "axios" @@ -418,6 +418,26 @@ describe("OpenAiHandler", () => { expect(callArgs).toHaveProperty("temperature") }) + it("should use the configured modelTemperature when supportsTemperature is not false", async () => { + const customTempHandler = new OpenAiHandler({ ...mockOptions, modelTemperature: 0.5 }) + const stream = customTempHandler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + } + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(0.5) + }) + + it("should default to DEEP_SEEK_DEFAULT_TEMPERATURE for deepseek-reasoner models", async () => { + const deepseekHandler = new OpenAiHandler({ ...mockOptions, openAiModelId: "deepseek-reasoner" }) + const stream = deepseekHandler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + } + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(DEEP_SEEK_DEFAULT_TEMPERATURE) + }) + it("should include max_tokens when includeMaxTokens is true", async () => { const optionsWithMaxTokens: ApiHandlerOptions = { ...mockOptions,