Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use this skill whenever the user wants to call DeepSeek V4-Pro / V4-Flash (or its legacy aliases deepseek-chat / deepseek-reasoner), or you see code that imports `from openai import OpenAI` with `base_url="https://api.deepseek.com"`. This skill teaches you the 10 protocol contract rules required to avoid the 16 documented bugs (reasoning_content lifecycle 400, tool-call leakage, max_tokens runaway → V8 string limit, parallel tool delta interleave, prefix-cache invalidation, /beta endpoint remap,
.claude/skills/henryz838978-deepseek-harness/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 50% | 0% |
When you call DeepSeek V4-Pro or V4-Flash via the OpenAI-compatible API, you MUST follow the 10 contract rules below. Violating any one of them causes a documented production bug (the upstream issue and our reproduction probe are cited per rule).
deepseek-v4-pro defaults to thinking=enabled. Every call then burns 30-300 reasoning_tokens even on trivial prompts.
pythonclient.chat.completions.create( model="deepseek-v4-pro", messages=messages, extra_body={"thinking": {"type": "disabled"}}, # ← saves money on every non-reasoning call )
For TypeScript / openai-node, put thinking at the top level of the request, not inside extra_body (the JS SDK passes unknown top-level keys through):
tsawait openai.chat.completions.create({ model: "deepseek-v4-pro", messages, thinking: { type: "disabled" }, } as any);
reasoning_content on assistant messagesIf thinking IS enabled and you re-send a prior assistant message that has tool_calls, you must include the original reasoning_content field. Otherwise the next request returns:
HTTP 400: The reasoning_content in the thinking mode must be passed back to the API.(Reproduced in reports/probes/probe_2_reasoning_lifecycle.py 3/3 trials on V4-Pro and V4-Flash, 2026-05-09.)
pythonmsg = response.choices[0].message history.append({ "role": "assistant", "content": msg.content, "tool_calls": _serialize_tool_calls(msg.tool_calls), "reasoning_content": getattr(msg, "reasoning_content", None), # ← REQUIRED })
When a NEW user turn arrives, you MAY strip reasoning_content from prior assistant messages — DeepSeek doesn't require it across user-turn boundaries, and keeping it bloats the prefix-cache key.
max_tokens (default 4096)Without an output cap, reasoning_content can stream 8000+ chunks (probes/probe_9_reasoning_runaway.py measured 26 KB / 84 s on a self-doubt prompt) and downstream Electron clients (ChatWise, Cherry Studio) crash with RangeError: Invalid string length once their string buffer hits V8's 512 MB ceiling.
tool_calls by tc.index, not list orderDeepSeek interleaves chunks across parallel tool calls (probe_7 100% interleave on 3/3 V4-Pro and V4-Flash trials). Use a dict[int, slot]:
pythontool_call_acc: dict[int, dict] = {} for chunk in stream: for tc in (chunk.choices[0].delta.tool_calls or []): slot = tool_call_acc.setdefault(tc.index, {"id": None, "name": None, "arguments": ""}) if tc.id: slot["id"] = tc.id if tc.function and tc.function.name: slot["name"] = tc.function.name if tc.function and tc.function.arguments: slot["arguments"] += tc.function.arguments
"".join, NOT state += chunkDeepSeek streams 1-3 chars per reasoning chunk. state.text += chunk is O(n²) string allocation:
pythonbuf = [] for chunk in stream: if c := (chunk.choices[0].delta.content or ""): buf.append(c) final = "".join(buf)
DeepSeek emits ~3 chunks per response with choices == []. Check truthiness before indexing:
pythonfor chunk in stream: choices = chunk.choices or [] if not choices: if chunk.usage is not None: usage = chunk.usage continue ...
The V4-Pro / V4-Flash hard ceiling is exactly 2^20 = 1,048,576 tokens (probe_6b validated). The server enforces len(messages_tokens) + max_tokens <= 1,048,576 and returns 400 if exceeded.
DeepSeek's prefix cache buckets in 256-token blocks and gives a 50× discount on hits. To maximise:
prompt_cache_hit_tokens (DeepSeek-native) AND prompt_tokens_details.cached_tokens (OpenAI-shape) — both are returned/beta endpoint when calling V4 with tools/beta silently remaps deepseek-v4-pro → legacy deepseek-reasoner, which rejects specific tool_choice={"type":"function","function":{"name":"..."}}. Use https://api.deepseek.com for tool-using flows.
strict: true is empirically OK on V4 (despite #1069 still being open)You MAY enable function.strict=true and additionalProperties=false on V4-Pro / V4-Flash — the historic JSON-corruption bug (deepseek-ai/DeepSeek-V3#1069) was not reproducible in 32 trials on 2026-05-09. Still validate JSON post-hoc with jsonschema.
If the user's environment allows installing third-party libraries, use any of:
| form | install | command | |---|---|---| | Python lib | pip install deepseek-harness | from deepseek_harness import DeepSeekHarness | | Python CLI | pip install deepseek-harness-cli | dsh chat, dsh doctor, dsh validate | | MCP server | npx -y @deepseek-harness/mcp | wire into Claude/Cursor/Cline/ChatWise MCP config |
If they want zero deps beyond openai, copy scripts/safe_init.py (in this skill folder) into their project. It implements all 10 rules in 200 lines.
Every claim in this skill is backed by a runnable probe in reports/probes/:
bash# Reproduce the reasoning_content 400: python reports/probes/probe_2_reasoning_lifecycle.py --n 3 # Expected: 3/3 phase-B BadRequestError with the verbatim error message above. # Confirm tool-call leakage rate at 0% on official endpoint: python reports/probes/probe_3_tool_call_leakage.py --n 30 # Map cache prefix sensitivity: python reports/probes/probe_5_cache_prefix_sensitivity.py --n 8
See reports/REPORT_2026-05-09.md for the full 16-finding report.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→pass | 17,452 | 12,677 | -27% | 1 | 1 | 0% | 3,937 | 4,953 | +26% | 0 | 0 | — |
case-01 | fail→pass | 12,691 | 7,388 | -42% | 1 | 1 | 0% | 2,662 | 3,565 | +34% | 0 | 0 | — |
case-03 | fail→pass | 10,743 | 7,624 | -29% | 1 | 1 | 0% | 2,289 | 3,572 | +56% | 0 | 0 | — |
case-04 | pass→pass | 11,460 | 10,412 | -9% | 1 | 1 | 0% | 2,874 | 4,486 | +56% | 0 | 0 | — |
case-05 | pass→pass | 5,848 | 5,505 | -6% | 1 | 1 | 0% | 1,383 | 3,117 | +125% | 0 | 0 | — |
case-06 | pass→pass | 5,217 | 7,184 | +38% | 1 | 1 | 0% | 1,255 | 3,644 | +190% | 0 | 0 | — |
case-07 | fail→pass | 12,071 | 9,081 | -25% | 1 | 1 | 0% | 2,488 | 3,851 | +55% | 0 | 0 | — |
case-08 | fail→pass | 10,850 | 6,717 | -38% | 1 | 1 | 0% | 2,224 | 3,329 | +50% | 0 | 0 | — |
case-09 | pass→pass | 11,797 | 7,538 | -36% | 1 | 1 | 0% | 2,548 | 3,691 | +45% | 0 | 0 | — |
case-10 | pass→pass | 10,238 | 7,049 | -31% | 1 | 1 | 0% | 2,190 | 3,292 | +50% | 0 | 0 | — |
case-11 | pass→pass | 9,938 | 4,744 | -52% | 1 | 1 | 0% | 1,753 | 2,859 | +63% | 0 | 0 | — |
case-12 | pass→pass | 8,406 | 9,993 | +19% | 1 | 1 | 0% | 1,730 | 4,368 | +152% | 0 | 0 | — |
case-13 | pass→pass | 15,313 | 7,086 | -54% | 1 | 1 | 0% | 2,427 | 3,340 | +38% | 0 | 0 | — |
case-14 | pass→pass | 10,705 | 6,395 | -40% | 1 | 1 | 0% | 2,303 | 3,302 | +43% | 0 | 0 | — |
case-15 | fail→pass | 7,089 | 5,479 | -23% | 1 | 1 | 0% | 1,465 | 2,942 | +101% | 0 | 0 | — |
case-16 | fail→pass | 15,896 | 6,639 | -58% | 1 | 1 | 0% | 2,798 | 3,376 | +21% | 0 | 0 | — |
case-17 | fail→pass | 10,856 | 3,083 | -72% | 1 | 1 | 0% | 2,185 | 2,464 | +13% | 0 | 0 | — |
case-18 | fail→pass | 11,582 | 8,485 | -27% | 1 | 1 | 0% | 2,378 | 3,774 | +59% | 0 | 0 | — |
case-19 | fail→pass | 10,893 | 4,346 | -60% | 1 | 1 | 0% | 2,203 | 2,752 | +25% | 0 | 0 | — |
case-20 | fail→pass | 13,079 | 6,071 | -54% | 1 | 1 | 0% | 2,546 | 3,274 | +29% | 0 | 0 | — |
case-21 | fail→pass | 8,862 | 5,864 | -34% | 1 | 1 | 0% | 1,912 | 3,069 | +61% | 0 | 0 | — |
case-22 | pass→pass | 10,025 | 6,188 | -38% | 1 | 1 | 0% | 1,796 | 3,149 | +75% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.