feat(zai): add Z.AI GLM Coding Plan provider preset (#896)

* feat(zai): add Z.AI GLM Coding Plan provider preset

Add dedicated Z.AI provider support for the GLM Coding Plan, enabling
use of GLM-5.1, GLM-5-Turbo, GLM-4.7, and GLM-4.5-Air models through
the OpenAI-compatible shim with proper thinking mode (reasoning_content),
max_tokens handling, and context window sizing.

* fix(zai): unify GLM max output token limits across casing variants

glm-5/glm-4.7 had conservative 16K max output while GLM-5/GLM-4.7
had 131K. Use consistent Z.AI coding plan limits for all GLM variants.

* fix(zai): restore DashScope GLM limits, enable GLM thinking support

- Restore lowercase glm-5/glm-4.7 to 16_384 max output (DashScope limits)
  while keeping Z.AI coding plan high limits on uppercase GLM-* keys only
- Add GLM model support to modelSupportsThinking() so reasoning_content
  is enabled when using GLM-5.x/GLM-4.7 models on Z.AI

* fix(zai): tighten GLM regexes, fix misleading context window comment

- Use precise regex in thinking.ts: exact GLM model matches only,
  no false positives on glm-50/glm-4, includes glm-4.5-air
- Use uppercase-only match in StartupScreen rawModel fallback so
  DashScope lowercase glm-* models aren't mislabeled as Z.AI
- Clarify context window comment: lowercase glm-5.1/glm-5-turbo/
  glm-4.5-air are Z.AI-specific aliases, not DashScope

* fix(zai): scope GLM detection to Z.AI

* improve readability of max_completion_tokens check

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
chioarub
2026-04-26 03:18:59 +03:00
committed by GitHub
parent 29f7579377
commit a0d657ee18
16 changed files with 342 additions and 6 deletions

View File

@@ -64,6 +64,7 @@ import {
} from './openaiErrorClassification.js'
import { sanitizeSchemaForOpenAICompat } from '../../utils/schemaSanitizer.js'
import { redactSecretValueForDisplay } from '../../utils/providerProfile.js'
import { isZaiBaseUrl } from '../../utils/zaiProvider.js'
import {
normalizeToolArguments,
hasToolFieldMapping,
@@ -93,7 +94,6 @@ const KIMI_CODE_API_HOST = 'api.kimi.com'
const DEEPSEEK_API_HOSTS = new Set([
'api.deepseek.com',
])
const COPILOT_HEADERS: Record<string, string> = {
'User-Agent': 'GitHubCopilotChat/0.26.7',
'Editor-Version': 'vscode/1.99.3',
@@ -1514,7 +1514,8 @@ class OpenAIShimMessages {
// thinking block we captured on the inbound response.
preserveReasoningContent:
isMoonshotCompatibleBaseUrl(request.baseUrl) ||
isDeepSeekBaseUrl(request.baseUrl),
isDeepSeekBaseUrl(request.baseUrl) ||
isZaiBaseUrl(request.baseUrl),
})
const body: Record<string, unknown> = {
@@ -1553,8 +1554,19 @@ class OpenAIShimMessages {
const isMoonshot = isMoonshotCompatibleBaseUrl(request.baseUrl)
const isDeepSeek = isDeepSeekBaseUrl(request.baseUrl)
const isZai = isZaiBaseUrl(request.baseUrl)
if ((isGithub || isMistral || isLocal || isMoonshot || isDeepSeek) && body.max_completion_tokens !== undefined) {
if (
(
isGithub ||
isMistral ||
isLocal ||
isMoonshot ||
isDeepSeek ||
isZai
) &&
body.max_completion_tokens !== undefined
) {
body.max_tokens = body.max_completion_tokens
delete body.max_completion_tokens
}
@@ -1562,10 +1574,10 @@ class OpenAIShimMessages {
// mistral and gemini don't recognize body.store — Gemini returns 400
// "Invalid JSON payload received. Unknown name 'store': Cannot find field."
// Moonshot direct API, Kimi Code's OpenAI-compatible coding endpoint,
// and DeepSeek have not published support for the parameter either;
// DeepSeek, and Z.AI have not published support for the parameter either;
// strip it preemptively to avoid the same class of error on strict-parse
// providers.
if (isMistral || isGeminiMode() || isMoonshot || isDeepSeek) {
if (isMistral || isGeminiMode() || isMoonshot || isDeepSeek || isZai) {
delete body.store
}
@@ -1593,6 +1605,17 @@ class OpenAIShimMessages {
}
}
// Z.AI uses the same thinking format as DeepSeek: { type: "enabled" | "disabled" }
// with reasoning_content in responses.
if (isZai) {
const requestedThinkingType = (params.thinking as { type?: string } | undefined)?.type
if (requestedThinkingType && requestedThinkingType !== 'disabled') {
body.thinking = { type: 'enabled' }
} else if (requestedThinkingType === 'disabled') {
body.thinking = { type: 'disabled' }
}
}
if (params.tools && params.tools.length > 0) {
const converted = convertTools(
params.tools as Array<{