Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/types/src/__tests__/deepseek-v4-pro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ describe("DeepSeek V4 Pro 0813 provider catalogs", () => {
})
})

it("marks deepseek-v4-flash-vision-exp as a vision-capable model", () => {
const model = deepSeekModels["deepseek-v4-flash-vision-exp"]
expect(model).toBeDefined()
expect(model.supportsImages).toBe(true)
expect(model.supportsPromptCache).toBe(true)
expect(model.contextWindow).toBeGreaterThanOrEqual(1_000_000)
expect(model.supportsReasoningEffort).toEqual(["disable", "low", "high", "max"])
})

// Self-hosted providers retain separate IDs for the preview weights and 0813 checkpoint.
it.each([
["Fireworks AI", fireworksModels["accounts/fireworks/models/deepseek-v4-pro"]],
Expand Down
16 changes: 16 additions & 0 deletions packages/types/src/providers/deepseek.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ export const deepSeekModels = {
cacheReadsPrice: 0.044,
description: `DeepSeek-V4-Pro-0813 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.`,
},
"deepseek-v4-flash-vision-exp": {
displayName: "DeepSeek V4 Flash Vision Exp",
maxTokens: 384_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoningEffort: ["disable", "low", "high", "max"], // Updated 2026-08-13
preserveReasoning: true,
reasoningEffort: "high",
inputPrice: 0, // the inputs are priced as cache read/write, so `inputPrice` should be 0
// Static estimates use peak rates; off-peak rates are 50% lower.
outputPrice: 1.32,
cacheWritesPrice: 0.44,
cacheReadsPrice: 0.014,
description: `DeepSeek-V4-Flash-Vision-Exp is DeepSeek's experimental multimodal V4 Flash model with image understanding. It supports thinking and non-thinking modes, JSON output, tool calls, chat prefix completion (beta), and image inputs.`,
},
} as const satisfies Record<string, ModelInfo>

// https://api-docs.deepseek.com/quick_start/parameter_settings
Expand Down
71 changes: 71 additions & 0 deletions src/api/providers/__tests__/deepseek.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ describe("DeepSeekHandler", () => {
expect((model.info as ModelInfo).reasoningEffort).toBe("high")
})

it("should return vision model info for deepseek-v4-flash-vision-exp", () => {
const handlerWithVision = new DeepSeekHandler({
...mockOptions,
apiModelId: "deepseek-v4-flash-vision-exp",
})
const model = handlerWithVision.getModel()
expect(model.id).toBe("deepseek-v4-flash-vision-exp")
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,
Expand Down Expand Up @@ -412,6 +424,35 @@ describe("DeepSeekHandler", () => {
const reasoningChunks = chunks.filter((chunk) => chunk.type === "reasoning")
expect(reasoningChunks).toEqual([{ type: "reasoning", text: "primary thought" }])
})

it("enables thinking and forwards image_url for the vision model", async () => {
const visionHandler = new DeepSeekHandler({
...mockOptions,
apiModelId: "deepseek-v4-flash-vision-exp",
})
const withImage: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{ type: "text", text: "Describe:" },
{ type: "image", source: { type: "base64", media_type: "image/png", data: "abc" } },
],
},
]

await collectStream(visionHandler.createMessage(systemPrompt, withImage))

const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.thinking).toEqual({ type: "enabled" })
expect(callArgs.messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
role: "user",
content: expect.arrayContaining([expect.objectContaining({ type: "image_url" })]),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}),
]),
)
})
})

describe("processUsageMetrics", () => {
Expand Down Expand Up @@ -684,6 +725,36 @@ describe("DeepSeekHandler", () => {
rawReasoningEffort: "max",
mappedReasoningEffort: "max",
},
{
modelId: "deepseek-v4-flash-vision-exp",
rawReasoningEffort: "disable",
mappedReasoningEffort: undefined,
},
{
modelId: "deepseek-v4-flash-vision-exp",
rawReasoningEffort: "low",
mappedReasoningEffort: "low",
},
{
modelId: "deepseek-v4-flash-vision-exp",
rawReasoningEffort: "medium",
mappedReasoningEffort: "high",
},
{
modelId: "deepseek-v4-flash-vision-exp",
rawReasoningEffort: "high",
mappedReasoningEffort: "high",
},
{
modelId: "deepseek-v4-flash-vision-exp",
rawReasoningEffort: "xhigh",
mappedReasoningEffort: "high",
},
{
modelId: "deepseek-v4-flash-vision-exp",
rawReasoningEffort: "max",
mappedReasoningEffort: "max",
},
]

for (const { modelId, rawReasoningEffort, mappedReasoningEffort } of mappings) {
Expand Down
3 changes: 2 additions & 1 deletion src/api/providers/deepseek.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type DeepSeekChatCompletionParams = Omit<OpenAI.Chat.ChatCompletionCreateParamsS
reasoning_effort?: "low" | "high" | "max"
}

const deepSeekV4ThinkingModels = new Set(["deepseek-v4-flash", "deepseek-v4-pro"])
const deepSeekV4ThinkingModels = new Set(["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-flash-vision-exp"])
const supportsDeepSeekThinkingToggle = (modelId: string) => deepSeekV4ThinkingModels.has(modelId)

// Only known V4 models and the legacy reasoner alias support DeepSeek's
Expand All @@ -51,6 +51,7 @@ export const normalizeDeepSeekReasoningEffort = (
switch (modelId) {
case "deepseek-v4-flash":
case "deepseek-v4-pro":
case "deepseek-v4-flash-vision-exp":
switch (reasoningEffort) {
case "low":
return "low"
Expand Down
Loading