Skip to content

Commit a9cf1d3

Browse files
authored
fix: update Deepseek configurations (#1083)
* fix: update Deepseek configurations * fix: restore the "disable" reasoning effort option * fix: remove deepseek-chat and deekseek-reasoner tests, or change them to deepseek-v4-flash or deepseek-v4-pro * fix: normalizeDeepSeekReasoningEffort * chore: add unit testing for normalizeDeepSeekReasoningEffort
1 parent 7ed3d82 commit a9cf1d3

5 files changed

Lines changed: 141 additions & 146 deletions

File tree

packages/types/src/providers/deepseek.ts

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,56 +14,32 @@ export const deepSeekModels = {
1414
contextWindow: 1_000_000,
1515
supportsImages: true,
1616
supportsPromptCache: true,
17-
supportsReasoningEffort: ["disable", "low", "medium", "high", "xhigh"],
17+
supportsReasoningEffort: ["disable", "low", "high", "max"], // Updated 2026-08-01
1818
preserveReasoning: true,
1919
reasoningEffort: "high",
20-
inputPrice: 0.14, // $0.14 per million tokens (cache miss) - Updated Apr 29, 2026
21-
outputPrice: 0.28, // $0.28 per million tokens - Updated Apr 29, 2026
22-
cacheWritesPrice: 0.14, // $0.14 per million tokens (cache miss) - Updated Apr 29, 2026
23-
cacheReadsPrice: 0.0028, // $0.0028 per million tokens (cache hit) - Updated Apr 29, 2026
20+
inputPrice: 0, // the inputs are priced as cache read/write, so `inputPrice` should be 0
21+
// the peak/off-peak pricing policy has not been implemented yet - Updated 2026-08-01
22+
outputPrice: 0.28, // $0.28 per million tokens - Updated 2026-08-01
23+
cacheWritesPrice: 0.14, // $0.14 per million tokens (cache miss) - Updated 2026-08-01
24+
cacheReadsPrice: 0.0028, // $0.0028 per million tokens (cache hit) - Updated 2026-08-01
2425
description: `DeepSeek-V4-Flash is DeepSeek's fast, cost-efficient V4 model. It supports thinking and non-thinking modes, JSON output, tool calls, chat prefix completion (beta), and FIM completion (beta) in non-thinking mode.`,
2526
},
2627
"deepseek-v4-pro": {
2728
maxTokens: 384_000,
2829
contextWindow: 1_000_000,
2930
supportsImages: true,
3031
supportsPromptCache: true,
31-
supportsReasoningEffort: ["disable", "low", "medium", "high", "xhigh"],
32+
supportsReasoningEffort: ["disable", "high", "max"], // Updated 2026-08-01
3233
preserveReasoning: true,
3334
reasoningEffort: "high",
34-
// TODO(deepseek): Re-check V4 Pro discounted prices after DeepSeek's 2026-05-31 discount end date.
35-
inputPrice: 0.435, // $0.435 per million tokens (cache miss, discounted) - Updated Apr 29, 2026
36-
outputPrice: 0.87, // $0.87 per million tokens (discounted) - Updated Apr 29, 2026
37-
cacheWritesPrice: 0.435, // $0.435 per million tokens (cache miss, discounted) - Updated Apr 29, 2026
38-
cacheReadsPrice: 0.003625, // $0.003625 per million tokens (cache hit, discounted) - Updated Apr 29, 2026
35+
inputPrice: 0, // the inputs are priced as cache read/write, so `inputPrice` should be 0
36+
// the peak/off-peak pricing policy has not been implemented yet - Updated 2026-08-01
37+
outputPrice: 0.87, // $0.87 per million tokens - Updated 2026-08-01
38+
cacheWritesPrice: 0.435, // $0.435 per million tokens (cache miss) - Updated 2026-08-01
39+
cacheReadsPrice: 0.003625, // $0.003625 per million tokens (cache hit) - Updated 2026-08-01
3940
description: `DeepSeek-V4-Pro is DeepSeek's strongest V4 model for reasoning, coding, long-context, and agentic workloads. It supports thinking and non-thinking modes, JSON output, tool calls, chat prefix completion (beta), and FIM completion (beta) in non-thinking mode.`,
4041
},
41-
// TODO(deepseek): Remove this compatibility alias after DeepSeek's 2026-07-24 retirement date.
42-
"deepseek-chat": {
43-
maxTokens: 8192, // 8K max output
44-
contextWindow: 128_000,
45-
supportsImages: false,
46-
supportsPromptCache: true,
47-
inputPrice: 0.28, // $0.28 per million tokens (cache miss) - Updated Dec 9, 2025
48-
outputPrice: 0.42, // $0.42 per million tokens - Updated Dec 9, 2025
49-
cacheWritesPrice: 0.28, // $0.28 per million tokens (cache miss) - Updated Dec 9, 2025
50-
cacheReadsPrice: 0.028, // $0.028 per million tokens (cache hit) - Updated Dec 9, 2025
51-
description: `Legacy compatibility alias for the non-thinking mode of deepseek-v4-flash. DeepSeek plans to deprecate this model name on 2026-07-24.`,
52-
},
53-
// TODO(deepseek): Remove this compatibility alias after DeepSeek's 2026-07-24 retirement date.
54-
"deepseek-reasoner": {
55-
maxTokens: 8192, // 8K max output
56-
contextWindow: 128_000,
57-
supportsImages: false,
58-
supportsPromptCache: true,
59-
preserveReasoning: true,
60-
inputPrice: 0.28, // $0.28 per million tokens (cache miss) - Updated Dec 9, 2025
61-
outputPrice: 0.42, // $0.42 per million tokens - Updated Dec 9, 2025
62-
cacheWritesPrice: 0.28, // $0.28 per million tokens (cache miss) - Updated Dec 9, 2025
63-
cacheReadsPrice: 0.028, // $0.028 per million tokens (cache hit) - Updated Dec 9, 2025
64-
description: `Legacy compatibility alias for the thinking mode of deepseek-v4-flash. DeepSeek plans to deprecate this model name on 2026-07-24.`,
65-
},
6642
} as const satisfies Record<string, ModelInfo>
6743

