From 48220df2188c8bc1615ebe0ab2c65748d076d9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=89=91=E9=9B=840668001315?= Date: Thu, 18 Jun 2026 14:22:52 +0800 Subject: [PATCH] fix(zai): fall back to manifest baseUrl for synthesized GLM-5 models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When resolveGlm5ForwardCompatModel synthesizes a GLM-5 model and both providerConfig and the template model (glm-4.7) are unavailable, the resolved model was missing baseUrl. This caused the OpenAI SDK to fall back to api.openai.com instead of api.z.ai, producing confusing 401 errors. The fix adds a third fallback to ZAI_MANIFEST_BASE_URL — the provider-level baseUrl from the bundled manifest JSON — so the model always carries a baseUrl regardless of runtime state. Fixes #94269 --- extensions/zai/index.test.ts | 22 ++++++++++++++++++++++ extensions/zai/index.ts | 6 ++++-- extensions/zai/model-definitions.ts | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/extensions/zai/index.test.ts b/extensions/zai/index.test.ts index 8862a5e529ec..aa8e1031c33c 100644 --- a/extensions/zai/index.test.ts +++ b/extensions/zai/index.test.ts @@ -189,6 +189,28 @@ describe("zai provider plugin", () => { ).toEqual(registered); }); + // FIX #94269: synthesized model must include baseUrl even when the template model + // is not in the registry and no provider config is set. + it("falls back to manifest baseUrl when both providerConfig and template model are unavailable", async () => { + const provider = await registerSingleProviderPlugin(plugin); + + const resolved = provider.resolveDynamicModel?.({ + provider: "zai", + modelId: "glm-5-turbo", + modelRegistry: { + find: () => null, + }, + } as never) as Record | undefined; + expectModelFields(resolved, { + id: "glm-5-turbo", + provider: "zai", + api: "openai-completions", + baseUrl: "https://api.z.ai/api/paas/v4", + reasoning: true, + input: ["text"], + }); + }); + it("still synthesizes unknown GLM-5 variants from the GLM-4.7 template", async () => { const provider = await registerSingleProviderPlugin(plugin); const template = createGlm47Template(); diff --git a/extensions/zai/index.ts b/extensions/zai/index.ts index f14c474cdf6b..a0915a2b933c 100644 --- a/extensions/zai/index.ts +++ b/extensions/zai/index.ts @@ -34,7 +34,7 @@ import { fetchZaiUsage } from "openclaw/plugin-sdk/provider-usage"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime"; import { detectZaiEndpoint, type ZaiEndpointId } from "./detect.js"; import { zaiMediaUnderstandingProvider } from "./media-understanding-provider.js"; -import { buildZaiModelDefinition } from "./model-definitions.js"; +import { buildZaiModelDefinition, ZAI_MANIFEST_BASE_URL } from "./model-definitions.js"; import { applyZaiConfig, applyZaiProviderConfig, resolveZaiModelId } from "./onboard.js"; const PROVIDER_ID = "zai"; @@ -104,7 +104,9 @@ function resolveGlm5ForwardCompatModel( ...template, id: def.id, name: def.name, - baseUrl: ctx.providerConfig?.baseUrl ?? template?.baseUrl, + // FIX #94269: fall back to manifest provider-level baseUrl when neither + // provider config nor template model in registry has one. + baseUrl: ctx.providerConfig?.baseUrl ?? template?.baseUrl ?? ZAI_MANIFEST_BASE_URL, api: "openai-completions", provider: PROVIDER_ID, reasoning: def.reasoning, diff --git a/extensions/zai/model-definitions.ts b/extensions/zai/model-definitions.ts index e2b6b45af414..88295086b506 100644 --- a/extensions/zai/model-definitions.ts +++ b/extensions/zai/model-definitions.ts @@ -11,6 +11,8 @@ export const ZAI_DEFAULT_MODEL_ID = "glm-5.1"; export const ZAI_CODING_DEFAULT_MODEL_ID = "glm-5.2"; const ZAI_MANIFEST_CATALOG = manifest.modelCatalog.providers.zai; +/** Provider-level default baseUrl from the bundled manifest. Used as runtime fallback. */ +export const ZAI_MANIFEST_BASE_URL = ZAI_MANIFEST_CATALOG.baseUrl; const ZAI_MANIFEST_PROVIDER = buildManifestModelProviderConfig({ providerId: "zai", catalog: ZAI_MANIFEST_CATALOG,