Skip to content

fix(openai): 保留 ChatGPT 令牌下准入错误的信息,模型列表缺少 data 时报错 - #113

Open
ix64 wants to merge 7 commits into
felinics:mainfrom
ix64:fix/siwc-compat
Open

ix64 wants to merge 7 commits into
felinics:mainfrom
ix64:fix/siwc-compat

Conversation

@ix64

@ix64 ix64 commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

问题

用 ChatGPT 账号的令牌(Sign in with ChatGPT)调用 https://api.openai.com/v1 时,openai-responses 有两处信息丢失:

  • 准入阶段被拒的请求返回 400,错误体只有 {"detail":"..."},没有 error 对象;请求 ID 放在 x-oai-request-id,没有 x-request-id。DecodeOpenAI 两者都不读,*sdk.APIError 只剩状态码,Message 和 RequestID 为空。
  • GET /v1/models 返回 {"models":[{"slug":...,"visibility":...}]},没有 data。ListModels 返回空列表和 nil error,看起来像是没有模型。

改动

  • errorformat.ParseOpenAI:error 对象没有 message 时,用 detail 作为 Message。字符串原样保留,其他 JSON 值转成紧凑文本。两者都有时 error 对象的 message 优先。codex 和 OpenRouter 的解码器也调用 ParseOpenAI,一并生效。
  • errorformat.DecodeOpenAI:没有 x-request-id 时读 x-oai-request-id,再退到 x-amzn-requestid。
  • responses 和 completions 的 ListModels:响应里没有 data 数组(缺失或为 null)时返回错误;"data": [] 仍是合法的空列表。做法和 openai/transcription 一致。没有解析 models[].slug:这是 ChatGPT 令牌专有的格式,调用方本来也要按 visibility 过滤。
  • 测试:
    • 三个 detail 错误体是 2026-10-10 从 /v1/responses 原样录入的(带 temperature、stream: false、模型 gpt-4o)。
    • 模型列表用例从同一天抓到的 /v1/models 响应里截取了 2 个条目的 3 个字段。原响应 614 KB,含 Codex 的提示词,不放进仓库。
    • 其余边界用例是构造的。

验证

  • go build、go vet、go test -short -race ./... 和 internal/sdkdiff 的测试均通过,go mod tidy 无 diff。把新测试放到 main 的代码上跑,TestDecodeOpenAI、TestDecodeOpenAIDetail 和两个 TestListModels_NoDataArray 都失败。
  • golangci-lint:CI 锁定的 v2.11.3 在 Go 1.27.1 上运行时崩溃,改用 v2.13.2,--new-from-rev main 为 0 个问题。
  • 用真实的 ChatGPT 令牌和本分支的代码做了端到端检查,模型 gpt-6.1-sol:
    • 正常路径全部通过:流式生成、会话中途的 developer 消息、图片输入、工具调用、加密推理内容回放、经 ProviderOptions 的托管 web_search、同一请求两次序列化字节相同。
    • 带 temperature:400,Message 为 Unsupported parameter: temperature,RequestID 有值。v0.7.1 下两者都为空。
    • 未知模型:400,Message 为 The 'gpt-does-not-exist' model is not supported when using Codex with a ChatGPT account.,RequestID 有值。v0.7.1 下两者都为空。
    • 非流式请求:400 Stream must be set to true,带 RequestID。
    • 无效令牌:401 invalid_api_key,Kind 为 authentication,和 v0.7.1 一致。
    • ListModels 返回 list models: response has no data array。v0.7.1 返回空列表。
  • 没有覆盖的范围:普通 API key 下的 ListModels 只有单元测试;Codex 后端(provider/openai/codex)是否也返回 detail 错误体,没有实测。

ix64 added 2 commits October 13, 2026 19:52
The stream kept reasoning_details only from deltas that also carried
reasoning text, and wrote the accumulated text over the last item. On
OpenRouter that dropped every opaque part before the replay: Gemini's
thought signature (reasoning.encrypted), Anthropic's signature on
reasoning.text, and OpenAI's reasoning.encrypted beside its summary; it
also turned OpenAI's summary item into one with both summary and text.

Pieces are now merged into items by type and index, so Gemini's
encrypted item, which shares index 0 with its text item, stays its own
item. Text and summary concatenate; signature, data and id are set by
the piece that carries them. A piece that arrives after the text or the
tool call reopens the reasoning block under the same ID, so the block's
end carries it. The replay sends the items of every reasoning part
unchanged.

The metadata namespace becomes openai-chat, as upstream's, and parts
stored under minimax still replay. The tests stream chunks recorded
from OpenRouter on 2026-10-13 for Gemini, GPT and Claude and check the
replayed request.
…e stream"

This reverts commit 9821109.

Upstream felinics#118 merges reasoning_details in wire order and supersedes the
type+index merge here; the late-piece fix is proposed separately in felinics#122.
This branch goes back to the errorformat and openai models fixes only.
ix64 added a commit to ix64/twilight that referenced this pull request Oct 13, 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.

1 participant