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,