6844
// https://api-docs.deepseek.com/quick_start/parameter_settings
69-
export const DEEP_SEEK_DEFAULT_TEMPERATURE = 0.3
45+
export const DEEP_SEEK_DEFAULT_TEMPERATURE = 0.0

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

Lines changed: 81 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,11 @@ vi.mock("openai", () => {
124124
import OpenAI from "openai"
125125
import type { Anthropic } from "@anthropic-ai/sdk"
126126

127-
import { deepSeekDefaultModelId, DEEP_SEEK_DEFAULT_TEMPERATURE, type ModelInfo } from "@roo-code/types"
127+
import { deepSeekDefaultModelId, DEEP_SEEK_DEFAULT_TEMPERATURE, type ModelInfo, DeepSeekModelId } from "@roo-code/types"
128128

129129
import type { ApiHandlerOptions } from "../../../shared/api"
130130

131-
import { DeepSeekHandler } from "../deepseek"
131+
import { DeepSeekHandler, normalizeDeepSeekReasoningEffort } from "../deepseek"
132132

133133
describe("DeepSeekHandler", () => {
134134
let handler: DeepSeekHandler
@@ -137,7 +137,7 @@ describe("DeepSeekHandler", () => {
137137
beforeEach(() => {
138138
mockOptions = {
139139
deepSeekApiKey: "test-api-key",
140-
apiModelId: "deepseek-chat",
140+
apiModelId: "deepseek-v4-flash",
141141
deepSeekBaseUrl: "https://api.deepseek.com",
142142
}
143143
handler = new DeepSeekHandler(mockOptions)
@@ -208,11 +208,11 @@ describe("DeepSeekHandler", () => {
208208
const model = handler.getModel()
209209
expect(model.id).toBe(mockOptions.apiModelId)
210210
expect(model.info).toBeDefined()
211-
expect(model.info.maxTokens).toBe(8192) // deepseek-chat legacy alias has 8K max
212-
expect(model.info.contextWindow).toBe(128_000)
213-
expect(model.info.supportsImages).toBe(false)
211+
expect(model.info.maxTokens).toBe(384_000)
212+
expect(model.info.contextWindow).toBe(1_000_000)
213+
expect(model.info.supportsImages).toBe(true)
214214
expect(model.info.supportsPromptCache).toBe(true) // Should be true now
215-
expect((model.info as ModelInfo).preserveReasoning).toBeUndefined()
215+
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
216216
})
217217

218218
it("should use deepseek-v4-flash as the default model ID for new configs", () => {
@@ -226,21 +226,7 @@ describe("DeepSeekHandler", () => {
226226
expect(model.info.maxTokens).toBe(384_000)
227227
expect(model.info.contextWindow).toBe(1_000_000)
228228
expect(model.info.supportsImages).toBe(true)
229-
expect((model.info as ModelInfo).supportsReasoningEffort).toContain("xhigh")
230-
})
231-
232-
it("should return correct model info for deepseek-reasoner", () => {
233-
const handlerWithReasoner = new DeepSeekHandler({
234-
...mockOptions,
235-
apiModelId: "deepseek-reasoner",
236-
})
237-
const model = handlerWithReasoner.getModel()
238-
expect(model.id).toBe("deepseek-reasoner")
239-
expect(model.info).toBeDefined()
240-
expect(model.info.maxTokens).toBe(8192) // deepseek-reasoner has 8K max
241-
expect(model.info.contextWindow).toBe(128_000)
242-
expect(model.info.supportsImages).toBe(false)
243-
expect(model.info.supportsPromptCache).toBe(true)
229+
expect((model.info as ModelInfo).supportsReasoningEffort).toContain("max")
244230
})
245231

246232
it("should return correct model info for deepseek-v4-pro", () => {
@@ -259,31 +245,6 @@ describe("DeepSeekHandler", () => {
259245
expect((model.info as ModelInfo).reasoningEffort).toBe("high")
260246
})
261247

262-
it("should have preserveReasoning enabled for deepseek-reasoner to support interleaved thinking", () => {
263-
// This is critical for DeepSeek's interleaved thinking mode with tool calls.
264-
// See: https://api-docs.deepseek.com/guides/thinking_mode
265-
// The reasoning_content needs to be passed back during tool call continuation
266-
// within the same turn for the model to continue reasoning properly.
267-
const handlerWithReasoner = new DeepSeekHandler({
268-
...mockOptions,
269-
apiModelId: "deepseek-reasoner",
270-
})
271-
const model = handlerWithReasoner.getModel()
272-
// Cast to ModelInfo to access preserveReasoning which is an optional property
273-
expect((model.info as ModelInfo).preserveReasoning).toBe(true)
274-
})
275-
276-
it("should NOT have preserveReasoning enabled for deepseek-chat", () => {
277-
// deepseek-chat doesn't use thinking mode, so no need to preserve reasoning
278-
const chatHandler = new DeepSeekHandler({
279-
...mockOptions,
280-
apiModelId: "deepseek-chat",
281-
})
282-
const model = chatHandler.getModel()
283-
// Cast to ModelInfo to access preserveReasoning which is an optional property
284-
expect((model.info as ModelInfo).preserveReasoning).toBeUndefined()
285-
})
286-
287248
it("should return provided model ID with default model info if model does not exist", () => {
288249
const handlerWithInvalidModel = new DeepSeekHandler({
289250
...mockOptions,
@@ -540,10 +501,10 @@ describe("DeepSeekHandler", () => {
540501
},
541502
]
542503

543-
it("should handle reasoning_content in streaming responses for deepseek-reasoner", async () => {
504+
it("should handle reasoning_content in streaming responses for deepseek-v4-pro", async () => {
544505
const reasonerHandler = new DeepSeekHandler({
545506
...mockOptions,
546-
apiModelId: "deepseek-reasoner",
507+
apiModelId: "deepseek-v4-pro",
547508
})
548509

549510
const stream = reasonerHandler.createMessage(systemPrompt, messages)
@@ -559,10 +520,10 @@ describe("DeepSeekHandler", () => {
559520
expect(reasoningChunks[1].text).toBe(" I'll analyze step by step.")
560521
})
561522

562-
it("should pass thinking parameter for deepseek-reasoner model", async () => {
523+
it("should pass thinking parameter for deepseek-v4-pro model", async () => {
563524
const reasonerHandler = new DeepSeekHandler({
564525
...mockOptions,
565-
apiModelId: "deepseek-reasoner",
526+
apiModelId: "deepseek-v4-pro",
566527
})
567528

568529
const stream = reasonerHandler.createMessage(systemPrompt, messages)
@@ -579,7 +540,7 @@ describe("DeepSeekHandler", () => {
579540
{}, // Empty path options for non-Azure URLs
580541
)
581542
const callArgs = mockCreate.mock.calls[0][0]
582-
expect(callArgs.reasoning_effort).toBeUndefined()
543+
expect(callArgs.reasoning_effort).toBe("high")
583544
})
584545

585546
it("should enable thinking by default for deepseek-v4-flash", async () => {
@@ -619,27 +580,6 @@ describe("DeepSeekHandler", () => {
619580
expect(callArgs.max_completion_tokens).toBe(32_000)
620581
})
621582

622-
it("should map xhigh reasoning effort to DeepSeek max effort", async () => {
623-
const v4Handler = new DeepSeekHandler({
624-
...mockOptions,
625-
apiModelId: "deepseek-v4-pro",
626-
reasoningEffort: "xhigh",
627-
})
628-
629-
const stream = v4Handler.createMessage(systemPrompt, messages)
630-
for await (const _chunk of stream) {
631-
// Consume the stream
632-
}
633-
634-
expect(mockCreate).toHaveBeenCalledWith(
635-
expect.objectContaining({
636-
thinking: { type: "enabled" },
637-
reasoning_effort: "max",
638-
}),
639-
{},
640-
)
641-
})
642-
643583
it("should disable thinking for deepseek-v4 models when reasoning is disabled", async () => {
644584
const v4Handler = new DeepSeekHandler({
645585
...mockOptions,
@@ -674,26 +614,10 @@ describe("DeepSeekHandler", () => {
674614
expect(callArgs.temperature).toBe(DEEP_SEEK_DEFAULT_TEMPERATURE)
675615
})
676616

677-
it("should NOT pass thinking parameter for deepseek-chat model", async () => {
678-
const chatHandler = new DeepSeekHandler({
679-
...mockOptions,
680-
apiModelId: "deepseek-chat",
681-
})
682-
683-
const stream = chatHandler.createMessage(systemPrompt, messages)
684-
for await (const _chunk of stream) {
685-
// Consume the stream
686-
}
687-
688-
// Verify that the thinking parameter was NOT passed to the API
689-
const callArgs = mockCreate.mock.calls[0][0]
690-
expect(callArgs.thinking).toBeUndefined()
691-
})
692-
693617
it("should handle tool calls with reasoning_content", async () => {
694618
const reasonerHandler = new DeepSeekHandler({
695619
...mockOptions,
696-
apiModelId: "deepseek-reasoner",
620+
apiModelId: "deepseek-v4-pro",
697621
})
698622

699623
const tools: any[] = [
@@ -723,4 +647,71 @@ describe("DeepSeekHandler", () => {
723647
expect(toolCallChunks[0].name).toBe("get_weather")
724648
})
725649
})
650+
651+
describe("normalizeDeepSeekReasoningEffort", () => {
652+
// https://api-docs.deepseek.com/guides/thinking_mode/
653+
it("should map acceptable reasoning efforts the same way as stated by the official documentation", async () => {
654+
const mappings: {
655+
modelId: DeepSeekModelId
656+
rawReasoningEffort: string
657+
mappedReasoningEffort: string | undefined
658+
}[] = [
659+
{
660+
modelId: "deepseek-v4-flash",
661+
rawReasoningEffort: "disable",
662+
mappedReasoningEffort: undefined,
663+
},
664+
{
665+
modelId: "deepseek-v4-flash",
666+
rawReasoningEffort: "low",
667+
mappedReasoningEffort: "low",
668+
},
669+
{
670+
modelId: "deepseek-v4-flash",
671+
rawReasoningEffort: "high",
672+
mappedReasoningEffort: "high",
673+
},
674+
{
675+
modelId: "deepseek-v4-flash",
676+
rawReasoningEffort: "xhigh",
677+
mappedReasoningEffort: "high",
678+
},
679+
{
680+
modelId: "deepseek-v4-flash",
681+
rawReasoningEffort: "max",
682+
mappedReasoningEffort: "max",
683+
},
684+
{
685+
modelId: "deepseek-v4-pro",
686+
rawReasoningEffort: "disable",
687+
mappedReasoningEffort: undefined,
688+
},
689+
{
690+
modelId: "deepseek-v4-pro",
691+
rawReasoningEffort: "low",
692+
mappedReasoningEffort: "high",
693+
},
694+
{
695+
modelId: "deepseek-v4-pro",
696+
rawReasoningEffort: "high",
697+
mappedReasoningEffort: "high",
698+
},
699+
{
700+
modelId: "deepseek-v4-pro",
701+
rawReasoningEffort: "xhigh",
702+
mappedReasoningEffort: "max",
703+
},
704+
{
705+
modelId: "deepseek-v4-pro",
706+
rawReasoningEffort: "max",
707+
mappedReasoningEffort: "max",
708+
},
709+
]
710+
711+
for (const { modelId, rawReasoningEffort, mappedReasoningEffort } of mappings) {
712+
const result = normalizeDeepSeekReasoningEffort(modelId, rawReasoningEffort)
713+
expect(result).toBe(mappedReasoningEffort)
714+
}
715+
})
716+
})
726717
})

0 commit comments

Comments
 (0)