Skip to content

fix(upstream): 国际版 11-128 通道校验分流 + 始终发送 X-Conversation-ID - #233

Merged
linguo2625469 merged 1 commit into
linguo2625469:mainfrom
thrasher824:fix/11-128-channel-refused
Oct 21, 2026
Merged

linguo2625469 merged 1 commit into
linguo2625469:mainfrom
thrasher824:fix/11-128-channel-refused

Conversation

@thrasher824

Copy link
Copy Markdown
Contributor

问题

国际版端点收紧通道校验后,出现两类被混淆的错误:

  1. 11-128 request illegal(通道校验失败,displayTips 明确提示“可能不是官方客户端发出”)与 11140 request illegal(账号被上游限制)的 msg 完全相同。现有实现按裸文案 "request illegal" 判定,把 11-128 误判成账号封禁,直接 Disable 健康账号——用户在官方客户端里同一账号可以正常对话,面板却显示 banned。
  2. 请求没有会话 ID 时不发送 X-Conversation-ID,国际端点会返回 400 / 11-128(conversation id missing),即 fix(upstream): always send the conversation channel header trio #190 描述的问题。

修复

  • 新增 ErrChannelRefused:11-128 判为通道校验失败,不罚账号(无冷却/熔断/错误计数),由轮转继续尝试。
  • 11140 改为 code + 文案双条件(IsAccountRestricted)判定,仍走账号限制禁用;禁用提示改为“在 WorkBuddy 提交反馈申诉”,不再误导为重新登录。
  • 11140 的模型级限流变体(rate-limiting 文案)保持 ErrSoftRate,不会被误判为账号故障。
  • injectConversationHeaders 改为始终发送 X-Conversation-ID:入站带值则透传,否则生成 32 位 hex(与 request/message ID 的兜底同口径)。
  • 补充分类/策略回归测试与会话头 wire-level 测试。

验证

  • go build ./...、go test ./internal/upstream ./internal/server 通过。
  • 实机:4 个国际版账号逐一强制路由实测,均返回 HTTP 200;/v1/chat/completions 与 /v1/messages 均正常。
  • 修复前:同一请求对 4 个账号全部返回 400 / 11-128,且健康账号被误禁用;修复后账号池 4 个账号全部可用。

关联

账号池实机截图(4 个账号全部可用)请见 PR 讨论区。

- 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 均正常返回。
@thrasher824

Copy link
Copy Markdown
Contributor Author
1f1480d82b5d71c05b9abaf902631d1

@misakano7545

Copy link
Copy Markdown

这个 PR 里两处修复我们 fork 上都已经有了,而且实现比这里更完整,所以没有整条合入(整条合会把这些更完整的部分降级掉)。只取了唯一有增量的一行。

逐项核对

1. 11-128 与 11140 分流 —— 已有,且更细

internal/server/handler.go(本仓)在 applyErrorPolicy 里按 code 三分:

body 处置
"request illegal" + 11-128 软冷却(channel verification blocked),不罚号
"request illegal" + 11140 硬禁用
14017 软冷却(trial 未激活,补完 register 后可自愈)

PR 把 11-128 移到 Classify 里判 ErrChannelRefused,功能等价,但 14017 这一档本仓单独处理了——合过来会丢。

已有回归用例:TestApplyErrorPolicyChannelVerificationKeepsAccount(11-128 不得永久禁用)、TestApplyErrorPolicyAuthForbiddenStillDisables(11140 仍硬禁用)。

2. X-Conversation-ID 始终发送 —— 已有,且多一层

internal/upstream/headers.go:injectConversationHeaders 的三路兜底:

  1. 入站带值 → 透传
  2. 有会话但没 ID → buildCacheKey(uid, src)(带 uid 段,防止同一客户端会话打在不同账号上共用同一个上游缓存槽)
  3. 零值 ChatMeta(夜猫子对话、面板模型任务)→ 现造 32 hex

