Repository navigation
fix(upstream): 国际版 11-128 通道校验分流 + 始终发送 X-Conversation-ID - #233
Conversation
- 11-128 "request illegal" 是通道校验失败(上游认为请求不是官方客户端发出), 此前按裸 "request illegal" 文案被误判为 11140 账号封禁,硬禁用健康账号 - 新增 ErrChannelRefused:11-128 不罚账号(无冷却/熔断/错误计数),由轮转继续; 11140 仍按账号限制禁用,提示改为在 WorkBuddy 提交反馈申诉 - 11140 判定改为 code + 文案双条件,保留其模型级限流变体的 ErrSoftRate 语义 - 国际端点收紧通道校验后,缺 X-Conversation-ID 会 400/11-128 (conversation id missing);会话头三元组改为始终发送,缺省生成 32 hex - 补充分类/策略回归测试与会话头 wire-level 测试 验证:4 个国际版账号逐一实测均 HTTP 200;/v1/messages 与 /v1/chat/completions 均正常返回。
|
这个 PR 里两处修复我们 fork 上都已经有了,而且实现比这里更完整,所以没有整条合入(整条合会把这些更完整的部分降级掉)。只取了唯一有增量的一行。 逐项核对1. 11-128 与 11140 分流 —— 已有,且更细
PR 把 11-128 移到 已有回归用例: 2.
PR 的版本是「透传 or 生成 32 hex」,少了第 2 路,合并会退掉缓存槽隔离。 3. 11140 禁用提示文案 —— 唯一增量,已采纳
建议这个 PR 可以关掉了——两处功能修复在本 fork 已存在,剩下的一行文案已落地。如果上游要合,PR 本身对上游是有效的(上游还没这两处)。 |
…-Conversation-ID
上游「request illegal」有两种形态,msg 文案完全相同,只有业务码不同:
11-128 / 11128 → 请求级「通道校验失败」(displayMsg「请求被拦截,请重新发送」、
displayTips「没通过渠道校验,可能不是官方客户端发出的」)。
账号本身健康(官方客户端可正常使用同一账号),历史实现却
Disable → 仍可用的号被永久踢出池子,用户只能反复点「解冻」
(issue linguo2625469#197 / linguo2625469#231 的用户侧症状)。
11140 → 账号级授权限制(auth/auth_forbidden),真封禁,须申诉解除。
历史实现用文案 marker「request illegal」一刀切归 ErrAccountFault,于是:
1. 11-128 被 Disable —— 误伤健康账号(issue linguo2625469#197、linguo2625469#231)。
2. 一旦把 11-128 改成软冷却,11140 的真封禁也跟着降级 —— 账号永远轮换、
永远不恢复。
本次改法(分类层分流,语义最具体者优先):
- internal/upstream/client.go
- 新增 ErrKind ErrChannelRefused(String() = "channel_refused")。
- accountFaultMarkers 移除「request illegal」(它已不能作为通用 marker)。
- 新增 IsAccountRestricted(body):11140 + 文案「request illegal」双条件。
- 新增 IsChannelRefused(body):业务码 11-128/11128(两种书写)+ 文案合取。
认 issue linguo2625469#188「conversation id missing」、issue linguo2625469#231「first message is not
system prompt」等同码变体;内容审核变体(blocked by security policy /
unapproved channel / illegal api invocation)归 ErrContentBlocked 域,不被
通道校验语义吞掉;11140 优先(真封禁不得降级)。
- Classify 在 accountFaultMarkers 之前先按业务码分流二者。
- 移除旧的 IsChannelVerificationBlocked / channelVerificationMarkers(被
IsChannelRefused 取代)。
- internal/server/handler.go
- applyErrorPolicy 的 ErrAccountFault 分支改用 IsAccountRestricted 判 Disable,
文案换成「需在 WorkBuddy 官方渠道申诉解除」(重登无效)。
- 新增 ErrChannelRefused 分支:**零动作**(不冷却/不熔断/不 NoteFailures),
账号本身健康不该受罚。
- 轮转循环内 fail-fast:通道指纹是「出口 IP + 出站头部 + 请求体形状」的组合,
换号不改变它 —— 轮转只会把无效请求放大 MaxRotate 倍、每号各交一次配额,
末端还落成 503 no_healthy_account,把确定性的客户端侧问题伪装成服务端过载,
客户端于是无限重试。故透传上游原文回 400 + code=channel_refused。
例外:跨域回退开启(pool.cross_domain_fallback,默认关)且另一域还有能服务
该模型的号时,换域可能换到另一条通道,此时继续轮转。
- 末端错误映射补 ErrChannelRefused → 400 channel_refused。
- internal/upstream/hint.go
- ErrChannelRefused 的 gateway_hint:指向排查 user-agent/头部/拦截代理。
- internal/server/request_illegal_policy_test.go
- 重写为按业务码分流的断言:11-128/11128 零罚号、11140 禁用、11140 限流
变体仍为软限流、裸「request illegal」不归属账号级判定;覆盖 issue linguo2625469#188 /
linguo2625469#231 的同码变体与内容审核变体。
上游 PR linguo2625469#233 走的是同一思路(新增 ErrChannelRefused + 按业务码分流),本提交
在两点上更强:① 覆盖 issue linguo2625469#188/linguo2625469#231 的同码文案变体(上游版要求文案必须是
「request illegal」,会漏掉「conversation id missing」);② 轮转内 fail-fast 并
给出 400 channel_refused + 可操作 hint,避免把客户端侧问题伪装成 503 过载。
实测抓到的真实误伤:上游把模型级限流说明塞进 11140 的 displayMsg,msg 仍是 "request illegal",于是 code+文案双条件双双命中 → 分类落到 ErrAccountFault → 好号被永久禁用(面板标 banned),而用户重登对模型级限流毫无作用,等几分钟就 自愈的东西被当成永久封禁。 IsAccountRestricted 增加第三条排除项:命中 softRateMarkers 即放弃封禁判定, 让分类链继续落到 ErrSoftRate(软冷却)。与上游 PR linguo2625469#233 的口径一致。 回归守卫:TestClassify11140RateLimitVariantStaysSoftRate + TestIsAccountRestricted 新增子用例。 另:本轮一并把上游真机抓包实录的六种 body 全部跑过分类探针,结论: 11-128/11128 + request illegal → channel_refused(不罚号) 11-128/11128 + conversation id missing → channel_refused(issue linguo2625469#188) 11-128/11128 + first message is not system → channel_refused(issue linguo2625469#231) 11-128/11128 + unapproved channel → content_blocked(内容审核域) 11140 + request illegal → account_fault(禁用) 11140 + 限流文案 → soft_rate(软冷却)
|
已合并(2d9e749),但合并时发现并修复了一个关键问题(d809396): 你的提交里业务码字面量是 已修正实现与全部测试体,并新增 提醒:这类「测试与实现同错」的自洽盲区,建议本地验证时用 issue 里贴的原样响应体跑一次分类,而不是只看测试绿灯。多谢这份贡献,分流设计本身是对的。 |
PR #233 的提交里业务码被写成 "11-128"(缺连字符,字节 49 49 49 50 56), 实现与测试自洽所以全绿,但上游真实码是 "11-128"——线上永远命中不了, 11-128 会落到 ErrClient(不罚号但也不计入通道拒绝的分流语义)。 修复实现与全部测试体的码值,并新增用字节构造业务码的回归用例 (TestClassifyRealChannelRefusedBody),防止实现与测试一起写错再通过。
上游 PR linguo2625469#233:11140 是账号被上游限制,重登无效,只能官方渠道申诉。 本仓的 11-128 分流与 X-Conversation-ID 兜底早已实现且更完整(14017 软冷却、 uid 隔离键),故只取这一行文案。
吸收的上游修复(均为 bug fix): - linguo2625469#233 国际版 11-128 通道校验分流 + 始终发送 X-Conversation-ID(取代本地 linguo2625469#190 旧实现;11-128 不再硬禁用账号) - linguo2625469#159 剥离 content 里的 reasoning_text 片段,修 Codex 转换后 11101 - linguo2625469#185 6021 queue.full 归为渠道级繁忙(不轮转、不罚号,503 upstream_busy) - linguo2625469#217 同账号 refresh 单飞,避免 RT 复用撞 12153 掉登录 - linguo2625469#162 识别企业切换会话 token_source=enterprise_switch - linguo2625469#199 缓存告警假阳性:miss 显式存在时对称反推 hit 另含 linguo2625469#138 linguo2625469#143 linguo2625469#144 linguo2625469#158 linguo2625469#178 linguo2625469#196 linguo2625469#205 linguo2625469#213 linguo2625469#220 linguo2625469#221 linguo2625469#223 linguo2625469#225 linguo2625469#230 linguo2625469#232 等功能更新。 本地改动:README 停维护声明改写为本 fork 口径;版本号 1.14.0-panel。
主体保留上游语义:11-128 的两种形态分开——`11-128 + request illegal` 是通道校验失败 (账号健康、不罚号、继续轮转),`11140 + request illegal` 才是账号受限;并把裸 `request illegal` 从账号故障 marker 里删掉(它此前会把普通请求错误升级成硬禁用)。 与上游的差异(subject 因此去掉了「并始终发送 X-Conversation-ID」):本 fork 保持 `X-Conversation-ID`「空则不发」,不伪造会话 ID——伪造 ID 会影响上游会话维度统计与 风控判定,与既有「透传优先、不伪造」注释冲突,另案评估。另外本 fork 的 11-128 原本一律归内容拦截并触发降级重试,故新增 ErrChannelRefused 通道级分类(轮转耗尽回 400 channel_refused),而不是复用内容拦截路径。 Upstream-Commit: 7a8b7a7f0098df3bad8c17abeab0ddcb3618618e Upstream-Author: thrasher824 <1690002591@qq.com> Upstream-PR: linguo2625469/workbuddy2api-panel#233 Sync-Disposition: adapt Fork-Adaptation: 新增 ErrChannelRefused 通道级分类 + IsAccountRestricted(code 与文案双命中),不改 X-Conversation-ID 发送策略。 Tested: make fmt-check && make lint && make test AI-Generated: true Generated-At: 2026-10-21T13:31:43Z

问题
国际版端点收紧通道校验后,出现两类被混淆的错误:
request illegal(通道校验失败,displayTips明确提示“可能不是官方客户端发出”)与 11140request illegal(账号被上游限制)的msg完全相同。现有实现按裸文案"request illegal"判定,把 11-128 误判成账号封禁,直接Disable健康账号——用户在官方客户端里同一账号可以正常对话,面板却显示 banned。X-Conversation-ID,国际端点会返回 400 / 11-128(conversation id missing),即 fix(upstream): always send the conversation channel header trio #190 描述的问题。修复
ErrChannelRefused:11-128 判为通道校验失败,不罚账号(无冷却/熔断/错误计数),由轮转继续尝试。IsAccountRestricted)判定,仍走账号限制禁用;禁用提示改为“在 WorkBuddy 提交反馈申诉”,不再误导为重新登录。rate-limiting文案)保持ErrSoftRate,不会被误判为账号故障。injectConversationHeaders改为始终发送X-Conversation-ID:入站带值则透传,否则生成 32 位 hex(与 request/message ID 的兜底同口径)。验证
go build ./...、go test ./internal/upstream ./internal/server通过。/v1/chat/completions与/v1/messages均正常。关联