Skip to content

[Bug] 计费标头整流器应支持按供应商配置,避免破坏 Anthropic 订阅 Auto Mode #1403

Description

@oubeichen

问题背景

当前「计费标头整流器」只有一个全局开关 enableBillingHeaderRectifier(默认开启)。它会在每次 claude / claude-auth 请求发往上游前,无条件从 system 中移除 x-anthropic-billing-header: ... 文本块:

这里的 x-anthropic-billing-header 并不是 HTTP header,而是 Claude Code 注入到请求体 system 数组首部的 text block。

全局剥离对 Bedrock 等非原生上游是合理的:它们可能把该保留关键字拒绝为 400;但同一行为会误伤 Anthropic 第一方订阅 OAuth 路径,尤其是 Auto Mode 的安全分类器旁路请求。

已确认的两侧兼容性冲突

1. 非原生上游:需要剥离

anthropics/claude-code#24168 记录了 Bedrock 对该 system 文本块返回:

400 x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt.

这也是现有整流器最初要解决的问题。

2. Anthropic 第一方订阅 OAuth:必须保留

anthropics/claude-code#64585 给出了严格 A/B 复现:仅将 CLAUDE_CODE_ATTRIBUTION_HEADER 设为 0(即客户端不注入该 block),Auto Mode 分类器就对 Sonnet / Opus 确定性返回 429;恢复默认注入后立即正常。主对话请求不受影响,因此用户只会看到误导性的:

<model> is temporarily unavailable, so auto mode cannot determine the safety of Bash/Write/Edit right now

anthropics/claude-code#60438 的顶部更新也已将原先的“账户分类器池异常”结论更正为此根因;anthropics/claude-code#63819 的评论中也指向同一修复。

makecindy/cindy#758 还验证了宿主无条件设置 CLAUDE_CODE_ATTRIBUTION_HEADER=0 会导致所有需分类的工具调用 100% 失败。Cindy 最终改为:订阅 OAuth 连接保留 Claude Code 默认归因块,网关/第三方路径继续移除。

虽然 CCH 是在代理侧删除,而不是在客户端设置环境变量,但两者发给最终上游的请求体都缺少同一个 attribution block,因此会形成相同的第一方出站请求形态。

当前实现的问题

整流器的作用域由全局配置决定,而不是由最终选中的供应商决定。因此部署中只要同时存在:

  • Anthropic 第一方 API / 订阅 OAuth 上游;
  • Bedrock、Vertex、第三方 Anthropic-compatible 或需要稳定 KV/prompt cache 前缀的上游;

管理员就无法用一个全局布尔值同时满足两类渠道。

另外,rectifyBillingHeader() 会原地修改 session.request.message。实现供应商级策略时,如果一次请求可在供应商之间串行 fallback,或进入 hedge/discovery 并发尝试,必须保证某个供应商的 strip 不会污染后续/并行需要 preserve 的尝试。

建议方案

增加供应商级策略,例如:

type BillingHeaderPolicy = "inherit" | "preserve" | "strip";
  • inherit:沿用全局默认,保持数据库迁移和现有部署兼容;
  • preserve:不运行 rectifier;适用于 Anthropic 第一方端点,尤其是订阅 OAuth / Auto Mode;
  • strip:移除 attribution block;适用于 Bedrock、Vertex、第三方兼容上游或跨会话缓存场景。

可选地对新建供应商做安全默认:

  • 明确识别为 Anthropic 第一方端点时默认 preserve
  • 明确识别为 Bedrock/Vertex/非原生上游时默认 strip
  • 自定义/无法可靠识别的 URL 由管理员显式选择,不建议仅根据 claude / claude-auth 类型推断,因为这两个类型目前同时覆盖第一方和中转语义。

全局开关可以保留为默认策略,但供应商配置应能覆盖它。UI 文案也建议说明:删除该 block 可能导致 Anthropic 订阅 OAuth 的 Auto Mode 分类器不可用。

验收标准

  • 每个 claude / claude-auth 供应商可单独配置 inherit / preserve / strip
  • preserve 路径发往上游的 system 中仍包含原始 x-anthropic-billing-header block
  • strip 路径继续兼容当前字符串/数组等处理,并保留 specialSettings 审计
  • 串行 fallback:先尝试 strip 供应商后切到 preserve,第二次请求仍能拿到原始 block;反向同理
  • hedge/discovery:不同策略的并发尝试使用相互隔离的请求体,不共享原地修改结果
  • 新增第一方保留、非原生剥离、混合供应商 fallback/hedge 的回归测试
  • 文档明确该字段是 system prompt text block,不是 HTTP header,并注明 Auto Mode 风险

环境

  • 核查版本:main / v0.9.2
  • 核查提交:ccbad37f266e3e69d57a4427e2f27cf288796e63

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions