diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index aa3e5b632b..f45b311f63 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -409,13 +409,15 @@ describe("OpenAiHandler", () => { expect(callArgs).not.toHaveProperty("temperature") }) - it("should include temperature by default when supportsTemperature is not set", async () => { + it("should omit temperature by default when no custom temperature is set", async () => { + // Option A: when "use custom temperature" is off (modelTemperature unset) and the model has no + // required default, omit `temperature` so the server's own default applies instead of forcing 0. 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") + expect(callArgs).not.toHaveProperty("temperature") }) it("should use the configured modelTemperature when supportsTemperature is not false", async () => { @@ -438,6 +440,17 @@ describe("OpenAiHandler", () => { expect(callArgs.temperature).toBe(DEEP_SEEK_DEFAULT_TEMPERATURE) }) + it("should still send temperature when the user sets a custom value of 0", async () => { + // A deliberate 0 must be distinguished from "unset" — it is sent, not omitted. + const zeroTempHandler = new OpenAiHandler({ ...mockOptions, modelTemperature: 0 }) + const stream = zeroTempHandler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + } + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(0) + }) + it("should include max_tokens when includeMaxTokens is true", async () => { const optionsWithMaxTokens: ApiHandlerOptions = { ...mockOptions, @@ -679,7 +692,7 @@ describe("OpenAiHandler", () => { ], stream: true, stream_options: { include_usage: true }, - temperature: 0, + // No custom temperature set → `temperature` is omitted. tools: undefined, tool_choice: undefined, parallel_tool_calls: true, diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 336a290c2f..532ed38ba2 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -155,13 +155,14 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { model: modelId, // Some OpenAI-Compatible models (e.g. claude-opus-4-7, claude-opus-4-8) 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), - }), + // `temperature` as deprecated/unsupported, so honor the model's `supportsTemperature` + // flag and omit it when that flag is false. Beyond that, only send `temperature` when + // the user set a custom value or the model needs a specific default (deepseek-reasoner); + // otherwise omit it so the server's own default applies instead of forcing 0. + ...(modelInfo.supportsTemperature !== false && + (this.options.modelTemperature != null || deepseekReasoner) && { + temperature: this.options.modelTemperature ?? DEEP_SEEK_DEFAULT_TEMPERATURE, + }), messages: convertedMessages, stream: true as const, ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }),