diff --git a/docs/providers/deepseek.md b/docs/providers/deepseek.md
index b3219b58236c..9ab3f4e81c13 100644
--- a/docs/providers/deepseek.md
+++ b/docs/providers/deepseek.md
@@ -80,8 +80,9 @@ available to that process (for example, in `~/.openclaw/.env` or via
| Model ref | Name | Input | Context | Max output | Notes |
| --------------------------------------- | --------------------------------------- | ----------- | --------- | ---------- | -------------------------------- |
+| `deepseek/deepseek-flash` | DeepSeek V4.1 Flash | text, image | 1,000,000 | 384,000 | Canonical Flash model |
| `deepseek/deepseek-v4-flash` | DeepSeek V4 Flash | text | 1,000,000 | 384,000 | Fast V4 thinking-capable surface |
-| `deepseek/deepseek-v4-pro` | DeepSeek V4 Pro | text | 1,000,000 | 384,000 | Default; strongest V4 model |
+| `deepseek/deepseek-v4-pro` | DeepSeek V4 Pro | text | 1,000,000 | 384,000 | Onboarding default |
| `deepseek/deepseek-v4-flash-vision-exp` | DeepSeek V4 Flash Vision (Experimental) | text, image | 1,000,000 | 384,000 | Experimental image understanding |
@@ -90,19 +91,24 @@ DeepSeek retired `deepseek-chat` and `deepseek-reasoner` on July 24, 2026 at
to `deepseek/deepseek-v4-flash` or `deepseek/deepseek-v4-pro`.
-OpenClaw's local costs are estimates. The vision model's bundled estimate uses
-DeepSeek's peak rates; its published off-peak rates are half those amounts.
+OpenClaw's local costs are estimates. Canonical Flash uses DeepSeek's peak
+rates: $0.30 per million input tokens, $1.20 per million output tokens, and
+$0.006 per million cached input tokens. Published off-peak rates are half those amounts.
+Legacy rows retain their earlier bundled metadata. DeepSeek still accepts
+`deepseek-v4-flash` and `deepseek-v4-flash-vision-exp`, routes them to V4.1 Flash,
+and bills them at current Flash rates. Existing explicit selections remain valid in OpenClaw.
DeepSeek can change rates; its
[Models & Pricing](https://api-docs.deepseek.com/quick_start/pricing/) page is
authoritative for billing.
-For image inputs, select `deepseek/deepseek-v4-flash-vision-exp`. The regular
-Flash and Pro models are text-only. DeepSeek's experimental vision model accepts
+For image inputs, select `deepseek/deepseek-flash`. The legacy
+`deepseek/deepseek-v4-flash-vision-exp` selection also retains image support.
+Canonical Flash accepts
PNG, JPEG, GIF, and WebP images through the same API and API key. See
[DeepSeek vision](https://api-docs.deepseek.com/guides/vision) for image limits.
-V4 models support DeepSeek's `thinking` control. OpenClaw also replays
+Canonical Flash and V4 models support DeepSeek's `thinking` control. OpenClaw also replays
DeepSeek `reasoning_content` on follow-up turns so thinking sessions with tool
calls can continue.
Use `/think xhigh` or `/think max` with DeepSeek V4 models to request DeepSeek's
@@ -114,7 +120,7 @@ maximum `reasoning_effort`; both map to `"max"`.
DeepSeek V4 thinking sessions require replayed assistant messages from a
thinking-enabled turn to include `reasoning_content` on follow-up requests.
OpenClaw's DeepSeek plugin backfills that field automatically, so normal
-multi-turn tool use works on `deepseek/deepseek-v4-flash`,
+multi-turn tool use works on `deepseek/deepseek-flash`, `deepseek/deepseek-v4-flash`,
`deepseek/deepseek-v4-flash-vision-exp`, and `deepseek/deepseek-v4-pro` even when history came from another
OpenAI-compatible provider (no native `reasoning_content`) or from a plain
assistant message. No `/new` required after switching providers mid-session.
@@ -123,9 +129,11 @@ When thinking is disabled (including the UI **None** selection), OpenClaw
sends `thinking: { type: "disabled" }` and strips replayed `reasoning_content`
from outgoing history, keeping the session on the non-thinking DeepSeek path.
-Fresh onboarding selects the stronger `deepseek/deepseek-v4-pro` model. Use
-`deepseek/deepseek-v4-flash` when lower cost or latency matters more than
-maximum capability.
+Fresh onboarding selects `deepseek/deepseek-v4-pro`. To select canonical Flash:
+
+```bash
+openclaw models set deepseek/deepseek-flash
+```
## Live testing
diff --git a/extensions/deepseek/index.test.ts b/extensions/deepseek/index.test.ts
index 86a84278792b..828f1744e4bf 100644
--- a/extensions/deepseek/index.test.ts
+++ b/extensions/deepseek/index.test.ts
@@ -170,6 +170,7 @@ describe("deepseek provider plugin", () => {
"deepseek-v4-flash",
"deepseek-v4-pro",
"deepseek-v4-flash-vision-exp",
+ "deepseek-flash",
]);
const flashModel = catalogProvider.models?.find((model) => model.id === "deepseek-v4-flash");
expect(flashModel?.reasoning).toBe(true);
@@ -189,6 +190,11 @@ describe("deepseek provider plugin", () => {
]),
),
).toEqual({
+ "deepseek-flash": {
+ contextWindow: 1_000_000,
+ maxTokens: 384_000,
+ cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0 },
+ },
"deepseek-v4-flash": {
contextWindow: 1_000_000,
maxTokens: 384_000,
@@ -308,6 +314,7 @@ describe("deepseek provider plugin", () => {
const expectedV4Levels = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
for (const modelId of [
+ "deepseek-flash",
"deepseek-v4-flash",
"deepseek-v4-pro",
"deepseek-v4-flash-vision-exp",
@@ -324,60 +331,62 @@ describe("deepseek provider plugin", () => {
).toBe(undefined);
});
- it.each(["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-flash-vision-exp"])(
- "maps thinking levels to %s payload controls",
- async (modelId) => {
- let capturedPayload: Record | undefined;
- const baseStreamFn = (
- _model: Model<"openai-completions">,
- _context: Context,
- options?: { onPayload?: (payload: unknown) => unknown },
- ) => {
- capturedPayload = {
- model: "deepseek-v4-pro",
- reasoning_effort: "high",
- };
- options?.onPayload?.(capturedPayload);
- const stream = createAssistantMessageEventStream();
- queueMicrotask(() => stream.end());
- return stream;
+ it.each([
+ "deepseek-flash",
+ "deepseek-v4-flash",
+ "deepseek-v4-pro",
+ "deepseek-v4-flash-vision-exp",
+ ])("maps thinking levels to %s payload controls", async (modelId) => {
+ let capturedPayload: Record | undefined;
+ const baseStreamFn = (
+ _model: Model<"openai-completions">,
+ _context: Context,
+ options?: { onPayload?: (payload: unknown) => unknown },
+ ) => {
+ capturedPayload = {
+ model: "deepseek-v4-pro",
+ reasoning_effort: "high",
};
+ options?.onPayload?.(capturedPayload);
+ const stream = createAssistantMessageEventStream();
+ queueMicrotask(() => stream.end());
+ return stream;
+ };
- const wrapThinkingOff = expectDefined(
- createDeepSeekV4ThinkingWrapper(baseStreamFn as never, "off"),
- "DeepSeek thinking wrapper for off",
- );
- await wrapThinkingOff(
- {
- provider: "deepseek",
- id: modelId,
- api: "openai-completions",
- } as never,
- { messages: [] } as never,
- {},
- );
+ const wrapThinkingOff = expectDefined(
+ createDeepSeekV4ThinkingWrapper(baseStreamFn as never, "off"),
+ "DeepSeek thinking wrapper for off",
+ );
+ await wrapThinkingOff(
+ {
+ provider: "deepseek",
+ id: modelId,
+ api: "openai-completions",
+ } as never,
+ { messages: [] } as never,
+ {},
+ );
- expect(readThinking(capturedPayload)?.type).toBe("disabled");
- expect(capturedPayload).not.toHaveProperty("reasoning_effort");
+ expect(readThinking(capturedPayload)?.type).toBe("disabled");
+ expect(capturedPayload).not.toHaveProperty("reasoning_effort");
- const wrapThinkingXhigh = expectDefined(
- createDeepSeekV4ThinkingWrapper(baseStreamFn as never, "xhigh"),
- "DeepSeek thinking wrapper for xhigh",
- );
- await wrapThinkingXhigh(
- {
- provider: "deepseek",
- id: modelId,
- api: "openai-completions",
- } as never,
- { messages: [] } as never,
- {},
- );
+ const wrapThinkingXhigh = expectDefined(
+ createDeepSeekV4ThinkingWrapper(baseStreamFn as never, "xhigh"),
+ "DeepSeek thinking wrapper for xhigh",
+ );
+ await wrapThinkingXhigh(
+ {
+ provider: "deepseek",
+ id: modelId,
+ api: "openai-completions",
+ } as never,
+ { messages: [] } as never,
+ {},
+ );
- expect(readThinking(capturedPayload)?.type).toBe("enabled");
- expect(capturedPayload?.reasoning_effort).toBe("max");
- },
- );
+ expect(readThinking(capturedPayload)?.type).toBe("enabled");
+ expect(capturedPayload?.reasoning_effort).toBe("max");
+ });
it.each(["deepseek-v4-flash", "deepseek-v4-flash-vision-exp"])(
"preserves replayed reasoning_content for %s",
@@ -406,40 +415,43 @@ describe("deepseek provider plugin", () => {
},
);
- it("keeps image input in requests for the bundled vision model", () => {
- const provider = buildDeepSeekProvider();
- const entry = provider.models?.find((model) => model.id === "deepseek-v4-flash-vision-exp");
- expect(entry).toMatchObject({ reasoning: true, input: ["text", "image"] });
- const model = {
- ...entry,
- provider: "deepseek",
- baseUrl: provider.baseUrl,
- api: "openai-completions",
- } as OpenAICompletionsModel;
- const payload = buildOpenAICompletionsParams(
- model,
- {
- messages: [
- {
- role: "user",
- content: [
- { type: "text", text: "Describe this image." },
- { type: "image", data: "fixture-image", mimeType: "image/png" },
- ],
- timestamp: 1,
- },
+ it.each(["deepseek-flash", "deepseek-v4-flash-vision-exp"])(
+ "keeps image input in requests for %s",
+ (modelId) => {
+ const provider = buildDeepSeekProvider();
+ const entry = provider.models?.find((model) => model.id === modelId);
+ expect(entry).toMatchObject({ reasoning: true, input: ["text", "image"] });
+ const model = {
+ ...entry,
+ provider: "deepseek",
+ baseUrl: provider.baseUrl,
+ api: "openai-completions",
+ } as OpenAICompletionsModel;
+ const payload = buildOpenAICompletionsParams(
+ model,
+ {
+ messages: [
+ {
+ role: "user",
+ content: [
+ { type: "text", text: "Describe this image." },
+ { type: "image", data: "fixture-image", mimeType: "image/png" },
+ ],
+ timestamp: 1,
+ },
+ ],
+ },
+ {},
+ );
+ expect(payload.messages).toContainEqual({
+ role: "user",
+ content: [
+ { type: "text", text: "Describe this image." },
+ { type: "image_url", image_url: { url: "data:image/png;base64,fixture-image" } },
],
- },
- {},
- );
- expect(payload.messages).toContainEqual({
- role: "user",
- content: [
- { type: "text", text: "Describe this image." },
- { type: "image_url", image_url: { url: "data:image/png;base64,fixture-image" } },
- ],
- });
- });
+ });
+ },
+ );
it("adds blank reasoning_content for replayed tool calls from non-DeepSeek turns", async () => {
const capture: PayloadCapture = {};
diff --git a/extensions/deepseek/models.ts b/extensions/deepseek/models.ts
index 243150d588a6..7ee830365873 100644
--- a/extensions/deepseek/models.ts
+++ b/extensions/deepseek/models.ts
@@ -12,7 +12,9 @@ export const DEEPSEEK_MODEL_CATALOG: ModelDefinitionConfig[] = buildManifestMode
}).models.map((model) => Object.assign(model, { api: "openai-completions" }));
const DEEPSEEK_V4_MODEL_IDS = new Set(
- DEEPSEEK_MODEL_CATALOG.map((model) => model.id).filter((id) => id.startsWith("deepseek-v4-")),
+ DEEPSEEK_MODEL_CATALOG.map((model) => model.id).filter(
+ (id) => id === "deepseek-flash" || id.startsWith("deepseek-v4-"),
+ ),
);
export function isDeepSeekV4ModelId(modelId: string): boolean {
diff --git a/extensions/deepseek/openclaw.plugin.json b/extensions/deepseek/openclaw.plugin.json
index 70b49efafae6..b280305559d9 100644
--- a/extensions/deepseek/openclaw.plugin.json
+++ b/extensions/deepseek/openclaw.plugin.json
@@ -92,6 +92,26 @@
"maxTokensField": "max_tokens",
"codeMode": "preferred"
}
+ },
+ {
+ "id": "deepseek-flash",
+ "name": "DeepSeek V4.1 Flash",
+ "reasoning": true,
+ "input": ["text", "image"],
+ "contextWindow": 1000000,
+ "maxTokens": 384000,
+ "cost": {
+ "input": 0.3,
+ "output": 1.2,
+ "cacheRead": 0.006,
+ "cacheWrite": 0
+ },
+ "compat": {
+ "supportsUsageInStreaming": true,
+ "supportsReasoningEffort": true,
+ "maxTokensField": "max_tokens",
+ "codeMode": "preferred"
+ }
}
]
}