问题描述
当客户端只发 max_completion_tokens(不发 max_tokens)时,代理会用 max_tokens_cap_per_model 自动补上 max_tokens 并一起转发给上游。opencode zen 的部分后端实例会严格校验,返回:
400 [invalid_request_error] `max_tokens` and `max_completion_tokens` cannot both be set; use `max_completion_tokens`.
由于只有部分后端实例校验(同一请求重放经常成功),表现为偶发失败,很容易被误判为上游抖动。客户端侧(如 ZCode)对 400 不重试,用户会看到硬失败。
环境
- opencode2api v0.14.17(darwin/arm64,commit ea1f473)
- 上游:opencode zen 免费层(
Authorization: Bearer public)
复现与证据(本地假上游抓包)
客户端请求:stream=true + tools + thinking/reasoning 参数,仅带 "max_completion_tokens": 32000,无 max_tokens(代理日志 request_plan ... max_tokens=<nil> max_tokens_cap=200000 ...)。
代理实际转发到上游的请求体同时包含:
{"max_tokens": 200000, "max_completion_tokens": 32000, ...}
对照实验(同一客户端请求,仅修改 max_tokens_cap_per_model):
| 配置 |
转发给上游的 max_tokens |
{"big-pickle": 200000} |
200000(被填充)← 冲突来源 |
{"big-pickle": 0} |
不出现 |
{}(空) |
不出现 |
期望行为
仅当客户端的 max_tokens 与 max_completion_tokens 都未提供时才做填充;客户端已带 max_completion_tokens 时应原样转发(或填充时同步清除冲突参数)。
临时规避
将 max_tokens_cap_per_model 全部设为 0,可关闭填充、避开该问题。
问题描述
当客户端只发
max_completion_tokens(不发max_tokens)时,代理会用max_tokens_cap_per_model自动补上max_tokens并一起转发给上游。opencode zen 的部分后端实例会严格校验,返回:由于只有部分后端实例校验(同一请求重放经常成功),表现为偶发失败,很容易被误判为上游抖动。客户端侧(如 ZCode)对 400 不重试,用户会看到硬失败。
环境
Authorization: Bearer public)复现与证据(本地假上游抓包)
客户端请求:
stream=true+ tools + thinking/reasoning 参数,仅带"max_completion_tokens": 32000,无max_tokens(代理日志request_plan ... max_tokens=<nil> max_tokens_cap=200000 ...)。代理实际转发到上游的请求体同时包含:
{"max_tokens": 200000, "max_completion_tokens": 32000, ...}对照实验(同一客户端请求,仅修改
max_tokens_cap_per_model):max_tokens{"big-pickle": 200000}200000(被填充)← 冲突来源{"big-pickle": 0}{}(空)期望行为
仅当客户端的
max_tokens与max_completion_tokens都未提供时才做填充;客户端已带max_completion_tokens时应原样转发(或填充时同步清除冲突参数)。临时规避
将
max_tokens_cap_per_model全部设为0,可关闭填充、避开该问题。