Repository navigation
fix(providers): map agentrouter GLM thinking.type adaptive to enabled - #14043
Merged
diegosouzapw merged 1 commit intoSep 18, 2026
Merged
Conversation
…#13696) AgentRouter routes GLM models through the generic DefaultExecutor, which has no GLM-specific handling. When the connection's OpenAI-compatible alternate format is used, a Claude-style thinking:{type:"adaptive"} field survived stripUnsupportedParams untouched and reached AgentRouter's upstream GLM endpoint verbatim, which 400s (thinking.type "adaptive" is not supported by glm models; must be one of enabled, disabled). Add a mapThinkingType mechanism to paramSupport.ts's STRIP_RULES (in addition to the existing drop/clamp mechanisms) and scope a rule to provider "agentrouter" + model matching /glm-/i that remaps thinking.type from "adaptive" to "enabled", preserving any other thinking fields (e.g. budget_tokens) — mirroring the same mapping GlmExecutor already performs for its own provider.
Collaborator
Author
|
Validated in local merge-train /tmp/mt-train3b.log on 192.168.0.113 @ train tip 408e2128791696a18966681953136fc96aeb99b0 (32 PRs boarded): static gates green; full test:unit 40694 tests, 17 failing — every one reproduces on the pure release tip (base-red sweep list), zero new reds. Merged --admin per merge-gates §4/§7. |
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…cheaper-inference#13696) (cheaper-inference#14043) AgentRouter routes GLM models through the generic DefaultExecutor, which has no GLM-specific handling. When the connection's OpenAI-compatible alternate format is used, a Claude-style thinking:{type:"adaptive"} field survived stripUnsupportedParams untouched and reached AgentRouter's upstream GLM endpoint verbatim, which 400s (thinking.type "adaptive" is not supported by glm models; must be one of enabled, disabled). Add a mapThinkingType mechanism to paramSupport.ts's STRIP_RULES (in addition to the existing drop/clamp mechanisms) and scope a rule to provider "agentrouter" + model matching /glm-/i that remaps thinking.type from "adaptive" to "enabled", preserving any other thinking fields (e.g. budget_tokens) — mirroring the same mapping GlmExecutor already performs for its own provider.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
AgentRouter routes GLM models through the generic
DefaultExecutor(executor:"default"), which has no GLM-specific handling. When the connection's OpenAI-compatible alternate format is used and the client request already carries a Claude-stylethinking: {type: "adaptive"}field (source format == target format =="openai", sotranslateRequest's hub-and-spoke conversion is skipped), that field survivedstripUnsupportedParamsuntouched —STRIP_RULESinopen-sse/translator/paramSupport.tshad no rule for provideragentrouter+glm-*— and reached AgentRouter's upstream GLM endpoint verbatim, which 400s withthinking.type "adaptive" is not supported by glm models; must be one of enabled, disabled.The native GLM/ZAI executor (
open-sse/executors/glm.ts:404-420) already performs exactly thisadaptive→enabledremap for its own provider; AgentRouter just never reaches that code path since it uses the genericDefaultExecutorinstead ofGlmExecutor.Fix
Extended the
StripRuleshape inopen-sse/translator/paramSupport.tswith an optionalmapThinkingTypemap (alongside the existingdrop/clamp mechanisms), and added a rule scoped toprovider: "agentrouter"+match: /glm-/ithat remapsthinking.typefrom"adaptive"to"enabled", preserving any otherthinkingfields (e.g.budget_tokens) instead of dropping the whole field. Scoped to the wholeglm-*family (not a single model id), since AgentRouter's static catalog carries noglm-*entries at all —passthroughModels: truelets arbitrary GLM ids through, so the fix must not be tied to one model.Regression test
tests/unit/agentrouter-glm-thinking-adaptive-map.test.ts(new):agentrouter+glm-5.3withthinking.type: "adaptive"→ assertsthinkingbecomes{type: "enabled", budget_tokens: ...}(other fields preserved)agentrouter+claude-opus-5(regression guard) →thinking.typestays"adaptive"agentrouterprovider with aglm-*model id →thinking.typestays"adaptive"(native GLM executor handles its own mapping separately)agentrouter+glm-5.3with nothinkingfield → no-op, nothing introducedRED (before fix):
AssertionError: adaptive must be mapped to enabled for agentrouter GLM models, preserving other thinking fields(actual{type: 'adaptive', budget_tokens: 1024}vs expected{type: 'enabled', budget_tokens: 1024}).GREEN (after fix): all 4 new tests pass;
npm run test:unitequivalent focused run —node --import tsx/esm --test tests/unit/agentrouter-glm-thinking-adaptive-map.test.ts→ 4/4 pass.Also updated the existing structural assertion in
tests/unit/executors-strip-unsupported-params.test.ts("STRIP_RULES is non-empty and every rule has a drop list or a clamp mechanism") to also accept the newmapThinkingTypemechanism — it previously only recognized drop/clamp rules and would otherwise fail on the new rule shape. This is an alignment to the corrected contract (aStripRulemay now sanitize by remapping a value, not only by dropping/clamping), not a weakening: the assertion still requires every rule to do something (drop, clamp, or map), just enumerates the new legitimate option.Gates run
node scripts/check/check-file-size.mjs— OK (no violation on touched files; the reported test-file-size violation ontests/unit/chatcore-translation-paths.test.tsis pre-existing/untouched by this PR)node scripts/check/check-complexity.mjs— OK (2857 violations vs baseline 3218)node scripts/check/check-cognitive-complexity.mjs— OK (1291 violations vs baseline 1437)npm run typecheck:core— exit 0npm run check:open-sse-typecheck— OK, 0 errorsnpx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files>— cleanazure-max-output-clamp,chatcore-unsupported-params-strip,default-reasoning-effort-6879,executors-strip-unsupported-params,glm-executor-max-tokens-clamp-7364,nvidia-minimax-thinking-strip,nvidia-strip-prompt-cache-key,param-filters-apply,param-filters-db,zai-glm-max-tokens-clamp-7364— all passnode scripts/check/check-changelog-integrity.mjs— OKLive validation (Hard Rule #18)
Not performed against the production VPS in this pass — the fix is validated via TDD (a failing-then-passing regression test proving the exact reported 400 condition), which is the preferred validation path per the bug-fix protocol. The reported error string and root cause were confirmed by the prior triage's TDD probe and by the existing in-repo precedent (
glm.ts's identical adaptive→enabled mapping for its own provider, and the identical NVIDIA NIMz-ai/glm-5.2clash already fixed inparamSupport.ts).Closes #13696