diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index be2e08a76aa..80661ec948c 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -578,7 +578,7 @@ function detectOpenAICompletionsCompat(model: Model<"openai-completions">): Open isAntLing; const useMaxTokens = - baseUrl.includes("chutes.ai") || isMoonshot || isCloudflareAiGateway || isTogether || isNvidia || isAntLing; + baseUrl.includes("chutes.ai") || isMoonshot || isCloudflareAiGateway || isTogether || isNvidia || isAntLing || isZai; const isGrok = provider === "xai" || baseUrl.includes("api.x.ai"); const isDeepSeek = provider === "deepseek" || baseUrl.includes("deepseek.com"); diff --git a/packages/ai/src/api/openai-completions.ts b/packages/ai/src/api/openai-completions.ts index 29bb064f9bb..f0d114dfdb5 100644 --- a/packages/ai/src/api/openai-completions.ts +++ b/packages/ai/src/api/openai-completions.ts @@ -1417,7 +1417,13 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet isAntLing; const useMaxTokens = - baseUrl.includes("chutes.ai") || isMoonshot || isCloudflareAiGateway || isTogether || isNvidia || isAntLing; + baseUrl.includes("chutes.ai") || + isMoonshot || + isCloudflareAiGateway || + isTogether || + isNvidia || + isAntLing || + isZai; const isGrok = provider === "xai" || baseUrl.includes("api.x.ai"); const isDeepSeek = provider === "deepseek" || baseUrl.includes("deepseek.com"); diff --git a/packages/ai/test/openai-completions-tool-choice.test.ts b/packages/ai/test/openai-completions-tool-choice.test.ts index b585bb431d8..284b6329b51 100644 --- a/packages/ai/test/openai-completions-tool-choice.test.ts +++ b/packages/ai/test/openai-completions-tool-choice.test.ts @@ -1389,6 +1389,33 @@ describe("openai-completions tool_choice", () => { } }); + it("sends max_tokens for Z.AI completions models", async () => { + const cases = [getModel("zai", "glm-5.1")!, getModel("zai", "glm-5.2")!] as const; + + for (const model of cases) { + expect(model.compat?.maxTokensField).toBe("max_tokens"); + let payload: unknown; + + await streamSimple( + model, + { + messages: [{ role: "user", content: "Hi", timestamp: Date.now() }], + }, + { + apiKey: "test", + maxTokens: 123, + onPayload: (params: unknown) => { + payload = params; + }, + }, + ).result(); + + const params = (payload ?? mockState.lastParams) as { max_tokens?: number; max_completion_tokens?: number }; + expect(params.max_tokens).toBe(123); + expect(params.max_completion_tokens).toBeUndefined(); + } + }); + it("omits reasoning effort for OpenCode Grok Build", async () => { const model = getModel("opencode", "grok-build-0.1")!; let payload: unknown;