Skip to content

Commit 5bb3ac8

Browse files
fix(openai): omit temperature when no custom value is set (#242)
When "use custom temperature" is off, the OpenAI-Compatible provider still sent temperature: 0 (the fallback), so the model's server-side default never applied. Per the discussion on #242 (option A), omit the temperature field in that case. Preserves the supportsTemperature gate (#233), model-required defaults (deepseek-reasoner), and a deliberately-set 0.
1 parent 45b239c commit 5bb3ac8

3 files changed

Lines changed: 29 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"zoo-code": patch
3+
---
4+
5+
OpenAI-Compatible provider: omit the `temperature` field when no custom temperature is set, so the model's server-side default applies instead of forcing `0` (#242). Model-required defaults (e.g. `deepseek-reasoner`) and the existing `supportsTemperature` capability gate are preserved, and a deliberately-set `0` is still sent.

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,13 +409,15 @@ describe("OpenAiHandler", () => {
409409
expect(callArgs).not.toHaveProperty("temperature")
410410
})
411411

412-
it("should include temperature by default when supportsTemperature is not set", async () => {
412+
it("should omit temperature by default when no custom temperature is set (#242)", async () => {
413+
// Option A: when "use custom temperature" is off (modelTemperature unset) and the model has no
414+
// required default, omit `temperature` so the server's own default applies instead of forcing 0.
413415
const stream = handler.createMessage(systemPrompt, messages)
414416
for await (const _chunk of stream) {
415417
}
416418
expect(mockCreate).toHaveBeenCalled()
417419
const callArgs = mockCreate.mock.calls[0][0]
418-
expect(callArgs).toHaveProperty("temperature")
420+
expect(callArgs).not.toHaveProperty("temperature")
419421
})
420422

421423
it("should use the configured modelTemperature when supportsTemperature is not false", async () => {
@@ -438,6 +440,17 @@ describe("OpenAiHandler", () => {
438440
expect(callArgs.temperature).toBe(DEEP_SEEK_DEFAULT_TEMPERATURE)
439441
})
440442

443+
it("should still send temperature when the user sets a custom value of 0 (#242)", async () => {
444+
// A deliberate 0 must be distinguished from "unset" — it is sent, not omitted.
445+
const zeroTempHandler = new OpenAiHandler({ ...mockOptions, modelTemperature: 0 })
446+
const stream = zeroTempHandler.createMessage(systemPrompt, messages)
447+
for await (const _chunk of stream) {
448+
}
449+
expect(mockCreate).toHaveBeenCalled()
450+
const callArgs = mockCreate.mock.calls[0][0]
451+
expect(callArgs.temperature).toBe(0)
452+
})
453+
441454
it("should include max_tokens when includeMaxTokens is true", async () => {
442455
const optionsWithMaxTokens: ApiHandlerOptions = {
443456
...mockOptions,
@@ -679,7 +692,7 @@ describe("OpenAiHandler", () => {
679692
],
680693
stream: true,
681694
stream_options: { include_usage: true },
682-
temperature: 0,
695+
// #242 (option A): no custom temperature set → `temperature` is omitted.
683696
tools: undefined,
684697
tool_choice: undefined,
685698
parallel_tool_calls: true,

src/api/providers/openai.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,14 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
157157
// Some OpenAI-Compatible models (e.g. claude-opus-4-7) reject `temperature` as
158158
// deprecated/unsupported. Honor the model's `supportsTemperature` flag and omit it
159159
// 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-
}),
160+
// Include `temperature` only when the model supports it (#233) AND either the user set a
161+
// custom temperature or the model needs a specific default (deepseek-reasoner). When "use
162+
// custom temperature" is off and the model has no required default, omit it so the server's
163+
// own default applies instead of forcing 0 (#242 — option A).
164+
...(modelInfo.supportsTemperature !== false &&
165+
(this.options.modelTemperature != null || deepseekReasoner) && {
166+
temperature: this.options.modelTemperature ?? DEEP_SEEK_DEFAULT_TEMPERATURE,
167+
}),
164168
messages: convertedMessages,
165169
stream: true as const,
166170
...(isGrokXAI ? {} : { stream_options: { include_usage: true } }),

0 commit comments

Comments
 (0)