cc-switch 的 ModelMapping 会读这些变量:ANTHROPIC_DEFAULT_HAIKU_MODEL、ANTHROPIC_DEFAULT_SONNET_MODEL、ANTHROPIC_DEFAULT_OPUS_MODEL、ANTHROPIC_MODEL、ANTHROPIC_REASONING_MODEL。然后它映射时是这个顺序:
- 先看请求体里有没有
thinking,而且thinking.type是enabled或adaptive。
有的话,优先直接改写成ANTHROPIC_REASONING_MODEL。 - 如果没有 thinking,再看原始 model 名里是不是包含
haiku、sonnet、opus。
是的话,分别映射到你配的DEFAULT_HAIKU/SONNET/OPUS_MODEL。 - 都不是的话,才回退到
ANTHROPIC_MODEL。 - 如果这些也没配,就保留原模型名。
所以,注意:cc-switch 的“thinking 映射”和“haiku/sonnet/opus 家族映射”是两套逻辑,Claude Code 一旦发出一个被 cc-switch 识别成 “thinking enabled” 的请求,它会先命中 ANTHROPIC_REASONING_MODEL,而不是去思考你现在是 sonnet 还是 opus。
CPA 对这个后缀的解析逻辑非常直接,但也埋了坑。它只看字符串的最后一个 (,并且要求必须以 ) 结尾,才会把括号内的内容提取出来作为 suffix。接着,它会大小写不敏感地去匹配 minimal/low/medium/high/xhigh/max,或者特殊值 none/auto/-1,甚至是非负整数的预算值。但这里有一个非常关键的细节:源码根本没有对括号内的字符串做 trim 操作。也就是说,如果你手抖写成了 gpt-5.4(xhigh ) 或者 gpt-5.4( xhigh),哪怕只是多了一个空格,CPA 就会解析失败,直接将其当成“没有可用 thinking 配置”处理。
一旦后缀被成功识别,这就引出了下一个问题:如果请求体里本身就带有 thinking 参数,谁说了算?答案是后缀拥有绝对的最高优先级(Suffix Priority)。源码的逻辑很明确,只要模型名里带了后缀,就会走 parseSuffixToConfig 并在日志打出 thinking: config from model suffix,直接无视请求体里的配置。社区里就有人踩过这个坑:明明在 Claude Code 里切到了 /effort high,但因为模型名绑死写了 (xhigh),导致发到上游的永远是被后缀强制覆盖的 xhigh 级别。
解析完级别后,CPA 会根据你配置的上游协议进行“变形”。如果你将上游配置为 openai-compatibility,它会老老实实地将其转换成 OpenAI 协议的顶层参数 { "reasoning_effort": "xhigh" };如果走的是 codex-api-key,就会嵌套成 { "reasoning": { "effort": "xhigh" } }。更有意思的是对 Claude 和 Gemini 的处理:对于用户自定义的 Claude 模型,CPA 会直接透传 thinking.type="adaptive" 和 output_config.effort="xhigh",把决定权交给上游;而对于 Gemini 这种按 token 算预算的 provider,源码里有一套固定的映射表,(xhigh) 会被直接转换成 32768 的预算值发出去。
但这并不意味着你写了 (xhigh) 就万事大吉了。对于用户自定义的模型(比如 openai-compatibility.*.models[]),CPA 会跳过合法性校验,直接把参数塞给上游,让上游来判定。现实情况是,虽然 OpenAI 官方支持 xhigh,但很多第三方的 OpenAI 兼容后端(比如 vLLM)根本不认识这个词,只接受 low/medium/high,收到 xhigh 会直接报 400 错误。而且,CPA 配置里的 thinking.levels 默认是不包含 xhigh 的,想用的话你还得自己在配置里显式加上去。
回到我自己的 Claude Code → cc-switch → CPA → new-api 链路中。当 CPA 拿到 gpt-5.4(xhigh) 时,它本质上只是做了一次参数注入,给 new-api 发送了一个带有 reasoning_effort: "xhigh" 的标准请求,而不是去请求一个名叫 gpt-5.4-xhigh 的模型。所以,如果你的中转站卖家是严格靠不同的模型 ID 来区分计费和推理强度的,靠 CPA 解析后缀这种玩法根本没用。按这套实现机制,最稳妥的做法还是自己在上游做好 alias,或者老老实实填写真实的模型 ID。