diff --git a/open-sse/config/glmProvider.ts b/open-sse/config/glmProvider.ts index c11e5d14e5..f643bfc63b 100644 --- a/open-sse/config/glmProvider.ts +++ b/open-sse/config/glmProvider.ts @@ -18,6 +18,16 @@ export const GLM_ANTHROPIC_DEFAULT_BASE_URLS = Object.freeze({ }); export const GLM_SHARED_MODELS = Object.freeze([ + { + id: "glm-5.3-flash", + name: "GLM 5.3 Flash", + contextLength: 1000000, + maxOutputTokens: 131072, + toolCalling: true, + supportsReasoning: true, + supportedThinkingEfforts: ["low", "high", "max"], + supportsVision: true, + }, { // GLM-5.3 exposes low|high|max reasoning_effort (default max); -high/-low // are OmniRoute aliases resolved by GlmExecutor::parseGlmEffortTier. diff --git a/open-sse/config/providers/registry/zai/index.ts b/open-sse/config/providers/registry/zai/index.ts index 1141ea8dc3..605a80897d 100644 --- a/open-sse/config/providers/registry/zai/index.ts +++ b/open-sse/config/providers/registry/zai/index.ts @@ -19,6 +19,7 @@ export const zaiProvider: RegistryEntry = { // unknown IDs. Use the `glm` provider for effort tiers. Vision models are // likewise omitted (handled elsewhere). models: [ + { id: "glm-5.3-flash", name: "GLM 5.3 Flash" }, { id: "glm-5.3", name: "GLM 5.3" }, { id: "glm-5.2", name: "GLM 5.2" }, { id: "glm-5.1", name: "GLM 5.1" }, diff --git a/open-sse/mcp-server/__tests__/glmCodingProviderConfig.test.ts b/open-sse/mcp-server/__tests__/glmCodingProviderConfig.test.ts index 38c6a5af1a..a761898ae1 100644 --- a/open-sse/mcp-server/__tests__/glmCodingProviderConfig.test.ts +++ b/open-sse/mcp-server/__tests__/glmCodingProviderConfig.test.ts @@ -87,6 +87,7 @@ describe("GLM Coding provider registry surfaces", () => { expect(PROVIDER_ID_TO_ALIAS.glm).toBe("glm"); expect(byProviderId).toEqual(byAlias); expect(byProviderId.map((model) => model.id)).toEqual([ + "glm-5.3-flash", "glm-5.3", "glm-5.3-high", "glm-5.3-low", @@ -109,6 +110,7 @@ describe("GLM Coding provider registry surfaces", () => { it("declares exact GLM reasoning-effort tiers across every shared GLM provider", () => { const routedTiers = new Map([ + ["glm-5.3-flash", ["low", "high", "max"]], ["glm-5.3", ["low", "high", "max"]], ["glm-5.3-high", ["high"]], ["glm-5.3-low", ["low"]], @@ -181,6 +183,7 @@ describe("GLM Coding provider registry surfaces", () => { const get = (id: string) => models.find((m) => m.id === id); expect(get("glm-5")?.toolCalling).toBe(true); + expect(get("glm-5.3-flash")?.toolCalling).toBe(true); expect(get("glm-4.7-flash")?.toolCalling).toBe(true); expect(get("glm-4.5-air")?.toolCalling).toBe(true); expect(get("glm-5.2")?.toolCalling).toBe(true); @@ -194,6 +197,13 @@ describe("GLM Coding provider registry surfaces", () => { reasoning: 4.8, cache_creation: 1.0, }); + expect(getPricingForModel("glm", "glm-5.3-flash")).toEqual({ + input: 0.075, + output: 0.25, + cached: 0.015, + reasoning: 0.25, + cache_creation: 0.075, + }); expect(getPricingForModel("glm", "glm-4.7-flash")).toEqual({ input: 0, output: 0, diff --git a/src/shared/constants/modelSpecs.ts b/src/shared/constants/modelSpecs.ts index a8c238f080..a327ee97c6 100644 --- a/src/shared/constants/modelSpecs.ts +++ b/src/shared/constants/modelSpecs.ts @@ -67,6 +67,7 @@ const BEDROCK_CLAUDE_ALIASES = (...modelIds: string[]) => [ // 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.3-flash", "glm-5.3", "glm-5.3-high", "glm-5.3-low", @@ -553,6 +554,14 @@ export const MODEL_SPECS: Record = { // ── 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-flash": { + maxOutputTokens: 131072, + contextWindow: 1000000, + thinkingBudgetCap: 38912, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + }, "glm-5.3": { maxOutputTokens: 131072, contextWindow: 1000000, diff --git a/src/shared/constants/pricing/shared-tiers.ts b/src/shared/constants/pricing/shared-tiers.ts index 8004e43a47..b163550c66 100644 --- a/src/shared/constants/pricing/shared-tiers.ts +++ b/src/shared/constants/pricing/shared-tiers.ts @@ -111,6 +111,14 @@ export const CLAUDE_SONNET_5_PRICING = { }; export const GLM_PRICING = { + // GLM-5.3 Flash (2026-08-26, https://models.dev / Z.AI) + "glm-5.3-flash": { + input: 0.075, + output: 0.25, + cached: 0.015, + reasoning: 0.25, + cache_creation: 0.075, + }, // 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. diff --git a/src/shared/constants/visionModels.ts b/src/shared/constants/visionModels.ts index f8cd3efaa0..da7b755490 100644 --- a/src/shared/constants/visionModels.ts +++ b/src/shared/constants/visionModels.ts @@ -38,6 +38,7 @@ export const VISION_MODEL_ID_FRAGMENTS = [ "glm-4v", "glm-4.5v", "glm-4.6v", + "glm-5.3-flash", "gpt-4o", "gpt-4.1", "gpt-4-turbo", diff --git a/tests/unit/glm-5.3-catalog-and-effort-tiers.test.ts b/tests/unit/glm-5.3-catalog-and-effort-tiers.test.ts index ffe975d71d..07f80cc7e3 100644 --- a/tests/unit/glm-5.3-catalog-and-effort-tiers.test.ts +++ b/tests/unit/glm-5.3-catalog-and-effort-tiers.test.ts @@ -96,6 +96,7 @@ test("catalog suppresses inferred tiers for every GLM registry entry without a p test("catalog exposes only GLM effort tiers that each provider can route", () => { const routedTiers = new Map([ + ["glm-5.3-flash", ["low", "high", "max"]], ["glm-5.3", ["low", "high", "max"]], ["glm-5.3-high", ["high"]], ["glm-5.3-low", ["low"]], @@ -139,9 +140,10 @@ for (const provider of ["glm", "glm-cn", "glmt"]) { }); } -test("zai advertises the GLM-5.3 base model only (DefaultExecutor sends ids verbatim)", () => { +test("zai advertises the GLM-5.3 base model and GLM-5.3 Flash (DefaultExecutor sends ids verbatim)", () => { const ids = modelIds("zai"); assert.ok(ids.includes("glm-5.3"), `zai should advertise glm-5.3; got ${ids.join(", ")}`); + assert.ok(ids.includes("glm-5.3-flash"), `zai should advertise glm-5.3-flash; got ${ids.join(", ")}`); for (const alias of ["glm-5.3-high", "glm-5.3-low", "glm-5.3-max"]) { assert.ok( !ids.includes(alias), @@ -150,17 +152,18 @@ test("zai advertises the GLM-5.3 base model only (DefaultExecutor sends ids verb } }); -test("modelSpecs carries 1M/128K specs for all GLM-5.3 ids", () => { - for (const id of GLM_5_3_IDS) { +test("modelSpecs carries 1M/128K specs for all GLM-5.3 ids including flash", () => { + for (const id of [...GLM_5_3_IDS, "glm-5.3-flash"]) { const spec = MODEL_SPECS[id]; assert.ok(spec, `MODEL_SPECS should include ${id}`); assert.equal(spec.contextWindow, 1_000_000); assert.equal(spec.maxOutputTokens, 131_072); assert.equal(spec.supportsThinking, true); } + assert.equal(MODEL_SPECS["glm-5.3-flash"]?.supportsVision, true); }); -test("GLM_PRICING covers the GLM-5.3 ids with GLM-5.2-parity rates", () => { +test("GLM_PRICING covers the GLM-5.3 ids with GLM-5.2-parity rates and GLM-5.3-Flash rates", () => { const reference = GLM_PRICING["glm-5.2"]; assert.ok(reference, "glm-5.2 pricing reference missing"); for (const id of GLM_5_3_IDS) { @@ -168,6 +171,11 @@ test("GLM_PRICING covers the GLM-5.3 ids with GLM-5.2-parity rates", () => { assert.ok(pricing, `GLM_PRICING should include ${id}`); assert.deepEqual(pricing, reference); } + const flashPricing = GLM_PRICING["glm-5.3-flash"]; + assert.ok(flashPricing, "glm-5.3-flash pricing missing"); + assert.equal(flashPricing.input, 0.075); + assert.equal(flashPricing.output, 0.25); + assert.equal(flashPricing.cached, 0.015); }); test("GlmExecutor resolves glm-5.3-high to reasoning_effort=high on the OpenAI coding transport", () => {