Skip to content

feat: opt-in thinking with max_tokens budget floor (refs dengyie/cnb2api#9) - #6

Merged
godx999 merged 1 commit into
mainfrom
fix/thinking-opt-in
Oct 16, 2026
Merged

godx999 merged 1 commit into
mainfrom
fix/thinking-opt-in

Conversation

@godx999

@godx999 godx999 commented Oct 16, 2026

Copy link
Copy Markdown
Owner

背景

对应上游 issue dengyie#9(最新版双协议均无法思考)。

根因不是"上游不支持思考",而是上游的 thinking 变体会把推理阶段计入 max_tokens:
预算不足时推理吃穿预算 → finish_reason=length、content 全空,答案永不生成。

为此 src/proxy.mjs 在唯一出站点无条件剥除了 reasoning_effort / reasoning /
enable_thinking / thinking,锁死非 thinking 变体;Anthropic 侧的
fromAnthropicRequest() 本身也不透传顶层 thinking —— 两道关卡叠加即 issue 所述"双重失效"。

复现证据(对真实上游实测):

max_tokens 带开关 reasoning 字符 content 字符 finish_reason
2048 是 4,589 0 length
8192 是 20,305 0 length
2048 否 0 2,994 stop

8192 那次的 reasoning 产出约 20,305 字符,正好撞在 8192 token 的墙上 ——
推理工作正常,只是预算不够它写完。所以思考是被 max_tokens 饿死的,解法必须是
放开开关 + 同时给足预算,两者缺一不可。

本 PR 做了什么

在上游 PR dengyie#8 思路(开关 opt-in)基础上补上了缺失的预算兜底 —— 只放开开关而不给预算,
仍然会退化成空答案:

  • src/config.mjs — 新增 allowThinking(CNB2API_ALLOW_THINKING === '1')与
    thinkingMinMaxTokens(CNB2API_THINKING_MIN_MAX_TOKENS,0 = 不兜底);
    新增 fail-fast 校验(NaN / 负数启动即拒),并对"设了兜底却没开开关"给启动警告。
  • src/proxy.mjs — stripReasoningTriggers() 在开启时原样放行;新增
    applyThinkingBudgetFloor(),只抬高、不压低 max_tokens,与剥除共用同一出站点。
  • src/anthropic.mjs — fromAnthropicRequest(body, { allowThinking }) 新增第二参数;
    开启时把 thinking:{type:'enabled'} 映射为上游形态(type:'disabled' 与缺省不加字段);
    budget_tokens 在上游无对应字段,不映射。
  • src/server.mjs — 调用处传入 { allowThinking: config.allowThinking }。
  • .env.example — 新增配置段,说明为什么默认剥除。

默认行为零改动

不设 CNB2API_ALLOW_THINKING 时,所有行为与补丁前逐字节一致(原有的剥除测试原样通过)。

顺手修了一个既有 bug

test/proxy.test.mjs 里 cwd: new URL('..', import.meta.url).pathname 在 Windows 上会得到
/D:/...(前导斜杠),导致 23 个用例全部 port not up。改用 fileURLToPath 修复。

测试

# tests 77
# pass 77
# fail 0

(原始 71 + 新增 6:开关透传(流式/非流式)、预算兜底只抬不压、不设兜底时交由客户端控制、
非法兜底值 fail-fast、默认关闭时兜底不生效、Anthropic thinking 映射。)

端到端冒烟(mock 上游模拟两种变体)

A. 默认(未开 opt-in)      → 上游收 0 个思考字段,回复 ["text"]              ✅
B. opt-in + 兜底 32000      → max_tokens 2048→32000,回复 ["thinking","text"] ✅
C. opt-in 但不设兜底        → max_tokens 保持 2048,["thinking"] 正文空       ✅(复现 issue 现象)

- config: CNB2API_ALLOW_THINKING / CNB2API_THINKING_MIN_MAX_TOKENS + fail-fast
- proxy: passthrough when allowThinking, apply budget floor (raise-only)
- anthropic: fromAnthropicRequest(body, {allowThinking}) maps thinking:{type:enabled}
- server: pass allowThinking through
- tests: fix Windows cwd bug (fileURLToPath); +6 cases (71 -> 77 green)
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.

1 participant