Skip to content

fix: carry reasoning effort through to GLM-5.3 on the OpenAI client path - #32

Merged
TriDefender merged 3 commits into
TriDefender:masterfrom
greenhat616:feat/glm-5.3-contract
Aug 29, 2026
Merged

TriDefender merged 3 commits into
TriDefender:masterfrom
greenhat616:feat/glm-5.3-contract

Conversation

@greenhat616

@greenhat616 greenhat616 commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

按 review 意见收窄:只保留 OpenAI 协议 reasoning_effort 的修复。

已移除固定模型目录的改动、update-models 探测脚本,以及随之而来的 README / config / tsconfig 改动。改动面从 18 个文件收敛到 7 个,只剩 src/translator 与 src/provider/reasoning.ts。

问题

coding-plan 的上游是 Anthropic 格式,所以 OpenAI 客户端的请求会走 translateRequestOpenAIToAnthropic。该函数只特判了 reasoning_effort:"none",其余取值一律退化成 {type:"enabled"} —— 既没有 budget,也没有 output_config。抓到的上游请求体:

客户端发送 reasoning_effort:"high", model glm-5.3
-> 上游收到 {"model":"glm-5.3","thinking":{"type":"enabled"}}
-> 实际只有 1 个字符的思考,而 effort:"high" 应有 ~244

Anthropic 端点会静默忽略 reasoning_effort,output_config.effort 才是唯一生效的通道,而且必须配对 thinking.budget_tokens —— 只给 effort 标签,上游仍停在自己近乎零的默认值上。上游对非法 effort 值和超过 max_tokens 的 budget 都不做校验,所以归一化只能放在这里做。

改动

  • 新增 src/provider/reasoning.ts,把契约集中在一处:三档 effort 配对 budget 8000 / 16000 / 32000,默认档 max —— 取自 ZCode 自带目录里的 glm-5.3 条目。effort 归一化遵循 Z.AI 公布的映射表,向上取整:medium 映射到 high 而不是 low,这条有具名回归测试。
  • openai-to-anthropic(coding-plan 方向):GLM-5.3 系列请求一律带上显式 effort 与配对 budget,budget 会 fit 进 max_tokens 并为回答保留 1024 token。
  • anthropic-to-openai(start-plan 方向):两边通道相反,把 output_config.effort 带进 reasoning_effort。

默认值改动仅限 GLM-5.3 系列,均对齐 ZCode:未指定 reasoning_effort 时取 max,未指定 max_tokens 时回退到该模型目录里的 maxOutputTokens 而非通用的 4096。后者是前者的前提 —— ZCode 的 min(budget, maxOutputTokens - 1) 是针对模型上限写的,套到 4096 的兜底值上会把几乎整个额度吃进思考里。

其他模型行为完全不变,测试有断言。output_config 的透传也 gate 在 GLM-5.3 系列内。

关于模型目录

按 review 意见,本 PR 不改目录。这里的模型匹配走 model id 正则、不依赖目录收录,所以目录里没有的同系列模型(今天的 glm-5.3-flash)一样能拿到 effort 通道;只有 max_tokens 的兜底会查目录,查不到就退回通用的 4096,不会失败。两种情况都有测试覆盖。

Copilot review

  • explicitBudget > 0 之后才 floor —— 已改为先 floor 再判正。否则 0.5 能通过 > 0 却 floor 成 0,在 max_tokens 非有限数时会原样传给上游变成 budget_tokens: 0。补了 2 个回归测试。
  • output_config.effort 对所有模型透传 —— 已 gate 到 GLM-5.3 系列。补了 glm-4.7 的断言测试。
  • 另外两条(update-models 的 CLI 参数校验、body-transformer 的 output_config 处理)所在代码已随本次收窄一并移除。

验证

bun test 635 pass / 0 fail,tsc --noEmit 通过。

Copilot AI lite review requested due to automatic review settings August 29, 2026 06:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are a few concrete correctness issues in the new logic (notably CLI arg parsing and GLM-5.3 output_config/effort handling) that should be fixed before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR expands the proxy’s pinned GLM model catalog (notably adding glm-5.3-flash) and fixes GLM-5.3-family “reasoning effort” handling by introducing a shared reasoning contract and wiring it through request translators and passthrough body transforms. It also adds a probing-based update-models script (plus tests) to help maintain src/provider/models.ts safely.

Changes:

  • Add a GLM-5.3 reasoning-effort contract (output_config.effort + thinking.budget_tokens) and apply it in OpenAI↔Anthropic translators and passthrough transforms.
  • Update the pinned model catalog/specs (incl. glm-5.3-flash) and align configs/docs/tests with the new catalog.
  • Add bun run update-models probing script + unit tests and include scripts/**/* in TS compilation.
