diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 14ed35430a..a6fdc8cf45 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -380,6 +380,55 @@ describe("OpenAiHandler", () => { const callArgs = mockCreate.mock.calls[0][0] expect(callArgs.temperature).toBe(0.6) }) + + it("should detect DeepSeek V3 models with reasoning effort as reasoning models", async () => { + const deepseekV3Options: ApiHandlerOptions = { + ...mockOptions, + openAiModelId: "deepseek-v3", + openAiCustomModelInfo: { + ...openAiModelInfoSaneDefaults, + supportsReasoningEffort: true, + }, + reasoningEffort: "medium", + } + const deepseekHandler = new OpenAiHandler(deepseekV3Options) + const stream = deepseekHandler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + // consume stream + } + // Assert the mockCreate was called with R1 format messages + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + // When DeepSeek is detected as a reasoning model, it uses R1 format + // which combines system and user messages + expect(callArgs.messages[0].role).toBe("user") + expect(callArgs.messages[0].content).toContain("You are a helpful assistant.") + expect(callArgs.reasoning_effort).toBe("medium") + }) + + it("should detect DeepSeek-chat models with reasoning effort as reasoning models", async () => { + const deepseekChatOptions: ApiHandlerOptions = { + ...mockOptions, + openAiModelId: "deepseek-chat", + openAiCustomModelInfo: { + ...openAiModelInfoSaneDefaults, + supportsReasoningEffort: true, + }, + reasoningEffort: "high", + } + const deepseekHandler = new OpenAiHandler(deepseekChatOptions) + const stream = deepseekHandler.createMessage(systemPrompt, messages) + for await (const _chunk of stream) { + // consume stream + } + // Assert the mockCreate was called with R1 format messages + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + // When DeepSeek is detected as a reasoning model, it uses R1 format + expect(callArgs.messages[0].role).toBe("user") + expect(callArgs.messages[0].content).toContain("You are a helpful assistant.") + expect(callArgs.reasoning_effort).toBe("high") + }) }) describe("error handling", () => { diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 36158d770c..07bcd55dc3 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -89,7 +89,12 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const enabledR1Format = this.options.openAiR1FormatEnabled ?? false const enabledLegacyFormat = this.options.openAiLegacyFormat ?? false const isAzureAiInference = this._isAzureAiInference(modelUrl) - const deepseekReasoner = modelId.includes("deepseek-reasoner") || enabledR1Format + // Check if this is a DeepSeek model with reasoning enabled + const isDeepSeekWithReasoning = + (modelId.toLowerCase().includes("deepseek") && reasoning) || + modelId.includes("deepseek-reasoner") || + enabledR1Format + const deepseekReasoner = isDeepSeekWithReasoning const ark = modelUrl.includes(".volces.com") if (modelId.includes("o1") || modelId.includes("o3") || modelId.includes("o4")) {