Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit e94d7b3

Browse files
committed
feat: add deepseek-v4-pro and deepseek-v4-flash models
- Add deepseek-v4-pro and deepseek-v4-flash to DeepSeek model definitions - Both models support thinking mode (preserveReasoning), vision, and prompt caching - Update thinking mode detection to use modelInfo.preserveReasoning flag instead of hardcoded model name check, making it future-proof - Add tests for new v4 model info and thinking mode behavior Addresses #12174
1 parent 96d6e43 commit e94d7b3

3 files changed

Lines changed: 118 additions & 5 deletions

File tree

packages/types/src/providers/deepseek.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,30 @@ export const deepSeekModels = {
3232
cacheReadsPrice: 0.028, // $0.028 per million tokens (cache hit) - Updated Dec 9, 2025
3333
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).`,
3434
},
35+
"deepseek-v4-pro": {
36+
maxTokens: 16_384, // 16K max output
37+
contextWindow: 128_000,
38+
supportsImages: true,
39+
supportsPromptCache: true,
40+
preserveReasoning: true,
41+
inputPrice: 2.0, // $2.00 per million tokens (cache miss)
42+
outputPrice: 8.0, // $8.00 per million tokens
43+
cacheWritesPrice: 2.0, // $2.00 per million tokens (cache miss)
44+
cacheReadsPrice: 0.5, // $0.50 per million tokens (cache hit)
45+
description: `DeepSeek V4 Pro is a flagship reasoning model with thinking capabilities, vision support, and enhanced tool use. Excels at complex reasoning, coding, and multi-step problem solving tasks.`,
46+
},
47+
"deepseek-v4-flash": {
48+
maxTokens: 16_384, // 16K max output
49+
contextWindow: 128_000,
50+
supportsImages: true,
51+
supportsPromptCache: true,
52+
preserveReasoning: true,
53+
inputPrice: 1.0, // $1.00 per million tokens (cache miss)
54+
outputPrice: 4.0, // $4.00 per million tokens
55+
cacheWritesPrice: 1.0, // $1.00 per million tokens (cache miss)
56+
cacheReadsPrice: 0.25, // $0.25 per million tokens (cache hit)
57+
description: `DeepSeek V4 Flash is a fast, cost-efficient reasoning model with thinking capabilities and vision support. Optimized for speed while maintaining strong performance across coding, reasoning, and general tasks.`,
58+
},
3559
} as const satisfies Record<string, ModelInfo>
3660

3761
// https://api-docs.deepseek.com/quick_start/parameter_settings

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

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ vi.mock("openai", () => {
2929
}
3030
}
3131

32-
// Check if this is a reasoning_content test by looking at model
33-
const isReasonerModel = options.model?.includes("deepseek-reasoner")
32+
// Check if this is a thinking model - matches models with preserveReasoning: true
33+
// (deepseek-reasoner, deepseek-v4-pro, deepseek-v4-flash)
34+
const isReasonerModel =
35+
options.model?.includes("deepseek-reasoner") || options.model?.includes("deepseek-v4-")
3436
const isToolCallTest = options.tools?.length > 0
3537

3638
// Return async iterator for streaming
@@ -122,7 +124,7 @@ vi.mock("openai", () => {
122124
import OpenAI from "openai"
123125
import type { Anthropic } from "@anthropic-ai/sdk"
124126

125-
import { deepSeekDefaultModelId, DEEP_SEEK_DEFAULT_TEMPERATURE, type ModelInfo } from "@roo-code/types"
127+
import { deepSeekDefaultModelId, deepSeekModels, DEEP_SEEK_DEFAULT_TEMPERATURE, type ModelInfo } from "@roo-code/types"
126128

127129
import type { ApiHandlerOptions } from "../../../shared/api"
128130

@@ -247,6 +249,36 @@ describe("DeepSeekHandler", () => {
247249
expect((model.info as ModelInfo).preserveReasoning).toBeUndefined()
248250
})
249251

252+
it("should return correct model info for deepseek-v4-pro", () => {
253+
const handlerWithV4Pro = new DeepSeekHandler({
254+
...mockOptions,
255+
apiModelId: "deepseek-v4-pro",
256+
})
257+
const model = handlerWithV4Pro.getModel()
258+
expect(model.id).toBe("deepseek-v4-pro")
259+
expect(model.info).toBeDefined()
260+
expect(model.info.maxTokens).toBe(16_384)
261+
expect(model.info.contextWindow).toBe(128_000)
262+
expect(model.info.supportsImages).toBe(true)
263+
expect(model.info.supportsPromptCache).toBe(true)
264+
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
265+
})
266+
267+
it("should return correct model info for deepseek-v4-flash", () => {
268+
const handlerWithV4Flash = new DeepSeekHandler({
269+
...mockOptions,
270+
apiModelId: "deepseek-v4-flash",
271+
})
272+
const model = handlerWithV4Flash.getModel()
273+
expect(model.id).toBe("deepseek-v4-flash")
274+
expect(model.info).toBeDefined()
275+
expect(model.info.maxTokens).toBe(16_384)
276+
expect(model.info.contextWindow).toBe(128_000)
277+
expect(model.info.supportsImages).toBe(true)
278+
expect(model.info.supportsPromptCache).toBe(true)
279+
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
280+
})
281+
250282
it("should return provided model ID with default model info if model does not exist", () => {
251283
const handlerWithInvalidModel = new DeepSeekHandler({
252284
...mockOptions,
@@ -475,6 +507,61 @@ describe("DeepSeekHandler", () => {
475507
expect(callArgs.thinking).toBeUndefined()
476508
})
477509

510+
it("should pass thinking parameter for deepseek-v4-pro model", async () => {
511+
const v4ProHandler = new DeepSeekHandler({
512+
...mockOptions,
513+
apiModelId: "deepseek-v4-pro",
514+
})
515+
516+
const stream = v4ProHandler.createMessage(systemPrompt, messages)
517+
for await (const _chunk of stream) {
518+
// Consume the stream
519+
}
520+
521+
expect(mockCreate).toHaveBeenCalledWith(
522+
expect.objectContaining({
523+
thinking: { type: "enabled" },
524+
}),
525+
{},
526+
)
527+
})
528+
529+
it("should pass thinking parameter for deepseek-v4-flash model", async () => {
530+
const v4FlashHandler = new DeepSeekHandler({
531+
...mockOptions,
532+
apiModelId: "deepseek-v4-flash",
533+
})
534+
535+
const stream = v4FlashHandler.createMessage(systemPrompt, messages)
536+
for await (const _chunk of stream) {
537+
// Consume the stream
538+
}
539+
540+
expect(mockCreate).toHaveBeenCalledWith(
541+
expect.objectContaining({
542+
thinking: { type: "enabled" },
543+
}),
544+
{},
545+
)
546+
})
547+
548+
it("should handle reasoning_content in streaming responses for deepseek-v4-pro", async () => {
549+
const v4ProHandler = new DeepSeekHandler({
550+
...mockOptions,
551+
apiModelId: "deepseek-v4-pro",
552+
})
553+
554+
const stream = v4ProHandler.createMessage(systemPrompt, messages)
555+
const chunks: any[] = []
556+
for await (const chunk of stream) {
557+
chunks.push(chunk)
558+
}
559+
560+
const reasoningChunks = chunks.filter((chunk) => chunk.type === "reasoning")
561+
expect(reasoningChunks.length).toBeGreaterThan(0)
562+
expect(reasoningChunks[0].text).toBe("Let me think about this...")
563+
})
564+
478565
it("should handle tool calls with reasoning_content", async () => {
479566
const reasonerHandler = new DeepSeekHandler({
480567
...mockOptions,

src/api/providers/deepseek.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
deepSeekDefaultModelId,
77
DEEP_SEEK_DEFAULT_TEMPERATURE,
88
OPENAI_AZURE_AI_INFERENCE_PATH,
9+
type ModelInfo,
910
} from "@roo-code/types"
1011

1112
import type { ApiHandlerOptions } from "../../shared/api"
@@ -55,8 +56,9 @@ export class DeepSeekHandler extends OpenAiHandler {
5556
const modelId = this.options.apiModelId ?? deepSeekDefaultModelId
5657
const { info: modelInfo } = this.getModel()
5758

58-
// Check if this is a thinking-enabled model (deepseek-reasoner)
59-
const isThinkingModel = modelId.includes("deepseek-reasoner")
59+
// Check if this is a thinking-enabled model using the preserveReasoning flag from ModelInfo.
60+
// This covers deepseek-reasoner, deepseek-v4-pro, deepseek-v4-flash, and any future thinking models.
61+
const isThinkingModel = !!(modelInfo as ModelInfo).preserveReasoning
6062

6163
// Convert messages to R1 format (merges consecutive same-role messages)
6264
// This is required for DeepSeek which does not support successive messages with the same role

0 commit comments

Comments
 (0)