Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
78 changes: 78 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,42 @@ 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",
image_url: expect.objectContaining({
url: "data:image/png;base64,abc",
}),
}),
]),
}),
]),
)
})
})

describe("processUsageMetrics", () => {
Expand Down Expand Up @@ -684,6 +732,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