File summaries
File Description
tsconfig.json Includes scripts/**/* so the new probing script typechecks.
src/translator/types.ts Adds output_config typing for Anthropic requests (GLM-5.3 effort channel).
src/translator/openai-to-anthropic.ts Implements GLM-5.3 effort/budget translation and model-aware max_tokens defaulting.
src/translator/openai-to-anthropic.test.ts Adds regression tests covering GLM-5.3(-flash) effort/budget behavior.
src/translator/anthropic-to-openai.ts Carries output_config.effort into OpenAI reasoning_effort and rewrites disabled thinking for GLM-5.3.
src/translator/anthropic-to-openai.test.ts New tests for output_config.effort carry-over and disabled-thinking rewrite.
src/proxy/body-transformer.ts Adds passthrough-route rewrites for GLM-5.3 disabled thinking (OpenAI + Anthropic shapes).
src/proxy/body-transformer.test.ts Adds tests for passthrough-route disabled-thinking rewrites and budget fitting.
src/provider/reasoning.ts New module defining GLM-5.3 effort levels, budgets, model matching, and budget fitting.
src/provider/reasoning.test.ts Unit tests for the GLM-5.3 reasoning contract helpers.
src/provider/providers.test.ts Updates model catalog assertions (context windows, maxOutputTokens, reasoning flags, exclusions).
src/provider/models.ts Updates pinned model list/specs; adds glm-5.3-flash and adjusts other model metadata.
src/config/template.ts Updates template model list to match the updated pinned catalog.
scripts/update-models.ts Adds probing-based catalog regeneration script with a two-stage availability/ceiling probe.
scripts/update-models.test.ts Unit tests for the script’s pure functions (classification/parsing/merge policy).
README.md Updates documented pinned model table/specs and adds notes about aliasing/availability.
package.json Adds update-models script entry.
config.example.yaml Updates example model list to match the updated pinned catalog.
Review details
  • Files reviewed: 17/18 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/update-models.ts Outdated
Comment on lines +144 to +155
case "--provider": {
const v = argv[++i];
if (v !== "zai" && v !== "bigmodel") throw new Error(`--provider must be zai|bigmodel, got "${v}"`);
args.provider = v;
break;
}
case "--api-key":
args.apiKey = argv[++i];
break;
case "--config":
args.configPath = argv[++i];
break;
Comment on lines +151 to +154
const explicitBudget = explicit.budget_tokens ?? explicit.budgetTokens;
if (typeof explicitBudget === "number" && Number.isFinite(explicitBudget) && explicitBudget > 0) {
budget = Math.floor(explicitBudget);
}
Comment thread src/proxy/body-transformer.ts Outdated
Comment on lines +136 to +147
function applyGlm53AnthropicThinkingCompat(body: Record<string, unknown>): boolean {
if (!glm53Model(body) || !thinkingIsDisabled(body)) return false;
const legal = buildGlm53Reasoning(GLM53_DISABLED_REPLACEMENT_EFFORT);
const budget = fitGlm53Budget(legal.thinking.budget_tokens, body.max_tokens);
body.thinking = budget === undefined
? { type: "enabled" }
: { type: "enabled", budget_tokens: budget };
if (!isPlainObject(body.output_config)) {
body.output_config = legal.output_config;
}
return true;
}
Comment on lines +52 to +57
// GLM-5.3+ Anthropic clients set reasoning effort via `output_config.effort`
// (the Anthropic upstream ignores `reasoning_effort`); the OpenAI-format
// upstream on start-plan reads `reasoning_effort` instead, so carry it over.
if (req.output_config?.effort) {
result.reasoning_effort = req.output_config.effort as OpenAIChatRequest["reasoning_effort"];
}
@TriDefender TriDefender self-assigned this Aug 29, 2026
@TriDefender

Copy link
Copy Markdown
Owner

请只提交一个修复OpenAI协议 reasoning_effort PR,模型列表和上下文问题均为 NOT A BUG
原因如下:
部分harness 例如 OpenCode 不接受自动探测到的模型能力,更多依赖于用户手动配置或者预先维护的模型能力列表
5v turbo你没有我有,完全是个人账号权限问题
proxy并不会阻止你去调用某个未被列出的模型,这是防止zai放出新模型我就得更新一次

On coding-plan the upstream is Anthropic-format, so an OpenAI client's
request goes through translateRequestOpenAIToAnthropic. That function
only special-cased reasoning_effort:"none"; every other value degraded
to `{type:"enabled"}` with no budget and no output_config. A captured
upstream body:

  client sent reasoning_effort:"high", model glm-5.3
  -> upstream got {"model":"glm-5.3","thinking":{"type":"enabled"}}
  -> 1 character of thinking, where effort:"high" yields ~244

