anomalyco / anomalyco/opencode

OpenAI-compatible /v1/chat/completions streaming with stream_options.include_usage returns non-standard SSE (empty choices chunks, empty id/model, trailing {"choices":[],"cost":"0"})

Open
#42,918 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

Summary

The OpenAI-compatible gateway (opencode.ai/zen/go, /v1/chat/completions) emits non-standard SSE chunks when the client sends stream_options: {"include_usage": true}. This breaks OpenAI-compatible clients that rebuild the streamed response (e.g. LiteLLM crashes with litellm.APIError: Error building chunks for logging/streaming usage calculation).

Without stream_options, the same endpoint streams clean OpenAI-format chunks.

Repro

curl -sN https://opencode.ai/zen/go/v1/chat/completions \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"say hi"}],"stream":true,"max_tokens":20,"stream_options":{"include_usage":true}}'

Observed SSE (with include_usage)

data: {"id":"gen-...","object":"chat.completion.chunk","created":...,"model":"gpt-5.6-luna","choices":[]}
data: {"id":"gen-...","object":"chat.completion.chunk","created":...,"model":"gpt-5.6-luna","choices":[]}
data: {"id":"","object":"chat.completion.chunk","created":...,"model":"","choices":[]}
data: {"id":"","object":"chat.completion.chunk","created":...,"model":"","choices":[]}
data: {"id":"","object":"chat.completion.chunk","created":...,"model":"","choices":[{"index":0,"delta":{"content":"Hi!"},"finish_reason":null}]}
data: {"id":"","object":"chat.completion.chunk","created":...,"model":"","choices":[]}
data: {"id":"","object":"chat.completion.chunk","created":...,"model":"","choices":[]}
data: {"id":"","object":"chat.completion.chunk","created":...,"model":"","choices":[]}
data: {"id":"gen-...","object":"chat.completion.chunk","created":...,"model":"gpt-5.6-luna","choices":[],"usage":{"prompt_tokens":8,"completion_tokens":6,"total_tokens":14}}
data: {"choices":[],"cost":"0"}

Issues vs OpenAI spec

  1. Empty choices: [] on the first chunks — OpenAI streams always start with a chunk containing choices[0].delta.role; clients use the first chunk to seed the response object.
  2. id and model empty strings on mid-stream chunks — chunks should carry stable id/model.
  3. Trailing data: {"choices":[],"cost":"0"} — not a valid OpenAI chunk (no id/object/created/model); the cost field is non-standard.
  4. Content chunk's delta has no role (fine in the middle, but the first real chunk should carry it).

Impact

Any strict OpenAI-compatible client that accumulates chunks and rebuilds the full response (logging/usage calculation) crashes. LiteLLM (v1.84.0, and still in v1.97.0) raises Error building chunks for logging/streaming usage calculation because it assumes the first chunk with choices has a non-empty array with a delta.role.

Suggestion: only emit chunks with empty choices as the final usage chunk (like OpenAI does), keep id/model populated on every chunk, and drop the cost trailer (or put it inside the usage chunk).

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 reproduction against the OpenAI-compatible /v1/chat/completions endpoint with stream_options.include_usage enabled, then locate that endpoint's streaming implementation in the repository. Compare its SSE output with the stated OpenAI requirements and the output without stream_options. Done means valid initial and intermediate chunks with stable id/model fields, followed only by a standard usage chunk without the non-standard cost trailer.

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.