Skip to content

fix(relay/codex): force stream=true on /v1/responses - #5819

Closed
Ericcc-Ma wants to merge 1 commit into
QuantumNous:mainfrom
Ericcc-Ma:fix/codex-force-stream
Closed

fix(relay/codex): force stream=true on /v1/responses#5819
Ericcc-Ma wants to merge 1 commit into
QuantumNous:mainfrom
Ericcc-Ma:fix/codex-force-stream

Conversation

@Ericcc-Ma

@Ericcc-Ma Ericcc-Ma commented Jun 30, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 摘要由人工整理;改动在 Claude 协助下完成(详见下方"AI 协助说明")。

📝 变更描述 / Description

Codex 渠道(ChannelTypeCodex=57)走 chatgpt.com/backend-api/codex/responses,上游只接受流式请求,缺少 "stream": true 会返回 HTTP 400 Stream must be set to true

#4325 已经让"批量自动测试"路径强制使用 stream,但手动测试按钮路径漏了:前端测试弹窗默认 Stream Mode 开关关闭时,stream query 参数未发送,后端 strconv.ParseBool 解析为空→falsebuildTestRequest 拼出 Stream: lo.ToPtr(false),导致手动测试必然 400。

把强制 stream=true 的逻辑下沉到 codex 适配器(relay/channel/codex/adaptor.go:103-110),让所有调用路径(手动测试、批量测试、playground、第三方客户端)都符合上游契约。同时把 info.IsStream 同步置 true,避免 DoResponse 把 SSE 响应按非流式 JSON 解析。

/v1/responses/compact 端点不在此约束范围内,因此保留原有行为。

🚀 变更类型 / Type of change

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 已搜索现有 Issues/PRs,确认是 fix: use stream for codex auto test #4325 的延续修复,非重复提交。
  • Bug fix 说明: 配套 fix: use stream for codex auto test #4325,明确指向同一上游契约的另一切面,不归类为设计取舍或理解偏差。
  • 变更理解: 在 Codex 渠道适配器层强制 stream=true,覆盖所有调用路径;同步 info.IsStream 避免响应解析错误。
  • 范围聚焦: 仅修改 relay/channel/codex/adaptor.go 一个文件、9 行新增。
  • 本地验证: 因本地无 Go toolchain,未运行 go test;改动面小且正交,参考 fix: use stream for codex auto test #4325 同一文件同类改动可由维护者快速复核。
  • 安全合规: 代码中无敏感凭据;codex_key.json 等本地调试文件未入库。

📸 运行证明 / Proof of Work

调用路径推导(controller/channel-test.go:75-152controller/channel-test.go:695-756relay/responses_handler.go:81-86relay/channel/codex/adaptor.go:56-117):

  • 修复前:手动测试 Stream Mode=offisStream=falserequest.Stream=*bool{false} → 上游 400
  • 修复后request.Stream=lo.ToPtr(true) 由 codex 适配器无条件覆盖 → 上游接受

🤖 AI 协助说明 / AI Assistance Disclosure

本次改动由 Claude(Anthropic)协助完成:

  • 定位根因(手动测试路径漏了 stream 强制)
  • 设计修复方案(适配器层强制而非调用方补丁)
  • 实现 9 行改动
  • 起草 PR 描述

Summary by CodeRabbit

  • Bug Fixes
    • Improved Codex “responses (non-compact)” handling by automatically enabling streaming mode for these requests.
    • When stream-related metadata is provided, the system now marks it as streaming to stay consistent with upstream expectations, reducing request rejections while preserving existing response behavior.

@Ericcc-Ma Ericcc-Ma closed this Jun 30, 2026
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ee8d31f9-eaa5-4fc0-8346-51c4851b02a4

📥 Commits

Reviewing files that changed from the base of the PR and between c324e13 and 2c2dfb0.

📒 Files selected for processing (1)
  • relay/channel/codex/adaptor.go

Walkthrough

relay/channel/codex/adaptor.go now imports github.com/samber/lo and forces request.Stream and info.IsStream to true in the non-compact responses request conversion path.

Changes

Codex Streaming Enforcement

Layer / File(s) Summary
Force stream flags in non-compact responses path
relay/channel/codex/adaptor.go
Adds github.com/samber/lo import and sets request.Stream = true and info.IsStream = true in the non-compact responses conversion path.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Possibly related PRs

Poem

