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 packages/types/src/__tests__/lite-llm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})

Expand Down
1 change: 1 addition & 0 deletions packages/types/src/providers/lite-llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions packages/types/src/providers/zai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<string, ModelInfo>

export const ZAI_DEFAULT_TEMPERATURE = 0.6
Expand Down
86 changes: 86 additions & 0 deletions src/api/providers/__tests__/zai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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")
})
})

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions src/api/providers/fetchers/__tests__/litellm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 () => {
Expand Down
6 changes: 4 additions & 2 deletions src/api/providers/zai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type ZAiChatCompletionParams = Omit<OpenAI.Chat.ChatCompletionCreateParams, "rea
reasoning_effort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
}

const isGlm53Model = (model: string) => model === "glm-5.3" || model === "glm-5.3-flash"

export class ZAiHandler extends BaseOpenAiCompatibleProvider<string> {
constructor(options: ApiHandlerOptions) {
const apiLine = options.zaiApiLine ?? "international_coding"
Expand Down Expand Up @@ -105,7 +107,7 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider<string> {
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),
Expand Down Expand Up @@ -143,7 +145,7 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider<string> {

override async completePrompt(prompt: string, options?: CompletePromptOptions): Promise<string> {
const { id: model, info } = this.getModel()
if (model !== "glm-5.3") {
if (!isGlm53Model(model)) {
return super.completePrompt(prompt, options)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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")
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
Loading