diff --git a/src/api/providers/__tests__/chutes.spec.ts b/src/api/providers/__tests__/chutes.spec.ts index 0596a911df..0ce1823087 100644 --- a/src/api/providers/__tests__/chutes.spec.ts +++ b/src/api/providers/__tests__/chutes.spec.ts @@ -394,7 +394,6 @@ describe("ChutesHandler", () => { expect.objectContaining({ model: modelId, max_tokens: modelInfo.maxTokens, - temperature: 0.5, messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), stream: true, stream_options: { include_usage: true }, diff --git a/src/api/providers/__tests__/fireworks.spec.ts b/src/api/providers/__tests__/fireworks.spec.ts index cfab672c08..985a9edad8 100644 --- a/src/api/providers/__tests__/fireworks.spec.ts +++ b/src/api/providers/__tests__/fireworks.spec.ts @@ -352,7 +352,6 @@ describe("FireworksHandler", () => { expect.objectContaining({ model: modelId, max_tokens: modelInfo.maxTokens, - temperature: 0.5, messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), stream: true, stream_options: { include_usage: true }, diff --git a/src/api/providers/__tests__/groq.spec.ts b/src/api/providers/__tests__/groq.spec.ts index 72a834b21d..7de7a04262 100644 --- a/src/api/providers/__tests__/groq.spec.ts +++ b/src/api/providers/__tests__/groq.spec.ts @@ -114,7 +114,11 @@ describe("GroqHandler", () => { it("createMessage should pass correct parameters to Groq client", async () => { const modelId: GroqModelId = "llama-3.1-8b-instant" const modelInfo = groqModels[modelId] - const handlerWithModel = new GroqHandler({ apiModelId: modelId, groqApiKey: "test-groq-api-key" }) + const handlerWithModel = new GroqHandler({ + apiModelId: modelId, + groqApiKey: "test-groq-api-key", + modelTemperature: 0.5, // Explicitly set temperature for this test + }) mockCreate.mockImplementationOnce(() => { return { @@ -143,4 +147,75 @@ describe("GroqHandler", () => { }), ) }) + + it("should omit temperature when modelTemperature is undefined", async () => { + const modelId: GroqModelId = "llama-3.1-8b-instant" + const handlerWithoutTemp = new GroqHandler({ + apiModelId: modelId, + groqApiKey: "test-groq-api-key", + // modelTemperature is not set + }) + + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + async next() { + return { done: true } + }, + }), + } + }) + + const systemPrompt = "Test system prompt" + const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message" }] + + const messageGenerator = handlerWithoutTemp.createMessage(systemPrompt, messages) + await messageGenerator.next() + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: modelId, + messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), + stream: true, + }), + ) + + // Verify temperature is NOT included + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("temperature") + }) + + it("should include temperature when modelTemperature is explicitly set", async () => { + const modelId: GroqModelId = "llama-3.1-8b-instant" + const handlerWithTemp = new GroqHandler({ + apiModelId: modelId, + groqApiKey: "test-groq-api-key", + modelTemperature: 0.7, + }) + + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + async next() { + return { done: true } + }, + }), + } + }) + + const systemPrompt = "Test system prompt" + const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message" }] + + const messageGenerator = handlerWithTemp.createMessage(systemPrompt, messages) + await messageGenerator.next() + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: modelId, + temperature: 0.7, + messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), + stream: true, + }), + ) + }) }) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 3e744d6e16..14ed35430a 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -315,6 +315,71 @@ describe("OpenAiHandler", () => { const callArgs = mockCreate.mock.calls[0][0] expect(callArgs.max_completion_tokens).toBe(4096) }) + + it("should omit temperature when modelTemperature is undefined", async () => { + const optionsWithoutTemperature: ApiHandlerOptions = { + ...mockOptions, + // modelTemperature is not set, should not include temperature + } + const handlerWithoutTemperature = new OpenAiHandler(optionsWithoutTemperature) + const stream = handlerWithoutTemperature.createMessage(systemPrompt, messages) + // Consume the stream to trigger the API call + for await (const _chunk of stream) { + } + // Assert the mockCreate was called without temperature + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("temperature") + }) + + it("should include temperature when modelTemperature is explicitly set to 0", async () => { + const optionsWithZeroTemperature: ApiHandlerOptions = { + ...mockOptions, + modelTemperature: 0, + } + const handlerWithZeroTemperature = new OpenAiHandler(optionsWithZeroTemperature) + const stream = handlerWithZeroTemperature.createMessage(systemPrompt, messages) + // Consume the stream to trigger the API call + for await (const _chunk of stream) { + } + // Assert the mockCreate was called with temperature: 0 + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(0) + }) + + it("should include temperature when modelTemperature is set to a non-zero value", async () => { + const optionsWithCustomTemperature: ApiHandlerOptions = { + ...mockOptions, + modelTemperature: 0.7, + } + const handlerWithCustomTemperature = new OpenAiHandler(optionsWithCustomTemperature) + const stream = handlerWithCustomTemperature.createMessage(systemPrompt, messages) + // Consume the stream to trigger the API call + for await (const _chunk of stream) { + } + // Assert the mockCreate was called with temperature: 0.7 + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(0.7) + }) + + it("should include DEEP_SEEK_DEFAULT_TEMPERATURE for deepseek-reasoner models when temperature is not set", async () => { + const deepseekOptions: ApiHandlerOptions = { + ...mockOptions, + openAiModelId: "deepseek-reasoner", + // modelTemperature is not set + } + const deepseekHandler = new OpenAiHandler(deepseekOptions) + const stream = deepseekHandler.createMessage(systemPrompt, messages) + // Consume the stream to trigger the API call + for await (const _chunk of stream) { + } + // Assert the mockCreate was called with DEEP_SEEK_DEFAULT_TEMPERATURE (0.6) + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(0.6) + }) }) describe("error handling", () => { @@ -450,7 +515,7 @@ describe("OpenAiHandler", () => { ], stream: true, stream_options: { include_usage: true }, - temperature: 0, + // temperature should be omitted when not set }, { path: "/models/chat/completions" }, ) diff --git a/src/api/providers/__tests__/roo.spec.ts b/src/api/providers/__tests__/roo.spec.ts index b16a6e7ac1..093137e1b2 100644 --- a/src/api/providers/__tests__/roo.spec.ts +++ b/src/api/providers/__tests__/roo.spec.ts @@ -350,7 +350,7 @@ describe("RooHandler", () => { }) describe("temperature and model configuration", () => { - it("should use default temperature of 0.7", async () => { + it("should omit temperature when not explicitly set", async () => { handler = new RooHandler(mockOptions) const stream = handler.createMessage(systemPrompt, messages) for await (const _chunk of stream) { @@ -358,8 +358,8 @@ describe("RooHandler", () => { } expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - temperature: 0.7, + expect.not.objectContaining({ + temperature: expect.anything(), }), ) }) diff --git a/src/api/providers/__tests__/sambanova.spec.ts b/src/api/providers/__tests__/sambanova.spec.ts index cd0e4a1989..6757b7562f 100644 --- a/src/api/providers/__tests__/sambanova.spec.ts +++ b/src/api/providers/__tests__/sambanova.spec.ts @@ -144,7 +144,6 @@ describe("SambaNovaHandler", () => { expect.objectContaining({ model: modelId, max_tokens: modelInfo.maxTokens, - temperature: 0.7, messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), stream: true, stream_options: { include_usage: true }, diff --git a/src/api/providers/__tests__/zai.spec.ts b/src/api/providers/__tests__/zai.spec.ts index 6b93aaa43b..067f4344d5 100644 --- a/src/api/providers/__tests__/zai.spec.ts +++ b/src/api/providers/__tests__/zai.spec.ts @@ -220,7 +220,6 @@ describe("ZAiHandler", () => { expect.objectContaining({ model: modelId, max_tokens: modelInfo.maxTokens, - temperature: ZAI_DEFAULT_TEMPERATURE, messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), stream: true, stream_options: { include_usage: true }, diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index 3c824f2651..cb6331bab4 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -72,17 +72,19 @@ export abstract class BaseOpenAiCompatibleProvider info: { maxTokens: max_tokens }, } = this.getModel() - const temperature = this.options.modelTemperature ?? this.defaultTemperature - const params: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { model, max_tokens, - temperature, messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)], stream: true, stream_options: { include_usage: true }, } + // Only include temperature if explicitly set + if (this.options.modelTemperature !== undefined) { + params.temperature = this.options.modelTemperature + } + return this.client.chat.completions.create(params) } diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 939816480a..36158d770c 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -157,13 +157,20 @@ 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), messages: convertedMessages, stream: true as const, ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }), ...(reasoning && reasoning), } + // Only include temperature if explicitly set + if (this.options.modelTemperature !== undefined) { + requestOptions.temperature = this.options.modelTemperature + } else if (deepseekReasoner) { + // DeepSeek Reasoner has a specific default temperature + requestOptions.temperature = DEEP_SEEK_DEFAULT_TEMPERATURE + } + // Add max_tokens if needed this.addMaxTokensIfNeeded(requestOptions, modelInfo)