The Anthropic endpoint silently ignores reasoning_effort, so
output_config.effort is the only channel that works there, and the
upstream validates neither an illegal effort value nor a budget larger
than max_tokens — normalizing has to happen here.

New src/provider/reasoning.ts holds the contract in one place, taken
from ZCode's catalog entry for glm-5.3: three levels with budgets
8000 / 16000 / 32000, defaultLevel "max". Effort normalization follows
Z.AI's published table, which rounds up — "medium" maps to "high", not
"low"; there is a named regression test for that.

Also carries output_config.effort into reasoning_effort in the
start-plan direction (Anthropic client, OpenAI upstream), where the
channels are reversed. That carry-over is gated to the GLM-5.3 family:
output_config is this family's extension, and forwarding it for every
model would let a client reroute another model's reasoning through a
channel it never opted into.

Two default-value changes for the GLM-5.3 family only, both following
ZCode: a request that omits reasoning_effort now gets effort "max"
rather than the gateway's near-zero default, and one that omits
max_tokens falls back to the model's catalog maxOutputTokens rather
than the generic 4096. The second is load-bearing for the first —
ZCode's clamp of `min(budget, maxOutputTokens - 1)` is written against
the model ceiling, and applying it to a 4096 fallback left a 4095-token
thinking budget with nothing for the answer. fitGlm53Budget now
reserves 1024 tokens for the answer and floors a fractional max_tokens.

Membership of the pinned catalog is a hint here, never a gate. Model
matching is by id pattern, so a family member the catalog does not list
(glm-5.3-flash today) still gets the effort channel; only the
max_tokens fallback consults the catalog, and it degrades to the
generic 4096 default rather than failing. Tests cover both.

Net effect on a bare OpenAI request for glm-5.3: max_tokens 4096 and
~1 character of thinking becomes max_tokens 128_000 with a 32_000
thinking budget. That is ZCode parity, and it costs more.
@greenhat616
greenhat616 force-pushed the feat/glm-5.3-contract branch from 2bb2d5f to 675298b Compare August 29, 2026 16:30
@greenhat616 greenhat616 changed the title feat: add glm-5.3-flash and update-models scripts fix: carry reasoning effort through to GLM-5.3 on the OpenAI client path Aug 29, 2026
TriDefender and others added 2 commits August 29, 2026 17:32
…ns glm-5.3-flash

Merge of master (1646a4c 'feat(models): advertise glm-5.3-flash') invalidated
the premise that glm-5.3-flash is absent from the pinned catalog: the fixture
now resolves the model-aware default (128,000) instead of the generic 4096.

- Point the unlisted-family fixture at a genuinely unlisted id (glm-5.3-air),
  preserving the original intent: the effort channel is keyed off the model
  id, not catalog membership.
- Pin the new catalog reality: glm-5.3-flash gets the same model-aware
  default as glm-5.3 (max_tokens 128,000, max-effort budget 32,000).
@TriDefender
TriDefender merged commit e36b4d7 into TriDefender:master Aug 29, 2026
1 check passed
TriDefender added a commit that referenced this pull request Sep 7, 2026
…nk 最小放宽

- CL-16: handler.ts 顶部注释重写为 v2.6 现实(两套餐均 Anthropic 上游,
  OpenAI 客户端 O→A 翻译,Anthropic 客户端原生 passthrough);
  passthroughResponse docstring 改为如实描述 accept-encoding 转发;
  claim/client.ts 引用去掉不存在的 NOTEPAD 章节名
- R2-11: manager.ts 'refreshing if necessary' 过度承诺改为如实描述
  (凭据过期由上游 401 暴露,守卫保留备未来填充 expiresAt)
- CL-17: 抽共享 src/proxy/inflate.ts inflateWithCap(DecompressionStream
  + 上限 + cancel + releaseLock);proxy/handler.ts 与 async/handler.ts
  两处解压循环改调,64MiB/4MiB 上限与错误契约(throw vs errorResponse)
  保留在各自调用方
- CL-15: /v1/responses userId 门控对齐 handler.ts(startPlan 不注入
  metadata.user_id),留档 bundle 为 presence-gated 无套餐概念
- R2-18: parseSSEChunk data: 前缀放宽(至多剥一个前导空格,'data: x'
  输出逐字节不变);帧分割容忍 \r\n\r\n(导出 SSE_FRAME_SPLIT,与
  3.11.2 bundle Kxo 正则一致),anthropicSseToOpenaiSse 与 async 流适配器
  的缓冲分割同步切换——CRLF 流不再永久缓冲;:496 第二解析器与其余解析点
  不动(#32)
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.

3 participants