Skip to content

Fix tool calls being silently dropped on DeepSeek V4 and Cohere2MoE - #593

Draft
fgheorghe wants to merge 1 commit into
warpfront:betafrom
fgheorghe:beta-fix/tool-calling
Draft

Fix tool calls being silently dropped on DeepSeek V4 and Cohere2MoE#593
fgheorghe wants to merge 1 commit into
warpfront:betafrom
fgheorghe:beta-fix/tool-calling

Conversation

@fgheorghe

Copy link
Copy Markdown

Tested for DeepSeek V4 Flash 0713 - Cohere2MoE is reported by Claude.
Fix supplied by GLM 5.2.

The issue

When DeepSeek V4 emitted a tool call, the API response came back with
finish_reason: "tool_calls" and an empty tool_calls array. The model called
the tool, the daemon parsed it correctly — the call was then thrown away by the
CLI serve layer on its way out to the client. Cohere2MoE has the same bug.

The serve layer folds the daemon's event stream one of two ways, chosen by
whether gen_start advertises contract_version: 2. The V2 fold reads tool
calls from the calls array staged on the terminal payload. The older fold
doesn't — it only ever picked them up from a separate tool_calls event.

That worked as long as those two things lined up, and for most archs they do.
DeepSeek V4 doesn't advertise contract 2 (by design), so it lands on the older
fold — but all four of its generate paths (AR, spec, EP, heterogeneous) deliver
calls exclusively by staging them on the terminal, and never emit the separate
event the old fold was waiting for. Nothing errored; the buffer was just empty
when the response was built. Cohere2MoE delivers calls the same way, so it was
dropping them too.

Only tool turns on those archs are affected. Anything on the V2 fold, and any
generation that doesn't call a tool, behaves exactly as before.

The fix

Teach the older fold to read the staged calls array, the same way the V2 fold
already does. It runs at commit_ready, which is where the terminal is actually
delivered — the post-commit done handler is never reached on that path.

A staged array replaces the buffer rather than appending to it, so an arch that
both stages and emits the separate event doesn't end up with each call twice.
This matches what the V2 fold does, for the same reason. A terminal with no
calls field leaves the buffer alone, so archs that only emit the separate
event keep working untouched.

Malformed entries — a non-array calls, or a call missing its name — now fail
the request instead of being skipped. Quietly discarding them is what caused
this bug in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant