From b1525dc52313c2cfcd9d87ac26b5c0f712cc2d27 Mon Sep 17 00:00:00 2001 From: "@navedmerchant" <14171946+navedmerchant@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:26:00 +0000 Subject: [PATCH] feat(providers): add GLM-5.3-Flash support --- packages/types/src/__tests__/lite-llm.test.ts | 1 + packages/types/src/providers/lite-llm.ts | 1 + packages/types/src/providers/zai.ts | 22 +++++ src/api/providers/__tests__/zai.spec.ts | 86 +++++++++++++++++++ .../fetchers/__tests__/litellm.spec.ts | 4 +- src/api/providers/zai.ts | 6 +- .../__tests__/providerModelConfig.spec.ts | 6 +- .../hooks/__tests__/useSelectedModel.spec.ts | 7 +- 8 files changed, 125 insertions(+), 8 deletions(-) diff --git a/packages/types/src/__tests__/lite-llm.test.ts b/packages/types/src/__tests__/lite-llm.test.ts index 87fdfdd0ad..c76d0d84fc 100644 --- a/packages/types/src/__tests__/lite-llm.test.ts +++ b/packages/types/src/__tests__/lite-llm.test.ts @@ -21,6 +21,7 @@ describe("LiteLLM preserveReasoning model detection", () => { it("matches case-insensitively", () => { expect(isLiteLLMPreserveReasoningModel("MiniMax-M2.7-Highspeed")).toBe(true) expect(isLiteLLMPreserveReasoningModel("GLM-5.3")).toBe(true) + expect(isLiteLLMPreserveReasoningModel("GLM-5.3-FLASH")).toBe(true) expect(isLiteLLMPreserveReasoningModel("QWEN3.8-MAX")).toBe(true) }) diff --git a/packages/types/src/providers/lite-llm.ts b/packages/types/src/providers/lite-llm.ts index 2e19b5b8b7..5c28369651 100644 --- a/packages/types/src/providers/lite-llm.ts +++ b/packages/types/src/providers/lite-llm.ts @@ -55,6 +55,7 @@ export const LITELLM_PRESERVE_REASONING_MODEL_IDS = [ "glm-5.1", "glm-5.2", "glm-5.3", + "glm-5.3-flash", "glm-5-turbo", // bedrock.ts, minimax.ts, opencode-go.ts diff --git a/packages/types/src/providers/zai.ts b/packages/types/src/providers/zai.ts index 79af21d8dc..56c43c3212 100644 --- a/packages/types/src/providers/zai.ts +++ b/packages/types/src/providers/zai.ts @@ -6,6 +6,7 @@ import { ZaiApiLine } from "../provider-settings.js" // https://docs.z.ai/guides/llm/glm-4.5 // https://docs.z.ai/guides/llm/glm-4.6 // https://docs.z.ai/guides/llm/glm-5.3 +// https://docs.z.ai/guides/vlm/glm-5.3-flash // https://docs.z.ai/guides/llm/glm-5.1 // https://docs.z.ai/guides/llm/glm-5-turbo // https://docs.z.ai/guides/overview/pricing @@ -26,6 +27,13 @@ const glm53ModelInfo = { "GLM-5.3 is Zhipu's flagship coding and agent model with a 1M context window, 128k max output, and always-on reasoning with configurable effort (Low/High/Max).", } as const satisfies ModelInfo +const glm53FlashModelInfo = { + ...glm53ModelInfo, + supportsImages: true, + description: + "GLM-5.3-Flash is Zhipu's efficient multimodal coding and agent model with a 1M context window, 128k max output, and always-on reasoning with configurable effort (Low/High/Max).", +} as const satisfies ModelInfo + export type InternationalZAiModelId = keyof typeof internationalZAiModels export const internationalZAiDefaultModelId: InternationalZAiModelId = "glm-4.7" export const internationalZAiModels = { @@ -193,6 +201,13 @@ export const internationalZAiModels = { cacheWritesPrice: 0, cacheReadsPrice: 0.26, }, + "glm-5.3-flash": { + ...glm53FlashModelInfo, + inputPrice: 0.15, + outputPrice: 0.5, + cacheWritesPrice: 0, + cacheReadsPrice: 0.03, + }, "glm-5-turbo": { maxTokens: 131_072, contextWindow: 202_752, @@ -505,6 +520,13 @@ export const mainlandZAiCodingPlanOnlyModels = { cacheWritesPrice: 0, cacheReadsPrice: 0.13, }, + "glm-5.3-flash": { + ...glm53FlashModelInfo, + inputPrice: 0.075, + outputPrice: 0.25, + cacheWritesPrice: 0, + cacheReadsPrice: 0.015, + }, } as const satisfies Record export const ZAI_DEFAULT_TEMPERATURE = 0.6 diff --git a/src/api/providers/__tests__/zai.spec.ts b/src/api/providers/__tests__/zai.spec.ts index 0230b679a9..0a33e7ad66 100644 --- a/src/api/providers/__tests__/zai.spec.ts +++ b/src/api/providers/__tests__/zai.spec.ts @@ -167,6 +167,31 @@ describe("ZAiHandler", () => { expect(model.info.cacheReadsPrice).toBe(0.26) }) + it("should expose multimodal GLM-5.3-Flash for the international Coding Plan", () => { + const model = new ZAiHandler({ + apiModelId: "glm-5.3-flash", + zaiApiKey: "test-zai-api-key", + zaiApiLine: "international_coding", + }).getModel() + + expect(model.id).toBe("glm-5.3-flash") + expect(model.info).toMatchObject({ + contextWindow: 1_000_000, + maxTokens: 131_072, + supportsImages: true, + supportsPromptCache: true, + supportsMaxTokens: true, + supportsReasoningEffort: ["low", "high", "max"], + requiredReasoningEffort: true, + reasoningEffort: "max", + preserveReasoning: true, + defaultTemperature: 1, + inputPrice: 0.15, + outputPrice: 0.5, + cacheReadsPrice: 0.03, + }) + }) + it("should return GLM-5-Turbo international model with thinking support", () => { const testModelId: InternationalZAiModelId = "glm-5-turbo" const handlerWithModel = new ZAiHandler({ @@ -319,6 +344,22 @@ describe("ZAiHandler", () => { expect(model.info.cacheReadsPrice).toBe(0.13) }) + it("should expose GLM-5.3-Flash for the China Coding Plan", () => { + const model = new ZAiHandler({ + apiModelId: "glm-5.3-flash", + zaiApiKey: "test-zai-api-key", + zaiApiLine: "china_coding", + }).getModel() + + expect(model.id).toBe("glm-5.3-flash") + expect(model.info.supportsImages).toBe(true) + expect(model.info.supportsReasoningEffort).toEqual(["low", "high", "max"]) + expect(model.info.requiredReasoningEffort).toBe(true) + expect(model.info.inputPrice).toBe(0.075) + expect(model.info.outputPrice).toBe(0.25) + expect(model.info.cacheReadsPrice).toBe(0.015) + }) + it("should return GLM-4.7 China model with thinking support", () => { const testModelId: MainlandZAiModelId = "glm-4.7" const handlerWithModel = new ZAiHandler({ @@ -393,6 +434,7 @@ describe("ZAiHandler", () => { it("should expose GLM-5.3 on the international API", () => { expect(getZAiModels("international_api")).toHaveProperty("glm-5.3") + expect(getZAiModels("international_api")).toHaveProperty("glm-5.3-flash") const handlerWithModel = new ZAiHandler({ apiModelId: "glm-5.3", zaiApiKey: "test-zai-api-key", @@ -442,6 +484,7 @@ describe("ZAiHandler", () => { it("should not expose Coding Plan-only models", () => { expect(getZAiModels("china_api")).not.toHaveProperty("glm-5.3") + expect(getZAiModels("china_api")).not.toHaveProperty("glm-5.3-flash") }) }) @@ -692,6 +735,29 @@ describe("ZAiHandler", () => { ) }) + it("should use the official GLM-5.3-Flash thinking and sampling defaults", async () => { + const handlerWithModel = new ZAiHandler({ + apiModelId: "glm-5.3-flash", + zaiApiKey: "test-zai-api-key", + zaiApiLine: "international_coding", + reasoningEffort: "disable", + }) + + mockCreate.mockImplementationOnce(() => asyncStreamFrom([])) + + const messageGenerator = handlerWithModel.createMessage("system prompt", []) + await messageGenerator.next() + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: "glm-5.3-flash", + thinking: { type: "enabled", clear_thinking: false }, + reasoning_effort: "max", + temperature: 1, + }), + ) + }) + it("should keep GLM-5.3 reasoning enabled when the master reasoning setting is disabled", async () => { const handlerWithModel = new ZAiHandler({ apiModelId: "glm-5.3", @@ -734,6 +800,26 @@ describe("ZAiHandler", () => { }) }) + it("should use the official GLM-5.3-Flash parameters for completePrompt", async () => { + const handlerWithModel = new ZAiHandler({ + apiModelId: "glm-5.3-flash", + zaiApiKey: "test-zai-api-key", + zaiApiLine: "international_api", + reasoningEffort: "low", + }) + + mockCreate.mockResolvedValueOnce({ choices: [{ message: { content: "response" } }] }) + + await expect(handlerWithModel.completePrompt("prompt")).resolves.toBe("response") + expect(mockCreate).toHaveBeenCalledWith({ + model: "glm-5.3-flash", + messages: [{ role: "user", content: "prompt" }], + temperature: 1, + thinking: { type: "enabled", clear_thinking: false }, + reasoning_effort: "low", + }) + }) + it("should omit reasoning_effort for GLM-5.2 when reasoningEffort is set to disable", async () => { const handlerWithModel = new ZAiHandler({ apiModelId: "glm-5.2", diff --git a/src/api/providers/fetchers/__tests__/litellm.spec.ts b/src/api/providers/fetchers/__tests__/litellm.spec.ts index 9f7b80cb31..aa398ca0ce 100644 --- a/src/api/providers/fetchers/__tests__/litellm.spec.ts +++ b/src/api/providers/fetchers/__tests__/litellm.spec.ts @@ -765,7 +765,7 @@ describe("getLiteLLMModels", () => { data: { data: [ { - model_name: "glm-5.3", + model_name: "glm-5.3-flash", model_info: { max_tokens: 8192, max_input_tokens: 128000, @@ -782,7 +782,7 @@ describe("getLiteLLMModels", () => { const result = await getLiteLLMModels("test-api-key", "http://localhost:4000") - expect(result["glm-5.3"]).toMatchObject({ preserveReasoning: true }) + expect(result["glm-5.3-flash"]).toMatchObject({ preserveReasoning: true }) }) it("does not match a model id that merely contains a known family as a substring", async () => { diff --git a/src/api/providers/zai.ts b/src/api/providers/zai.ts index c53a434e38..da057406b4 100644 --- a/src/api/providers/zai.ts +++ b/src/api/providers/zai.ts @@ -27,6 +27,8 @@ type ZAiChatCompletionParams = Omit model === "glm-5.3" || model === "glm-5.3-flash" + export class ZAiHandler extends BaseOpenAiCompatibleProvider { constructor(options: ApiHandlerOptions) { const apiLine = options.zaiApiLine ?? "international_coding" @@ -105,7 +107,7 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider { stream_options: { include_usage: true }, // Models with required reasoning stay enabled even when an old setting requests disable. thinking: useReasoning - ? { type: "enabled", ...(model === "glm-5.3" && { clear_thinking: false }) } + ? { type: "enabled", ...(isGlm53Model(model) && { clear_thinking: false }) } : { type: "disabled" }, reasoning_effort: reasoningEffort, tools: this.convertToolsForOpenAI(metadata?.tools), @@ -143,7 +145,7 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider { override async completePrompt(prompt: string, options?: CompletePromptOptions): Promise { const { id: model, info } = this.getModel() - if (model !== "glm-5.3") { + if (!isGlm53Model(model)) { return super.completePrompt(prompt, options) } diff --git a/webview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.ts b/webview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.ts index 2bd91f19aa..6916de8479 100644 --- a/webview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.ts +++ b/webview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.ts @@ -206,7 +206,7 @@ describe("providerModelConfig", () => { expect(Object.keys(models).length).toBe(0) }) - it("shows GLM-5.3 for international Z.ai API and Coding Plan entrypoints", () => { + it("shows GLM-5.3 models for international Z.ai API and Coding Plan entrypoints", () => { const internationalCoding = getStaticModelsForProvider("zai", undefined, { apiProvider: "zai", zaiApiLine: "international_coding", @@ -225,9 +225,13 @@ describe("providerModelConfig", () => { }) expect(internationalCoding).toHaveProperty("glm-5.3") + expect(internationalCoding).toHaveProperty("glm-5.3-flash") expect(chinaCoding).toHaveProperty("glm-5.3") + expect(chinaCoding).toHaveProperty("glm-5.3-flash") expect(internationalApi).toHaveProperty("glm-5.3") + expect(internationalApi).toHaveProperty("glm-5.3-flash") expect(chinaApi).not.toHaveProperty("glm-5.3") + expect(chinaApi).not.toHaveProperty("glm-5.3-flash") }) }) diff --git a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts index 3558d47e38..33a9df16da 100644 --- a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts +++ b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts @@ -1331,13 +1331,14 @@ describe("useSelectedModel", () => { it("uses the International Coding catalog when no API line is configured", () => { const apiConfiguration: ProviderSettings = { apiProvider: providerIdentifiers.zai, - apiModelId: "glm-5.3", + apiModelId: "glm-5.3-flash", } const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper: createWrapper() }) - expect(result.current.id).toBe("glm-5.3") - expect(result.current.info).toEqual(getZAiModels("international_coding")["glm-5.3"]) + expect(result.current.id).toBe("glm-5.3-flash") + expect(result.current.info).toEqual(getZAiModels("international_coding")["glm-5.3-flash"]) + expect(result.current.info?.supportsImages).toBe(true) }) it("uses the China Coding catalog for GLM-5.3", () => {