mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
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
This commit is contained in:
parent
96d6e43643
commit
e94d7b3434
3 changed files with 118 additions and 5 deletions
|
|
@ -32,6 +32,30 @@ 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: 128_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
preserveReasoning: true,
|
||||
inputPrice: 2.0, // $2.00 per million tokens (cache miss)
|
||||
outputPrice: 8.0, // $8.00 per million tokens
|
||||
cacheWritesPrice: 2.0, // $2.00 per million tokens (cache miss)
|
||||
cacheReadsPrice: 0.5, // $0.50 per million tokens (cache hit)
|
||||
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.`,
|
||||
},
|
||||
"deepseek-v4-flash": {
|
||||
maxTokens: 16_384, // 16K max output
|
||||
contextWindow: 128_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
preserveReasoning: true,
|
||||
inputPrice: 1.0, // $1.00 per million tokens (cache miss)
|
||||
outputPrice: 4.0, // $4.00 per million tokens
|
||||
cacheWritesPrice: 1.0, // $1.00 per million tokens (cache miss)
|
||||
cacheReadsPrice: 0.25, // $0.25 per million tokens (cache hit)
|
||||
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.`,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
||||
// https://api-docs.deepseek.com/quick_start/parameter_settings
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ vi.mock("openai", () => {
|
|||
}
|
||||
}
|
||||
|
||||
// Check if this is a reasoning_content test by looking at model
|
||||
const isReasonerModel = options.model?.includes("deepseek-reasoner")
|
||||
// Check if this is a thinking model - matches models with preserveReasoning: true
|
||||
// (deepseek-reasoner, deepseek-v4-pro, deepseek-v4-flash)
|
||||
const isReasonerModel =
|
||||
options.model?.includes("deepseek-reasoner") || options.model?.includes("deepseek-v4-")
|
||||
const isToolCallTest = options.tools?.length > 0
|
||||
|
||||
// Return async iterator for streaming
|
||||
|
|
@ -122,7 +124,7 @@ vi.mock("openai", () => {
|
|||
import OpenAI from "openai"
|
||||
import type { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
import { deepSeekDefaultModelId, DEEP_SEEK_DEFAULT_TEMPERATURE, type ModelInfo } from "@roo-code/types"
|
||||
import { deepSeekDefaultModelId, deepSeekModels, DEEP_SEEK_DEFAULT_TEMPERATURE, type ModelInfo } from "@roo-code/types"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../../shared/api"
|
||||
|
||||
|
|
@ -247,6 +249,36 @@ describe("DeepSeekHandler", () => {
|
|||
expect((model.info as ModelInfo).preserveReasoning).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should return correct model info for deepseek-v4-pro", () => {
|
||||
const handlerWithV4Pro = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "deepseek-v4-pro",
|
||||
})
|
||||
const model = handlerWithV4Pro.getModel()
|
||||
expect(model.id).toBe("deepseek-v4-pro")
|
||||
expect(model.info).toBeDefined()
|
||||
expect(model.info.maxTokens).toBe(16_384)
|
||||
expect(model.info.contextWindow).toBe(128_000)
|
||||
expect(model.info.supportsImages).toBe(true)
|
||||
expect(model.info.supportsPromptCache).toBe(true)
|
||||
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
|
||||
})
|
||||
|
||||
it("should return correct model info for deepseek-v4-flash", () => {
|
||||
const handlerWithV4Flash = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "deepseek-v4-flash",
|
||||
})
|
||||
const model = handlerWithV4Flash.getModel()
|
||||
expect(model.id).toBe("deepseek-v4-flash")
|
||||
expect(model.info).toBeDefined()
|
||||
expect(model.info.maxTokens).toBe(16_384)
|
||||
expect(model.info.contextWindow).toBe(128_000)
|
||||
expect(model.info.supportsImages).toBe(true)
|
||||
expect(model.info.supportsPromptCache).toBe(true)
|
||||
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
|
||||
})
|
||||
|
||||
it("should return provided model ID with default model info if model does not exist", () => {
|
||||
const handlerWithInvalidModel = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
|
|
@ -475,6 +507,61 @@ describe("DeepSeekHandler", () => {
|
|||
expect(callArgs.thinking).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should pass thinking parameter for deepseek-v4-pro model", async () => {
|
||||
const v4ProHandler = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "deepseek-v4-pro",
|
||||
})
|
||||
|
||||
const stream = v4ProHandler.createMessage(systemPrompt, messages)
|
||||
for await (const _chunk of stream) {
|
||||
// Consume the stream
|
||||
}
|
||||
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
thinking: { type: "enabled" },
|
||||
}),
|
||||
{},
|
||||
)
|
||||
})
|
||||
|
||||
it("should pass thinking parameter for deepseek-v4-flash model", async () => {
|
||||
const v4FlashHandler = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "deepseek-v4-flash",
|
||||
})
|
||||
|
||||
const stream = v4FlashHandler.createMessage(systemPrompt, messages)
|
||||
for await (const _chunk of stream) {
|
||||
// Consume the stream
|
||||
}
|
||||
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
thinking: { type: "enabled" },
|
||||
}),
|
||||
{},
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle reasoning_content in streaming responses for deepseek-v4-pro", async () => {
|
||||
const v4ProHandler = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "deepseek-v4-pro",
|
||||
})
|
||||
|
||||
const stream = v4ProHandler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
const reasoningChunks = chunks.filter((chunk) => chunk.type === "reasoning")
|
||||
expect(reasoningChunks.length).toBeGreaterThan(0)
|
||||
expect(reasoningChunks[0].text).toBe("Let me think about this...")
|
||||
})
|
||||
|
||||
it("should handle tool calls with reasoning_content", async () => {
|
||||
const reasonerHandler = new DeepSeekHandler({
|
||||
...mockOptions,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
deepSeekDefaultModelId,
|
||||
DEEP_SEEK_DEFAULT_TEMPERATURE,
|
||||
OPENAI_AZURE_AI_INFERENCE_PATH,
|
||||
type ModelInfo,
|
||||
} from "@roo-code/types"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../shared/api"
|
||||
|
|
@ -55,8 +56,9 @@ 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 using the preserveReasoning flag from ModelInfo.
|
||||
// This covers deepseek-reasoner, deepseek-v4-pro, deepseek-v4-flash, and any future thinking models.
|
||||
const isThinkingModel = !!(modelInfo as ModelInfo).preserveReasoning
|
||||
|
||||
// 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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue