Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/features/10896-glm-5.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **feat(sse):** add GLM-5.3 support (`glm-5.3`, `glm-5.3-high`, `glm-5.3-low`) across the z.ai first-party providers, mapping the upstream `reasoning_effort` request parameter to the existing 5.2 tier UX ([#10896](https://github.com/diegosouzapw/OmniRoute/pull/10896)) — thanks @phuongddx
29 changes: 29 additions & 0 deletions open-sse/config/glmProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,35 @@ export const GLM_ANTHROPIC_DEFAULT_BASE_URLS = Object.freeze({
});

export const GLM_SHARED_MODELS = Object.freeze([
{
// GLM-5.3 (2026-08-14): one upstream id; effort is the reasoning_effort
// param (low|high|max, default max) — the -high/-low entries below are
// OmniRoute aliases resolved by GlmExecutor::parseGlmEffortTier.
// Default context window not yet published by Z.ai; 1M mirrored from
// GLM-5.2 (same base model). https://z.ai/blog/glm-5.3
id: "glm-5.3",
name: "GLM 5.3",
contextLength: 1000000,
maxOutputTokens: 131072,
toolCalling: true,
supportsReasoning: true,
},
{
id: "glm-5.3-high",
name: "GLM 5.3 High",
contextLength: 1000000,
maxOutputTokens: 131072,
toolCalling: true,
supportsReasoning: true,
},
{
id: "glm-5.3-low",
name: "GLM 5.3 Low",
contextLength: 1000000,
maxOutputTokens: 131072,
toolCalling: true,
supportsReasoning: true,
},
{
id: "glm-5.2",
name: "GLM 5.2",
Expand Down
14 changes: 8 additions & 6 deletions open-sse/config/providers/registry/zai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ export const zaiProvider: RegistryEntry = {
authType: "apikey",
authHeader: "x-api-key",
headers: getAnthropicCompatHeaders(),
// Real upstream model IDs only. The effort tiers (glm-5.2-high / glm-5.2-max)
// are intentionally NOT listed here: they are OmniRoute aliases resolved by the
// GlmExecutor (parseGlm52Effort → base "glm-5.2" + effort field). This provider
// uses the DefaultExecutor, which sends the model ID verbatim, so the aliases
// would reach z.ai's Anthropic endpoint as unknown IDs. Use the `glm` provider
// for effort tiers. Vision models are likewise omitted (handled elsewhere).
// Real upstream model IDs only. The effort tiers (glm-5.2-high/-max,
// glm-5.3-high/-low) are intentionally NOT listed here: they are OmniRoute
// aliases resolved by the GlmExecutor (parseGlmEffortTier → base model +
// effort selector). This provider uses the DefaultExecutor, which sends the
// model ID verbatim, so the aliases would reach z.ai's Anthropic endpoint as
// unknown IDs. Use the `glm` provider for effort tiers. Vision models are
// likewise omitted (handled elsewhere).
models: [
{ id: "glm-5.3", name: "GLM 5.3" },
{ id: "glm-5.2", name: "GLM 5.2" },
{ id: "glm-5.1", name: "GLM 5.1" },
{ id: "glm-5", name: "GLM 5" },
Expand Down
66 changes: 52 additions & 14 deletions open-sse/executors/glm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,41 @@ function getEffectiveKey(credentials: ProviderCredentials): string {
return credentials.apiKey || credentials.accessToken || "";
}

export type GlmEffortLevel = "low" | "high" | "max";

type GlmEffortTier = {
baseModel: string;
effort: GlmEffortLevel;
/** Transport where the upstream honors the effort selector for this family. */
transport: GlmTransport;
};

/**
* GLM-5.2 effort tiers route exclusively through the Anthropic transport,
* where Zhipu maps Claude Code effort selectors (high/max) to reasoning
* intensity. The base model ID sent upstream is always "glm-5.2".
* GLM-5.2 effort tiers (glm-5.2-high/-max) route exclusively through the
* Anthropic transport, where Zhipu maps Claude Code effort selectors (high/max)
* to reasoning intensity. The base model ID sent upstream is always "glm-5.2".
*
* GLM-5.3 replaced tier endpoints with a documented `reasoning_effort` request
* parameter (low|high|max, default max) on the coding chat/completions endpoint,
* so its tiers stay on the OpenAI transport and inject `reasoning_effort` +
* `thinking.type=enabled` (5.3 no longer accepts thinking disabled).
*
* https://docs.z.ai/devpack/latest-model
* https://z.ai/blog/glm-5.3
*/
function parseGlm52Effort(model: string): { baseModel: string; effort: "high" | "max" } | null {
if (model === "glm-5.2-high") return { baseModel: "glm-5.2", effort: "high" };
if (model === "glm-5.2-max") return { baseModel: "glm-5.2", effort: "max" };
return null;
function parseGlmEffortTier(model: string): GlmEffortTier | null {
switch (model) {
case "glm-5.2-high":
return { baseModel: "glm-5.2", effort: "high", transport: "anthropic" };
case "glm-5.2-max":
return { baseModel: "glm-5.2", effort: "max", transport: "anthropic" };
case "glm-5.3-high":
return { baseModel: "glm-5.3", effort: "high", transport: "openai" };
case "glm-5.3-low":
return { baseModel: "glm-5.3", effort: "low", transport: "openai" };
default:
return null;
}
}

/**
Expand Down Expand Up @@ -278,7 +302,7 @@ export class GlmExecutor extends DefaultExecutor {
credentials: ProviderCredentials,
transport: GlmTransport
) {
const effortTier = parseGlm52Effort(model);
const effortTier = parseGlmEffortTier(model);
const effectiveModel = effortTier ? effortTier.baseModel : model;

const transformed = this.transformRequest(effectiveModel, body, stream, credentials);
Expand Down Expand Up @@ -313,6 +337,14 @@ export class GlmExecutor extends DefaultExecutor {
}

if (transport === "openai") {
// GLM-5.3 effort tiers: inject the documented `reasoning_effort` param and
// force thinking on — 5.3 rejects thinking.type "disabled", and an effort
// tier without thinking would silently drop the selector upstream.
if (record && effortTier && effortTier.transport === "openai") {
const existingThinking = asRecord(record.thinking);
record.thinking = { ...existingThinking, type: "enabled" };
record.reasoning_effort = effortTier.effort;
}
if (record && stream && hasTools(record) && record.tool_stream === undefined) {
return { ...record, tool_stream: true };
}
Expand Down Expand Up @@ -446,7 +478,12 @@ export class GlmExecutor extends DefaultExecutor {
*/
private async finalizeAnthropicTransportResult(
input: ExecuteInput,
result: { response: Response; url: string; headers: Record<string, string>; transformedBody: unknown }
result: {
response: Response;
url: string;
headers: Record<string, string>;
transformedBody: unknown;
}
): Promise<GlmExecuteResult> {
const { response: rawResponse, url, headers, transformedBody } = result;
const clientHeaders = input.clientHeaders ?? {};
Expand Down Expand Up @@ -475,13 +512,14 @@ export class GlmExecutor extends DefaultExecutor {
}

async execute(input: ExecuteInput): Promise<GlmExecuteResult> {
const effortTier = parseGlm52Effort(input.model);
const effortTier = parseGlmEffortTier(input.model);

// GLM-5.2 effort tiers route directly through Anthropic transport (no fallback).
// Zhipu only graduates effort on the Anthropic endpoint via the
// effort-2025-11-24 beta header included in GLM_ANTHROPIC_BETA.
// Effort tiers route directly through their family's transport (no fallback):
// GLM-5.2 → Anthropic (Zhipu only graduates effort there, via the
// effort-2025-11-24 beta header in GLM_ANTHROPIC_BETA); GLM-5.3 → OpenAI
// coding endpoint (`reasoning_effort` param). See parseGlmEffortTier.
if (effortTier) {
return this.executeTransport(input, "anthropic");
return this.executeTransport(input, effortTier.transport);
}

const primaryTransport = getGlmTransport(
Expand Down
33 changes: 32 additions & 1 deletion src/shared/constants/modelSpecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ const BEDROCK_CLAUDE_ALIASES = (...modelIds: string[]) => [
// Provider discovery/sync sources can under-report GLM-5.2 IDs as 128K.
// Keep native/bare Z.AI GLM-5.2 context authoritative, but do not blindly apply
// it to every provider-wrapped alias: hosted providers can and do cap lower.
const AUTHORITATIVE_CONTEXT_WINDOW_MODEL_IDS = new Set(["glm-5.2", "glm-5.2-high", "glm-5.2-max"]);
const AUTHORITATIVE_CONTEXT_WINDOW_MODEL_IDS = new Set([
"glm-5.3",
"glm-5.3-high",
"glm-5.3-low",
"glm-5.2",
"glm-5.2-high",
"glm-5.2-max",
]);
const AUTHORITATIVE_PROVIDER_CONTEXT_WINDOWS = new Map<string, number>([
["cloudflare-ai/@cf/zai-org/glm-5.2", 262144],
// Hugging Face Router has 1M-capable backends, but bare routing can select
Expand Down Expand Up @@ -567,6 +574,30 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
supportsTools: true,
},

// ── Z.AI GLM-5.3 (1M context mirrored from 5.2 — same base model; 128K max
// output; effort via reasoning_effort param, tiers are OmniRoute aliases) ──
"glm-5.3": {
maxOutputTokens: 131072,
contextWindow: 1000000,
thinkingBudgetCap: 38912,
supportsThinking: true,
supportsTools: true,
},
"glm-5.3-high": {
maxOutputTokens: 131072,
contextWindow: 1000000,
thinkingBudgetCap: 38912,
supportsThinking: true,
supportsTools: true,
},
"glm-5.3-low": {
maxOutputTokens: 131072,
contextWindow: 1000000,
thinkingBudgetCap: 38912,
supportsThinking: true,
supportsTools: true,
},

// ── Z.AI GLM-5.2 (1M context, 128K max output, effort tiers) ────
"glm-5.2": {
maxOutputTokens: 131072,
Expand Down
24 changes: 24 additions & 0 deletions src/shared/constants/pricing/shared-tiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,30 @@ export const CLAUDE_SONNET_5_PRICING = {
};

export const GLM_PRICING = {
// GLM-5.3 (2026-08-14): Z.ai hasn't published 5.3 rates yet — mirrored from
// GLM-5.2 (same base model; 5.1 and 5.2 also share identical rates).
// Correct when https://docs.z.ai/guides/overview/pricing lists glm-5.3.
"glm-5.3": {
input: 1.2,
output: 5,
cached: 0.3,
reasoning: 5,
cache_creation: 1.2,
},
"glm-5.3-high": {
input: 1.2,
output: 5,
cached: 0.3,
reasoning: 5,
cache_creation: 1.2,
},
"glm-5.3-low": {
input: 1.2,
output: 5,
cached: 0.3,
reasoning: 5,
cache_creation: 1.2,
},
"glm-5.2": {
input: 1.2,
output: 5,
Expand Down
Loading
Loading