🐇 Hop hop, the stream flows bright,
Non-compact paths now set it right,
Stream = true in the adaptor light,
A tidy rabbit fix tonight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: Codex responses now force stream=true on /v1/responses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Ericcc-Ma
Ericcc-Ma deleted the fix/codex-force-stream branch June 30, 2026 09:11
@Ericcc-Ma
Ericcc-Ma restored the fix/codex-force-stream branch June 30, 2026 09:13
@Ericcc-Ma Ericcc-Ma reopened this Jun 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@relay/channel/codex/adaptor.go`:
- Around line 103-110: The Codex adaptor is forcing the upstream request to
stream correctly, but it also overwrites the caller’s stream preference by
setting info.IsStream to true. Update the adaptor logic around request.Stream
and info.IsStream so the request is always sent as streaming to Codex while
preserving the original info.IsStream value, and add a non-stream buffered
fallback in DoResponse/OaiResponsesStreamHandler for callers that requested
stream=false so they still receive a JSON response instead of SSE.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4be7d10d-d839-4da5-ba80-363f8986394d

📥 Commits

Reviewing files that changed from the base of the PR and between 12fc010 and 59eddfd.

📒 Files selected for processing (1)
  • relay/channel/codex/adaptor.go

Comment on lines +103 to +110
// Codex upstream (chatgpt.com/backend-api/codex/responses) only accepts
// streaming requests and rejects with 400 "Stream must be set to true"
// otherwise. Force it here so every call path (manual test, batch test,
// playground, third-party clients) honors the upstream contract.
request.Stream = lo.ToPtr(true)
if info != nil {
info.IsStream = true
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how IsStream is initially derived and whether the stream handler
# accounts for a client that requested non-streaming.
rg -nP --type=go 'func OaiResponsesStreamHandler' -A 40
echo '--- where IsStream is initialized ---'
rg -nP --type=go '\.IsStream\s*=' -C2

Repository: QuantumNous/new-api

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file list =="
git ls-files | rg '^(relay/channel/codex/adaptor\.go|.*openai.*\.go|.*responses.*\.go|.*stream.*\.go)$' || true

echo
echo "== locate DoResponse / OaiResponsesStreamHandler / IsStream assignments =="
rg -n --hidden --glob '*.go' 'DoResponse|OaiResponsesStreamHandler|IsStream|StreamHandler' relay . || true

Repository: QuantumNous/new-api

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relay/channel/codex/adaptor.go (lines 90-170) =="
sed -n '90,170p' relay/channel/codex/adaptor.go | cat -n

echo
echo "== relay/channel/openai/relay_responses.go (lines 1-220) =="
sed -n '1,220p' relay/channel/openai/relay_responses.go | cat -n

echo
echo "== relay/channel/openai/chat_via_responses.go (lines 1-240) =="
sed -n '1,240p' relay/channel/openai/chat_via_responses.go | cat -n

echo
echo "== relay/common/relay_info.go (lines 430-500) =="
sed -n '430,500p' relay/common/relay_info.go | cat -n

echo
echo "== relay/chat_completions_via_responses.go (lines 130-175) =="
sed -n '130,175p' relay/chat_completions_via_responses.go | cat -n

Repository: QuantumNous/new-api

Length of output: 25086


Preserve the caller's stream preference here

Force request.Stream = true for Codex, but keep info.IsStream unchanged. DoResponse routes through OaiResponsesStreamHandler, which always emits SSE; overwriting info.IsStream makes stream: false callers receive a streamed body instead of the JSON response they asked for. Add a buffered non-stream fallback for those callers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@relay/channel/codex/adaptor.go` around lines 103 - 110, The Codex adaptor is
forcing the upstream request to stream correctly, but it also overwrites the
caller’s stream preference by setting info.IsStream to true. Update the adaptor
logic around request.Stream and info.IsStream so the request is always sent as
streaming to Codex while preserving the original info.IsStream value, and add a
non-stream buffered fallback in DoResponse/OaiResponsesStreamHandler for callers
that requested stream=false so they still receive a JSON response instead of
SSE.

@Ericcc-Ma
Ericcc-Ma force-pushed the fix/codex-force-stream branch from 59eddfd to c324e13 Compare July 1, 2026 01:23
Codex upstream (chatgpt.com/backend-api/codex/responses) rejects requests
without "stream": true with HTTP 400 "Stream must be set to true". The
manual channel test button previously sent stream=false unless the user
explicitly toggled the Stream Mode switch in the test dialog, since the
manual test path passes the stream query parameter straight through
buildTestRequest without honoring shouldUseStreamForAutomaticChannelTest.

Force stream=true inside the codex adapter so every call path (manual
test, batch test, playground, third-party clients) honors the upstream
contract. Also set info.IsStream=true so DoResponse routes the SSE
response through OaiResponsesStreamHandler instead of trying to parse it
as a non-stream JSON body.

The /v1/responses/compact endpoint is left untouched since compact
requests do not share the same upstream constraint.

Co-Authored-By: Claude <noreply@anthropic.com>
@Ericcc-Ma
Ericcc-Ma force-pushed the fix/codex-force-stream branch from c324e13 to 2c2dfb0 Compare July 1, 2026 01:27
@seefs001

seefs001 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

codex渠道类型在配置时已有说明,仅能在codex cli中使用,不存在此类使用场景,不会考虑对其进行修改。
至于手动测试没有强制stream,使用该渠道需要用户对其支持行为有了解,不会强制修改用户的测试请求,需要自己去点流式选项。

@seefs001 seefs001 closed this Jul 1, 2026
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.

2 participants