PR 的版本是「透传 or 生成 32 hex」,少了第 2 路,合并会退掉缓存槽隔离。

3. 11140 禁用提示文案 —— 唯一增量,已采纳

- account banned by upstream (11140 request illegal), re-login required
+ account restricted by upstream (11140 request illegal);
+ appeal in WorkBuddy (submit feedback) to lift the restriction

re-login required 确实是误导:11140 重登无效,只能申诉。这一行已单独提交(internal/server/handler.go),go build + internal/server、internal/upstream 测试通过。

建议

这个 PR 可以关掉了——两处功能修复在本 fork 已存在,剩下的一行文案已落地。如果上游要合,PR 本身对上游是有效的(上游还没这两处)。

pardSun added a commit to pardSun/workbuddy2api-panel that referenced this pull request Oct 20, 2026
Huiaei pushed a commit to Huiaei/workbuddy2api-panel that referenced this pull request Oct 20, 2026
上游「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 过载。
Huiaei pushed a commit to Huiaei/workbuddy2api-panel that referenced this pull request Oct 20, 2026
实测抓到的真实误伤:上游把模型级限流说明塞进 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(软冷却)
shinny-xionghongwei added a commit to shinny-xionghongwei/workbuddy2api-panel that referenced this pull request Oct 20, 2026
WilliamLuuu added a commit to WilliamLuuu/workbuddy2api-panel that referenced this pull request Oct 20, 2026
@linguo2625469
linguo2625469 merged commit 2d9e749 into linguo2625469:main Oct 21, 2026
@linguo2625469

Copy link
Copy Markdown
Owner

已合并(2d9e749),但合并时发现并修复了一个关键问题(d809396):

你的提交里业务码字面量是 "11-128"(缺连字符,字节 49 49 49 50 56),实现与测试体的码值是同一个写法——双方自洽所以 go test 全绿,但上游真实业务码是 "11-128"(带连字符,见 #197 的真实响应体)。线上 isChannelRefused 永远命中不了,11-128 会落到 ErrClient:不罚号(核心痛点已解决),但通道拒绝的分流语义(hint、错误码、专门文案)全部失效。

已修正实现与全部测试体,并新增 TestClassifyRealChannelRefusedBody——业务码用字节构造(string([]rune{49,49,45,49,50,56})),不依赖手打字面量,专门防止「实现和测试一起写错」再次通过。

提醒:这类「测试与实现同错」的自洽盲区,建议本地验证时用 issue 里贴的原样响应体跑一次分类,而不是只看测试绿灯。多谢这份贡献,分流设计本身是对的。

linguo2625469 added a commit that referenced this pull request Oct 21, 2026
PR #233 的提交里业务码被写成 "11-128"(缺连字符,字节 49 49 49 50 56),
实现与测试自洽所以全绿,但上游真实码是 "11-128"——线上永远命中不了,
11-128 会落到 ErrClient(不罚号但也不计入通道拒绝的分流语义)。

修复实现与全部测试体的码值,并新增用字节构造业务码的回归用例
(TestClassifyRealChannelRefusedBody),防止实现与测试一起写错再通过。
zhf883680 pushed a commit to zhf883680/workbuddy2api-panel that referenced this pull request Oct 21, 2026
上游 PR linguo2625469#233:11140 是账号被上游限制,重登无效,只能官方渠道申诉。
本仓的 11-128 分流与 X-Conversation-ID 兜底早已实现且更完整(14017 软冷却、
uid 隔离键),故只取这一行文案。
maintell added a commit to maintell/workbuddy2api-panel that referenced this pull request Oct 21, 2026
吸收的上游修复(均为 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。
BegoniaHe pushed a commit to BegoniaHe/wokbuddy-demon that referenced this pull request Oct 21, 2026
主体保留上游语义: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
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.

被拦截了 trae自定义模型添加国际版模型global:deepseek-v4.1-flash失败

3 participants