diff --git a/tier2/gen-worker/worker/config.py b/tier2/gen-worker/worker/config.py index be3b28a2..3ec2fe84 100644 --- a/tier2/gen-worker/worker/config.py +++ b/tier2/gen-worker/worker/config.py @@ -240,11 +240,18 @@ def build_model( if max_retries is None: max_retries = genconfig.get("model", "max_retries", 2) - if max_tokens <= thinking_budget: - raise ValueError( - f"max_tokens({max_tokens}) 必须 > thinking_budget({thinking_budget}):" - "Anthropic 要求输出上限严格大于 thinking 预算(否则无 token 留给正式输出)。" - ) + # thinking 关:budget 与正式输出无关,且 AnthropicChatModel.Parameters 对 thinking_budget 有 >0 约束 + # (传 0 会被 pydantic 拒——基线证路 thinking 关时实证暴露)。故 thinking 关 → budget 归一为 None + # (Parameters 字段是 int|None,Anthropic 仅在 thinking_enable 时才用它,_model.py:174),并跳过 + # max_tokens>budget 校验(该约束只在 thinking 开时有意义)。thinking 开 → 保留硬校验(M3 路不变)。 + if thinking_enable: + if max_tokens <= thinking_budget: + raise ValueError( + f"max_tokens({max_tokens}) 必须 > thinking_budget({thinking_budget}):" + "Anthropic 要求输出上限严格大于 thinking 预算(否则无 token 留给正式输出)。" + ) + else: + thinking_budget = None resolved_base = base_url or _DEFAULT_BASE_URL # base_url 若是调用方临时换的端点,也补一次代理旁路(幂等,已在 NO_PROXY 里则不动)。 @@ -374,7 +381,8 @@ def build_baseline_model( """ # 聚合 key(Opus/Fable)→ new-api 真名;传的若已是真名(不在表里)则原样用。 resolved_name = _BASELINE_NAME_BY_KEY.get(model_name.strip().lower(), model_name) - # thinking 默认关(基线纯证路):budget=None → 0;此时 build_model 的「max_tokens>budget」恒成立。 + # thinking 默认关(基线纯证路):budget=None → 0 表示「关」;thinking 关时 build_model 会把 budget + # 归一为 None(避开 Parameters 的 thinking_budget>0 校验)并跳过 max_tokens>budget 检查。 # 显式传 budget>0 → 开 thinking,build_model 会强制校验 max_tokens>budget。 budget = 0 if thinking_budget is None else thinking_budget thinking_on = budget > 0