mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 14:52:09 +03:00
* fix(nvidia): map GLM-5.2 reasoning to thinking toggle Fixes #7215. * fix(nvidia): shrink default.ts under the file-size ratchet The GLM-5.2 reasoning-mapping call in requestBodyDefaults() pushed open-sse/executors/default.ts from 877 to 881 lines, tripping the frozen check:file-size ceiling (Fast Quality Gates). withDefaults is typed unknown, so the `as typeof withDefaults` cast added by the multi-line call was unnecessary — collapsing to a single-line call removes the cast and the line-wrap, landing the file at 876 lines. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * refactor(nvidia): extract mapNvidiaGlm52ReasoningParams helpers to clear complexity ratchet mapNvidiaGlm52ReasoningParams landed at cyclomatic complexity 24 (limit 15), a brand-new violation that pushed the project-wide complexity ratchet from 2056 to 2057 (Fast Quality Gates: check:complexity-ratchets). It was previously masked by the file-size failure aborting the job before this step ran. Split the function into three single-purpose helpers — effort extraction, chat_template_kwargs construction, and the reasoning_effort/reasoning.effort strip — bringing the orchestrating function's complexity back under threshold with no behavior change (all 41 cases in tests/unit/base-executor-sanitize-effort.test.ts still pass unchanged). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(nvidia): restore default executor file-size gate --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
This commit is contained in:
@@ -38,6 +38,105 @@ export const MISTRAL_NO_REASONING_EFFORT_PATTERN = /devstral/i;
|
||||
// Order matters: the opt-in check must run BEFORE the broad Claude/haiku/oswe strip.
|
||||
export const GITHUB_REASONING_EFFORT_OPT_IN_PATTERN = /claude[-_.]?(?:opus|sonnet)[-_.]?4[-_.]6/i;
|
||||
export const GITHUB_NO_REASONING_EFFORT_PATTERN = /(claude|haiku|oswe)/i;
|
||||
const NVIDIA_GLM_52_PATTERN = /z-ai\/glm-5\.2\b/i;
|
||||
|
||||
type ReasoningSanitizeLog = {
|
||||
info?: (tag: string, msg: string) => void;
|
||||
};
|
||||
|
||||
function isNvidiaGlm52(provider: string, model: string | undefined): boolean {
|
||||
return provider === "nvidia" && NVIDIA_GLM_52_PATTERN.test(model || "");
|
||||
}
|
||||
|
||||
type NvidiaGlm52EffortInfo = {
|
||||
reasoning: Record<string, unknown> | null;
|
||||
effortStr: string;
|
||||
};
|
||||
|
||||
/** Pulls a normalized (lowercased) effort string out of top-level or nested `reasoning.effort`. */
|
||||
function extractNvidiaGlm52Effort(b: Record<string, unknown>): NvidiaGlm52EffortInfo | null {
|
||||
const reasoning =
|
||||
b.reasoning && typeof b.reasoning === "object" && !Array.isArray(b.reasoning)
|
||||
? (b.reasoning as Record<string, unknown>)
|
||||
: null;
|
||||
const effort = b.reasoning_effort ?? reasoning?.effort;
|
||||
if (effort === undefined) return null;
|
||||
|
||||
const effortStr = typeof effort === "string" ? effort.toLowerCase() : "";
|
||||
if (!effortStr) return null;
|
||||
|
||||
return { reasoning, effortStr };
|
||||
}
|
||||
|
||||
/** Builds `chat_template_kwargs.enable_thinking`, or null when the existing kwargs shape is unusable. */
|
||||
function buildNvidiaGlm52TemplateKwargs(
|
||||
rawTemplateKwargs: unknown,
|
||||
effortStr: string
|
||||
): Record<string, unknown> | null {
|
||||
if (
|
||||
rawTemplateKwargs !== undefined &&
|
||||
(!rawTemplateKwargs ||
|
||||
typeof rawTemplateKwargs !== "object" ||
|
||||
Array.isArray(rawTemplateKwargs))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const templateKwargs = {
|
||||
...((rawTemplateKwargs as Record<string, unknown> | undefined) ?? {}),
|
||||
};
|
||||
if (!Object.prototype.hasOwnProperty.call(templateKwargs, "enable_thinking")) {
|
||||
templateKwargs.enable_thinking = effortStr !== "none";
|
||||
}
|
||||
return templateKwargs;
|
||||
}
|
||||
|
||||
/** Returns a copy of `b` with `reasoning_effort`/`reasoning.effort` replaced by `templateKwargs`. */
|
||||
function withNvidiaGlm52TemplateKwargs(
|
||||
b: Record<string, unknown>,
|
||||
templateKwargs: Record<string, unknown>,
|
||||
reasoning: Record<string, unknown> | null
|
||||
): Record<string, unknown> {
|
||||
const next: Record<string, unknown> = { ...b, chat_template_kwargs: templateKwargs };
|
||||
delete next.reasoning_effort;
|
||||
if (reasoning) {
|
||||
const nextReasoning = { ...reasoning };
|
||||
delete nextReasoning.effort;
|
||||
if (Object.keys(nextReasoning).length === 0) delete next.reasoning;
|
||||
else next.reasoning = nextReasoning;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map OmniRoute's reasoning-effort inputs onto the binary thinking switch exposed by
|
||||
* NVIDIA's hosted GLM-5.2 chat template. This runs before DefaultExecutor's unsupported
|
||||
* parameter stripping so a nested `reasoning.effort` is not discarded first, and is also
|
||||
* reused by the final provider sanitizer for non-default execution paths.
|
||||
*/
|
||||
export function mapNvidiaGlm52ReasoningParams(
|
||||
body: unknown,
|
||||
provider: string,
|
||||
model: string | undefined,
|
||||
log?: ReasoningSanitizeLog | null
|
||||
): unknown {
|
||||
if (!isNvidiaGlm52(provider, model)) return body;
|
||||
if (!body || typeof body !== "object" || Array.isArray(body)) return body;
|
||||
|
||||
const b = body as Record<string, unknown>;
|
||||
const info = extractNvidiaGlm52Effort(b);
|
||||
if (!info) return body;
|
||||
|
||||
const templateKwargs = buildNvidiaGlm52TemplateKwargs(b.chat_template_kwargs, info.effortStr);
|
||||
if (!templateKwargs) return body;
|
||||
|
||||
const next = withNvidiaGlm52TemplateKwargs(b, templateKwargs, info.reasoning);
|
||||
log?.info?.(
|
||||
"REASONING_SANITIZE",
|
||||
`nvidia/${model || ""}: mapped reasoning effort to enable_thinking`
|
||||
);
|
||||
return next;
|
||||
}
|
||||
|
||||
export function supportsMaxEffortForProvider(provider: string, model: string): boolean {
|
||||
const isClaude =
|
||||
@@ -142,8 +241,12 @@ export function sanitizeReasoningEffortForProvider(
|
||||
body: unknown,
|
||||
provider: string,
|
||||
model: string | undefined,
|
||||
log?: { info?: (tag: string, msg: string) => void } | null
|
||||
log?: ReasoningSanitizeLog | null
|
||||
): unknown {
|
||||
if (isNvidiaGlm52(provider, model)) {
|
||||
return mapNvidiaGlm52ReasoningParams(body, provider, model, log);
|
||||
}
|
||||
|
||||
if (!body || typeof body !== "object" || Array.isArray(body)) return body;
|
||||
const b = body as Record<string, unknown>;
|
||||
const c = readEffortCarriers(b);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BaseExecutor, type ExecuteInput } from "./base.ts";
|
||||
import { mapNvidiaGlm52ReasoningParams } from "./base/reasoningEffort.ts";
|
||||
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.ts";
|
||||
import { getAccessToken } from "../services/tokenRefresh.ts";
|
||||
|
||||
@@ -701,14 +702,13 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
);
|
||||
}
|
||||
|
||||
// Config-driven strip of params unsupported by the target provider/model
|
||||
// (e.g. claude-opus-4 deprecated `temperature` → Anthropic 400). Port from
|
||||
// 9router#7ae9fff6 (fixes upstream #1748). Rules live in
|
||||
// ../translator/paramSupport.ts so adding one means editing one table.
|
||||
// Strip params unsupported by the target provider/model before sending upstream.
|
||||
// Rules live in ../translator/paramSupport.ts (9router#7ae9fff6; fixes #1748).
|
||||
if (typeof withDefaults === "object" && withDefaults !== null) {
|
||||
const bodyRecord = withDefaults as Record<string, unknown>;
|
||||
const outboundModel = typeof bodyRecord.model === "string" ? bodyRecord.model : model;
|
||||
stripUnsupportedParams(this.provider, outboundModel, bodyRecord);
|
||||
withDefaults = mapNvidiaGlm52ReasoningParams(bodyRecord, this.provider, outboundModel);
|
||||
stripUnsupportedParams(this.provider, outboundModel, withDefaults as Record<string, unknown>);
|
||||
}
|
||||
|
||||
// Apply modelIdPrefix from RegistryEntry (e.g. "accounts/fireworks/models/")
|
||||
|
||||
@@ -2,6 +2,7 @@ import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { sanitizeReasoningEffortForProvider } = await import("../../open-sse/executors/base.ts");
|
||||
const { DefaultExecutor } = await import("../../open-sse/executors/default.ts");
|
||||
|
||||
function makeLog() {
|
||||
const messages: Array<[string, string]> = [];
|
||||
@@ -394,6 +395,114 @@ test("sanitizeReasoningEffortForProvider: non-object body returns unchanged", ()
|
||||
assert.equal(sanitizeReasoningEffortForProvider(arr, "xiaomi-mimo", "x", null), arr);
|
||||
});
|
||||
|
||||
// ── NVIDIA NIM GLM-5.2 (#7215) ─────────────────────────────────────────────
|
||||
|
||||
test("sanitizeReasoningEffortForProvider: NVIDIA GLM-5.2 enables thinking for active effort", () => {
|
||||
for (const effort of ["low", "medium", "high", "xhigh", "max"]) {
|
||||
const body = { reasoning_effort: effort, messages: [] };
|
||||
const result = sanitizeReasoningEffortForProvider(
|
||||
body,
|
||||
"nvidia",
|
||||
"z-ai/glm-5.2",
|
||||
null
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.notEqual(result, body);
|
||||
assert.equal(result.reasoning_effort, undefined);
|
||||
assert.deepEqual(result.chat_template_kwargs, { enable_thinking: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("sanitizeReasoningEffortForProvider: NVIDIA GLM-5.2 maps none to thinking off", () => {
|
||||
const result = sanitizeReasoningEffortForProvider(
|
||||
{ reasoning_effort: "none", messages: [] },
|
||||
"nvidia",
|
||||
"z-ai/glm-5.2",
|
||||
null
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.equal(result.reasoning_effort, undefined);
|
||||
assert.deepEqual(result.chat_template_kwargs, { enable_thinking: false });
|
||||
});
|
||||
|
||||
test("sanitizeReasoningEffortForProvider: NVIDIA GLM-5.2 maps nested reasoning.effort", () => {
|
||||
const result = sanitizeReasoningEffortForProvider(
|
||||
{ reasoning: { effort: "high" }, messages: [] },
|
||||
"nvidia",
|
||||
"z-ai/glm-5.2",
|
||||
null
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.equal(result.reasoning, undefined);
|
||||
assert.deepEqual(result.chat_template_kwargs, { enable_thinking: true });
|
||||
});
|
||||
|
||||
test("DefaultExecutor: NVIDIA GLM-5.2 maps nested effort before unsupported-param stripping", () => {
|
||||
const result = new DefaultExecutor("nvidia").transformRequest(
|
||||
"z-ai/glm-5.2",
|
||||
{
|
||||
model: "z-ai/glm-5.2",
|
||||
reasoning: { effort: "high", summary: "auto" },
|
||||
messages: [],
|
||||
},
|
||||
false,
|
||||
{}
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.equal(result.reasoning, undefined);
|
||||
assert.equal(result.reasoning_effort, undefined);
|
||||
assert.deepEqual(result.chat_template_kwargs, { enable_thinking: true });
|
||||
});
|
||||
|
||||
test("DefaultExecutor: NVIDIA reasoning levels remain intact for GPT-OSS", () => {
|
||||
const result = new DefaultExecutor("nvidia").transformRequest(
|
||||
"openai/gpt-oss-120b",
|
||||
{
|
||||
model: "openai/gpt-oss-120b",
|
||||
reasoning_effort: "low",
|
||||
messages: [],
|
||||
},
|
||||
false,
|
||||
{}
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.equal(result.reasoning_effort, "low");
|
||||
assert.equal(result.chat_template_kwargs, undefined);
|
||||
});
|
||||
|
||||
test("sanitizeReasoningEffortForProvider: NVIDIA GLM-5.2 preserves a native thinking switch", () => {
|
||||
const result = sanitizeReasoningEffortForProvider(
|
||||
{
|
||||
reasoning_effort: "xhigh",
|
||||
chat_template_kwargs: { enable_thinking: false, custom_flag: true },
|
||||
messages: [],
|
||||
},
|
||||
"nvidia",
|
||||
"z-ai/glm-5.2",
|
||||
null
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.equal(result.reasoning_effort, undefined);
|
||||
assert.deepEqual(result.chat_template_kwargs, {
|
||||
enable_thinking: false,
|
||||
custom_flag: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("sanitizeReasoningEffortForProvider: NVIDIA GLM-5.2 mapping is narrowly scoped", () => {
|
||||
const otherModel = { reasoning_effort: "high", messages: [] };
|
||||
const otherProvider = { reasoning_effort: "high", messages: [] };
|
||||
|
||||
assert.equal(
|
||||
sanitizeReasoningEffortForProvider(otherModel, "nvidia", "z-ai/glm-5.1", null),
|
||||
otherModel
|
||||
);
|
||||
assert.equal(
|
||||
sanitizeReasoningEffortForProvider(otherProvider, "openai", "z-ai/glm-5.2", null),
|
||||
otherProvider
|
||||
);
|
||||
});
|
||||
|
||||
// ── Native DeepSeek (api.deepseek.com) ───────────────────────────────────────
|
||||
// DeepSeek V4 thinking mode accepts reasoning_effort ONLY as {high, max}. The
|
||||
// internal OmniRoute scale (low|medium|high|xhigh, xhigh = top) must be mapped
|
||||
|
||||
Reference in New Issue
Block a user