Skip to content

fix: 错误信息中显示 OpenRouter 转述的上游错误 - #125

Open
sheepbox8646 wants to merge 1 commit into
mainfrom
fix/twi-13-openrouter-upstream-error
Open

sheepbox8646 wants to merge 1 commit into
mainfrom
fix/twi-13-openrouter-upstream-error

Conversation

@sheepbox8646

Copy link
Copy Markdown
Member

概要

上游服务商报错时,OpenRouter 只返回 Provider returned error 这类通用 message。真正原因在 metadata.raw,服务商在 metadata.provider_name。Twilight 原来只显示通用 message,排查时必须另外抓包。

  • 新增 errorformat.AddOpenRouterUpstream:它读取 OpenRouter 的 metadata,把服务商名称和上游 message 追加到 APIError.Message,例如 Provider returned error (DeepInfra: Input should be 'text')。
    • metadata 位置:Chat Completions 放在 error.metadata,Responses 与 Anthropic Messages 放在顶层 metadata,两种都兼容。带顶层 object 字段的 Responses 响应对象不读,因为那里的 metadata 是调用方自己的。
    • raw 形式:JSON 字符串或 JSON 值;字符串内容依次按 JSON 对象、JSON 数组、SSE data: 行、纯文本解析。HTML 页面忽略。
    • 上游 message 折叠成一行,截断到 300 个字符,避免把过长的请求回显带进错误文本。
    • 原 message 已包含上游 message 时,只追加服务商名称。
  • 覆盖所有指向 OpenRouter 的解码路径:DecodeOpenAI(Chat Completions、Responses、embedding、images 等)、Responses 的 error 流事件、DecodeOpenRouter(provider/openrouter/*),以及 Anthropic Messages 的 decodeError。只改 DecodeOpenRouter 不够,因为 completions、responses 和 anthropic 指向 OpenRouter 时都不经过它。
  • 去掉重复状态码:APIError.Error() 在 code 只是重复 HTTP 状态码时不再输出它,422 422: ... 变成 422: ...。
  • 行为不变的部分:没有 metadata、没有 raw 或 raw 无法读出 message 时,错误文本与原来完全一致。Kind、Type、Code、StatusCode 都不变。

关联

验证

  • 新增 internal/errorformat/openrouter_upstream_test.go,并补充 Anthropic 与 sdk.APIError 的用例。
    • 真实样本(2026-10-15 抓取,去掉 user_id):DeepInfra 422、Alibaba 400、Alibaba 上游 429(纯文本 raw)、Responses 端点的校验错误(顶层 metadata、数组 raw)、Responses 经 Alibaba 的 400、Anthropic 端点经 Alibaba 的 400 与 temperature 校验错误。
    • 构造样本(测试中已注明):SSE 形式的 raw、FastAPI 风格的 detail、数组拼接、超长截断、Responses error 事件,以及各种缺失或无法解析的情况。
  • 本地运行 go build ./...、go vet ./...、go vet -tags integration ./...、go test ./... -short -count=1 -race、golangci-lint run ./...,全部通过。go mod tidy 无变化。
  • 真实接口(OpenRouter)前后对比:
场景 修改前 修改后
completions,DeepInfra 422 422 422: Provider returned error 422: Provider returned error (DeepInfra: Input should be 'text')
completions,Alibaba 400 400 400: Provider returned error 400: Provider returned error (Alibaba: <400> InternalError.Algo.InvalidParameter: Format error : ...)
completions,Alibaba 上游 429 429 429: Provider returned error 429: Provider returned error (Alibaba: qwen/qwen3.8-flash is temporarily rate-limited upstream. ...)
responses,temperature 50 400 invalid_prompt: Invalid Responses API request 400 invalid_prompt: Invalid Responses API request (Too big: expected number to be <=2)
responses,Alibaba 400 400 400: Provider returned error 400: Provider returned error (Alibaba: <400> ... is not valid under any of the given schemas.)
anthropic,Alibaba 上游 400 400 invalid_request_error: <400> ... in thinking mode 400 invalid_request_error: <400> ... in thinking mode (Alibaba)

未验证

  • SSE 形式的 raw、带 metadata 的 Responses error 流事件没有抓到真实样本,只有构造样本的单元测试。
  • Chat Completions 流中途出现在 SSE 里的错误 chunk,目前不会解析成 APIError,不在本 PR 范围内。

🤖 Generated with Claude Code

OpenRouter 在上游服务商报错时只返回 "Provider returned error",真正原因在 metadata.raw,服务商在 metadata.provider_name。新增 AddOpenRouterUpstream,兼容 error.metadata 与顶层 metadata,以及 JSON 对象、数组、SSE 和纯文本形式的 raw,把服务商名称与截断后的上游 message 追加到 Message。DecodeOpenAI、DecodeOpenAIErrorEvent、DecodeOpenRouter 和 Anthropic Messages 的 decodeError 都调用它。

APIError.Error() 在 code 只是重复 HTTP 状态码时不再重复输出(如 "422 422")。

Linear: TWI-13

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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