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, etc.) and ships a single drop-in `safe_init.py` script you can copy into the user's project.
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, etc.) and ships a single drop-in `safe_init.py` script you can copy into the user's project.
when_to_use
Any of these triggers should invoke this skill:
• user mentions DeepSeek, deepseek-v4-pro, deepseek-v4-flash, deepseek-chat, deepseek-reasoner, or 深度求索
• code references `https://api.deepseek.com` or `https://api.deepseek.com/anthropic` or `/beta`
• user complains about `400 reasoning_content`, `Invalid string length`, `RangeError`, hangs in chat clients, or "weird tool call behaviour"
• user wants the cheapest possible LLM hosting that is also smart (V4-Flash $0.14/M input miss, $0.0028/M cached → 50× discount)
license
MIT
version
0.2.0
author
Henry Zhang (HenryZ838978)
homepage
https://github.com/HenryZ838978/deepseek-harness
DeepSeek V4 Harness
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).
The 10 contract rules (ranked by blast radius)
C1 · Disable thinking unless the task is genuinely reasoning-heavy
deepseek-v4-pro defaults to thinking=enabled. Every call then burns 30-300 reasoning_tokens even on trivial prompts.
client.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):
C2 · In multi-turn loops, preserve reasoning_content on assistant messages
If 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.)
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.
C3 · Always set 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.
C4 · Streaming: aggregate parallel tool_calls by tc.index, not list order
DeepSeek interleaves chunks across parallel tool calls (probe_7 100% interleave on 3/3 V4-Pro and V4-Flash trials). Use a dict[int, slot]:
tool_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.idif tc.function and tc.function.name: slot["name"] = tc.function.name
if tc.function and tc.function.arguments: slot["arguments"] += tc.function.arguments
C5 · Streaming: list buffer + "".join, NOT state += chunk
DeepSeek streams 1-3 chars per reasoning chunk. state.text += chunk is O(n²) string allocation:
buf = []
for chunk in stream:
if c := (chunk.choices[0].delta.content or""):
buf.append(c)
final = "".join(buf)
C6 · Tolerate empty stream chunks
DeepSeek emits ~3 chunks per response with choices == []. Check truthiness before indexing:
for chunk in stream:
choices = chunk.choices or []
ifnot choices:
if chunk.usage isnotNone: usage = chunk.usage
continue
...
C7 · Cap context length under 1,048,576 tokens
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.
C8 · Cache awareness — do NOT inject volatile content into the system prompt
DeepSeek's prefix cache buckets in 256-token blocks and gives a 50× discount on hits. To maximise:
Do NOT prune or summarise history aggressively (every prune = cache miss)
Do NOT inject "current date: 2026-05-09" or similar into the cached prefix
Do read both prompt_cache_hit_tokens (DeepSeek-native) AND prompt_tokens_details.cached_tokens (OpenAI-shape) — both are returned
C9 · Avoid /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.
C10 · 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.
Drop-in implementation
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.
Reproduction commands
Every claim in this skill is backed by a runnable probe in reports/probes/:
# 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