anomalyco / anomalyco/opencode

[Bug] Zen Go /v1/chat/completions strips usage.completion_tokens_details for deepseek-v4-flash - official DeepSeek API sends it

Open
#45,606 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

The Zen Go OpenAI-compatible endpoint (https://opencode.ai/zen/go/v1/chat/completions) strips usage.completion_tokens_details.reasoning_tokens from responses for deepseek-v4-flash, while the official DeepSeek API (https://api.deepseek.com/chat/completions) reports it for the same model id, same prompt, same request shape.

The thinking text itself is forwarded (choices[0].message.reasoning_content is populated), so the model clearly engages reasoning through the gateway — only the token accounting for it disappears. Two additional DeepSeek-native usage fields (prompt_cache_hit_tokens, prompt_cache_miss_tokens) are likewise absent, and usage. prompt_tokens_details arrives hollowed ({} even when populated upstream).

This is a provider-specific gap in the gateway's normalization layer rather than a model-behavior difference: on this exact endpoint/harness/prompt combination, a kimi-k2.6 request returns a fully-populated details block minutes apart from a deepseek-v4-flash request that returns none.

Expected Behavior

Per the official DeepSeek API docs, the response usage should include:

"usage": {
   "completion_tokens": 0,
   "prompt_tokens": 0,
   "prompt_cache_hit_tokens": 0,
   "prompt_cache_miss_tokens": 0,
   "total_tokens": 0,
   "completion_tokens_details": {
     "reasoning_tokens": 0
}

and per the OpenAI chat-completions convention that Zen's own normalization code follows (see PR #24441), completion_tokens_details.reasoning_tokens is part of the standard
usage shape the gateway is expected to forward.

Actual Behavior

"usage": {
  "prompt_tokens": 124,
  "completion_tokens": 198,
  "total_tokens": 322,
  "prompt_tokens_details": {}
}
  • usage.completion_tokens_details is absent entirely on every Zen Go response for deepseek-v4-flash (12+ attempts over two days, at different hours, trivial and reasoning-
    forcing prompts), while message.reasoning_content is delivered.
  • usage.prompt_tokens_details arrives as {} (hollowed) instead of the populated shape the official API sends.
  • prompt_cache_hit_tokens / prompt_cache_miss_tokens are dropped, masking whether DeepSeek prefix caching engaged per request.
  • The same harness seconds apart returns a fully-populated details block for kimi-k2.6 — so the gateway is capable of forwarding it; the gap is specific to the DeepSeek path.
Plugins

No response

OpenCode version

1.18.23

Steps to reproduce

The strip is deterministic across 12+ requests spanning two days, trivial and forcing
prompts, stream=false, at different hours.

  1. Send the identical request body to both endpoints (fixed combinatorial prompt that forces reasoning):
FIXED='Solve step by step internally, then respond ONLY with valid JSON {"count": <int>}. How many distinct arrangements of the letters in "MISSISSIPPI" have no two S characters   adjacent?'

BODY='{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"'"$FIXED"'"}],"max_tokens":8000,"response_format":{"type":"json_object"},"stream":false}'

# Zen Go — completion_tokens_details MISSING
curl -s https://opencode.ai/zen/go/v1/chat/completions \
  -H "Authorization: Bearer $OPENCODE_GO_API_KEY" \
  -H "Content-Type: application/json" -d "$BODY" | jq '.usage'

# Official DeepSeek — completion_tokens_details.reasoning_tokens present
curl -s https://api.deepseek.com/chat/completions \
  -H "Authorization: Bearer $DEEPSEEK_API_KEY" \
  -H "Content-Type: application/json" -d "$BODY" | jq '.usage'
  1. Zen Go result (capture F1, cf-ray a31b27150f0aa330-SEA, 2026-08-27 12:41:52 GMT):
{"prompt_tokens": 124, "completion_tokens": 198, "total_tokens": 322,
 "prompt_tokens_details": {}}

choices[0].message.reasoning_content is populated (~500 chars of thinking) — the model reasoned; only the accounting vanished.

  1. Official result minutes later (capture D2, 2026-08-27 11:33:14 GMT), same model id + prompt:
{"prompt_tokens": 144, "completion_tokens": 221, "total_tokens": 365,
 "prompt_tokens_details": {"cached_tokens": 0},
 "completion_tokens_details": {"reasoning_tokens": 214},
 "prompt_cache_hit_tokens": 0, "prompt_cache_miss_tokens": 144}
  1. Positive control through the same gateway, same harness/prompt, minutes apart (capture F2, cf-ray a31b2b91ef3390db-SEA, 2026-08-27 12:45:43 GMT) — kimi-k2.6 returns
    the full OpenAI details shape:
"prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 47, "cache_write_tokens": 0},
"completion_tokens_details": {"audio_tokens": 0, "reasoning_tokens": 1757}

Captures were made with plain httpx/curl POSTs (no SDK, no OpenCode client parsing) — this rules out client-side field stripping

Screenshot and/or share link

No response

Operating System

macOS 14.5.0, arm64

Terminal

Ghostty

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the paired curl requests against the Zen Go and official DeepSeek /v1/chat/completions endpoints, then inspect the gateway's provider-specific response normalization path. Compare the returned usage objects for deepseek-v4-flash, using the kimi-k2.6 response as a positive control; done means the documented DeepSeek usage fields are preserved in the normalized response.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.