Skip to content

fix(llm): drop extra_body.stream from non-streaming requests (#647) - #657

Merged
lizhengfeng101 merged 1 commit into
alibaba:mainfrom
stay-foolish-forever:fix/issue-647-stream-field-handling
Aug 1, 2026
Merged

fix(llm): drop extra_body.stream from non-streaming requests (#647)#657
lizhengfeng101 merged 1 commit into
alibaba:mainfrom
stay-foolish-forever:fix/issue-647-stream-field-handling

Conversation

@stay-foolish-forever

Copy link
Copy Markdown
Contributor

Description

When extra_body.stream is configured, every LLM call fails with:

expected destination type of 'string' or '[]byte' for responses with content-type 'text/event-stream;charset=utf-8' that is not 'application/json'

Root cause

The error originates from the SDK's response decoder (both openai-go/v3 and anthropic-sdk-go share this code via Stainless generation). It surfaces when a non-streaming SDK call (*.New(), which expects application/json) receives an SSE body (text/event-stream).

OpenAIClient and AnthropicClient forwarded every extra_body key straight into the wire request body via WithJSONSet, including stream. The server therefore answered with SSE, but the client invoked the non-streaming New() path that expects JSON → decode fails.

This was already fixed for OpenAIResponsesClient — it drops the stream key in CompletionsWithCtx (see responses_client.go:82-93, with a detailed comment explaining why). This PR applies the same pattern to the other two non-streaming clients so all three behave consistently.

Changes

  • internal/llm/client.go:

    • OpenAIClient.CompletionsWithCtx: skip the stream key when merging extra_body into the request body. The streaming decision is preserved — only stream: true as a bool triggers the streaming path (NewStreaming sets stream=true itself on the wire); other value types ("true" string, false bool) now have the key dropped entirely so the server returns JSON instead of SSE.
    • AnthropicClient.CompletionsWithCtx: skip the stream key (mirrors OpenAIResponsesClient — this client has no streaming path at all, so any stream value would break the call).
  • internal/llm/client_test.go:

    • Refactored TestOpenAIClient_StreamingRequiresBooleanTrueTestOpenAIClient_NonStreamingRequestDropsStreamField: now verifies stream is absent from the non-streaming request body across three value types (missing / false / "true").
    • Added TestAnthropicClient_ExtraBodyStreamDropped: verifies stream is dropped while other extra_body keys (keep_me, temperature_override) are still forwarded (mirrors TestOpenAIResponsesClient_ExtraBodyStreamDropped).

End-to-end verification

Using the reporter's exact setup (DashScope-compatible Anthropic endpoint, model: glm-5.2):

Version extra_body.stream ocr llm test result
v1.8.3 (80a5794) true ❌ Fails — reproduces #647 exactly
This PR (85fccde) true Connection test successful

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring (no functional changes)
  • Documentation update
  • CI / Build / Tooling

How Has This Been Tested?

  • make test passes locally
  • Manual testing (see the end-to-end comparison table above — reproduced the bug on v1.8.3 and verified the fix on this branch, using the reporter's exact setup: DashScope Anthropic-compatible endpoint with model: glm-5.2 and extra_body: {"stream": true})

Additional test coverage:

  • All existing TestOpenAIClient_Streaming* tests (tool call, cancellation, error, usage, reasoning content, etc.) pass unchanged — the streaming path for stream: true (bool) is preserved.
  • Full internal/llm package test suite passes (go test -race -count=1 ./internal/llm/..., ~89s).

Checklist

  • My code follows the project's coding style (go fmt, go vet)
  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly (not applicable — the fix restores correct non-streaming behavior; the existing extra_body docs in README.md already accurately describe that extra_body.stream=true enables streaming for the Chat Completions client, and that remains unchanged)
  • I have signed the CLA

Related Issues

Relates to #647 — this PR addresses the same error, but the issue is intentionally left open for a short observation period to confirm this fix resolves the underlying problem for all affected users.

…#647)

Forwarding extra_body.stream to the non-streaming Chat Completions and
Anthropic Messages APIs made the server return text/event-stream (SSE)
while the SDK's non-streaming New() expects a JSON body, causing every
call to fail with "expected destination type of 'string' or '[]byte'
for responses with content-type 'text/event-stream;charset=utf-8' that
is not 'application/json'".

The OpenAIResponsesClient already had this fix (it drops the stream key
in CompletionsWithCtx). Apply the same pattern to OpenAIClient and
AnthropicClient so the three non-streaming clients behave consistently.
For OpenAIClient the streaming decision is preserved: only boolean
true triggers NewStreaming (which sets stream=true itself); other value
types (string "true", bool false) now have the key dropped from the
wire body so the server returns JSON instead of SSE.
@github-actions

Copy link
Copy Markdown
Contributor

OpenCodeReview: No comments generated. Looks good to me.

@lizhengfeng101 lizhengfeng101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lizhengfeng101
lizhengfeng101 merged commit e784744 into alibaba:main Aug 1, 2026
8 checks passed

@GDneymarphat GDneymarphat left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leava a commen....

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.

3 participants