anomalyco / anomalyco/opencode

Zen gateway (/go/v1) streaming: tool_calls continuation deltas send "id":null / "function":{"name":null} instead of omitting absent fields

Open
#43,328 1 comment 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

Streaming chat completions from the Zen gateway (https://opencode.ai/zen/go/v1/chat/completions) emit tool_calls continuation deltas with explicit null values for id and function.name instead of omitting the absent fields, as the OpenAI streaming format specifies.

The first tool_calls delta of a call carries the real values, e.g.:

"delta":{"role":null,"content":"","tool_calls":[{"index":0,"id":"chatcmpl-tool-b4d3cf2ed9145904","type":"function","function":{"name":"calculator","arguments":""}}]}

but every continuation delta for the same call sends:

"delta":{"role":null,"content":"","tool_calls":[{"index":0,"id":null,"type":null,"function":{"name":null,"arguments":""}}]}

This breaks strict OpenAI-compatible clients that accumulate deltas and treat any present field as an update. Concrete case: DeepSeek Harness (dsh) accumulates if (call.function?.name !== undefined) block.name = call.function.name — the null overwrites the previously accumulated name, and every tool call ends up dispatched as unknown tool "". The same request non-streamed returns a correct message.tool_calls[] with id and function.name intact.

Observed model: deepseek-v4-flash (stream mode). The behavior is intermittent across time windows — client-side session logs show tool calls failing on 2026-08-17 ~04:34–12:08 UTC and 2026-08-19 ~02:37–02:50 UTC, while requests outside those windows (e.g., all of 2026-08-18) received spec-compliant chunks with the fields omitted. That pattern suggests different upstream backends behind the gateway, some of which emit null-filled deltas.

Plugins

n/a

OpenCode version

n/a — Zen gateway (opencode.ai/zen/go/v1), captured 2026-08-19 02:56 UTC

Steps to reproduce
curl -N https://opencode.ai/zen/go/v1/chat/completions \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4-flash",
    "messages": [{"role":"user","content":"Use the calculator tool to compute 2+2"}],
    "tools": [{"type":"function","function":{"name":"calculator","description":"compute an arithmetic expression","parameters":{"type":"object","properties":{"expr":{"type":"string"}},"required":["expr"]}}}],
    "tool_choice": "auto",
    "max_tokens": 200,
    "stream": true
  }'

The streamed SSE shows the chunk sequence quoted above: id/name present on the first tool_calls delta, explicit null on the continuation deltas.

Expected: continuation deltas omit id / type / function.name when they carry no value (per the OpenAI streaming format), so field-presence-based delta accumulators keep the values from the first delta.

Screenshot and/or share link

n/a (raw SSE text)

Operating System

macOS 25.5 (arm64), curl — issue is server-side

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 provided curl request against the Zen gateway endpoint and compare the first and continuation tool_calls SSE deltas. Trace the gateway's streaming response path and any upstream normalization; done means continuation chunks omit absent id, type, and function.name fields while preserving valid accumulated tool-call values.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.