feat: add DeepSeek V4 Pro model support

- Add deepseek-v4-pro to the model registry with appropriate config
  (164K context, 16K max output, vision support, preserveReasoning)
- Update isThinkingModel check to recognize deepseek-v4 models so
  thinking mode and reasoning_content passback work correctly
- Add tests for the new model

Fixes #12177
This commit is contained in:
Roo Code 2026-04-24 10:04:49 +00:00
parent 96d6e43643
commit 415580b9b7
3 changed files with 80 additions and 3 deletions

View file

@ -32,6 +32,18 @@ export const deepSeekModels = {
cacheReadsPrice: 0.028, // $0.028 per million tokens (cache hit) - Updated Dec 9, 2025
description: `DeepSeek-V3.2 (Thinking Mode) achieves performance comparable to OpenAI-o1 across math, code, and reasoning tasks. Supports Chain of Thought reasoning with up to 8K output tokens. Supports JSON output, tool calls, and chat prefix completion (beta).`,
},
"deepseek-v4-pro": {
maxTokens: 16_384, // 16K max output
contextWindow: 164_000,
supportsImages: true,
supportsPromptCache: true,
preserveReasoning: true,
inputPrice: 2.19, // $2.19 per million tokens (cache miss)
outputPrice: 8.87, // $8.87 per million tokens
cacheWritesPrice: 2.19, // $2.19 per million tokens (cache miss)
cacheReadsPrice: 0.55, // $0.55 per million tokens (cache hit)
description: `DeepSeek V4 Pro is a frontier reasoning model with advanced capabilities across math, code, and complex reasoning tasks. Features 164K context window, 16K max output, vision support, and enhanced tool calling with interleaved thinking mode.`,
},
} as const satisfies Record<string, ModelInfo>
// https://api-docs.deepseek.com/quick_start/parameter_settings

View file

@ -30,7 +30,8 @@ vi.mock("openai", () => {
}
// Check if this is a reasoning_content test by looking at model
const isReasonerModel = options.model?.includes("deepseek-reasoner")
const isReasonerModel =
options.model?.includes("deepseek-reasoner") || options.model?.includes("deepseek-v4")
const isToolCallTest = options.tools?.length > 0
// Return async iterator for streaming
@ -507,5 +508,69 @@ describe("DeepSeekHandler", () => {
expect(toolCallChunks.length).toBeGreaterThan(0)
expect(toolCallChunks[0].name).toBe("get_weather")
})
it("should handle reasoning_content in streaming responses for deepseek-v4-pro", async () => {
const v4Handler = new DeepSeekHandler({
...mockOptions,
apiModelId: "deepseek-v4-pro",
})
const stream = v4Handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}
// Should have reasoning chunks
const reasoningChunks = chunks.filter((chunk) => chunk.type === "reasoning")
expect(reasoningChunks.length).toBeGreaterThan(0)
expect(reasoningChunks[0].text).toBe("Let me think about this...")
expect(reasoningChunks[1].text).toBe(" I'll analyze step by step.")
})
it("should pass thinking parameter for deepseek-v4-pro model", async () => {
const v4Handler = new DeepSeekHandler({
...mockOptions,
apiModelId: "deepseek-v4-pro",
})
const stream = v4Handler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume the stream
}
// Verify that the thinking parameter was passed to the API
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
thinking: { type: "enabled" },
}),
{}, // Empty path options for non-Azure URLs
)
})
it("should have preserveReasoning enabled for deepseek-v4-pro", () => {
const v4Handler = new DeepSeekHandler({
...mockOptions,
apiModelId: "deepseek-v4-pro",
})
const model = v4Handler.getModel()
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
})
})
describe("deepseek-v4-pro model info", () => {
it("should return correct model info for deepseek-v4-pro", () => {
const v4Handler = new DeepSeekHandler({
...mockOptions,
apiModelId: "deepseek-v4-pro",
})
const model = v4Handler.getModel()
expect(model.id).toBe("deepseek-v4-pro")
expect(model.info).toBeDefined()
expect(model.info.maxTokens).toBe(16_384)
expect(model.info.contextWindow).toBe(164_000)
expect(model.info.supportsImages).toBe(true)
expect(model.info.supportsPromptCache).toBe(true)
})
})
})

View file

@ -55,8 +55,8 @@ export class DeepSeekHandler extends OpenAiHandler {
const modelId = this.options.apiModelId ?? deepSeekDefaultModelId
const { info: modelInfo } = this.getModel()
// Check if this is a thinking-enabled model (deepseek-reasoner)
const isThinkingModel = modelId.includes("deepseek-reasoner")
// Check if this is a thinking-enabled model (deepseek-reasoner or deepseek-v4-pro)
const isThinkingModel = modelId.includes("deepseek-reasoner") || modelId.includes("deepseek-v4")
// Convert messages to R1 format (merges consecutive same-role messages)
// This is required for DeepSeek which does not support successive messages with the same role