This commit is contained in:
Inoki 2026-01-30 11:41:21 -05:00 committed by GitHub
commit 83b785e2c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 83 additions and 0 deletions

View File

@ -2380,6 +2380,8 @@ Notes:
- `z.ai/*` and `z-ai/*` are accepted aliases and normalize to `zai/*`.
- If `ZAI_API_KEY` is missing, requests to `zai/*` will fail with an auth error at runtime.
- Example error: `No API key found for provider "zai".`
- Optional: set `ZAI_BASE_URL` to override the `zai` provider endpoint without editing config.
(Legacy alias: `Z_AI_BASE_URL`.) Example for Chinese Z.AI endpoint: `ZAI_BASE_URL=https://open.bigmodel.cn/api/paas/v4/`
- Z.AIs general API endpoint is `https://api.z.ai/api/paas/v4`. GLM coding
requests use the dedicated Coding endpoint `https://api.z.ai/api/coding/paas/v4`.
The built-in `zai` provider uses the Coding endpoint. If you need the general

View File

@ -86,6 +86,17 @@ const OLLAMA_DEFAULT_COST = {
cacheWrite: 0,
};
function normalizeProviderBaseUrl(url: string): string {
return url.trim().replace(/\/+$/, "");
}
function resolveZaiBaseUrlEnv(env: NodeJS.ProcessEnv): string | null {
// Keep a legacy alias to mirror Z_AI_API_KEY support.
const raw = env.ZAI_BASE_URL?.trim() || env.Z_AI_BASE_URL?.trim() || "";
if (!raw) return null;
return normalizeProviderBaseUrl(raw);
}
interface OllamaModel {
name: string;
modified_at: string;
@ -392,6 +403,12 @@ export async function resolveImplicitProviders(params: {
agentDir: string;
}): Promise<ModelsConfig["providers"]> {
const providers: Record<string, ProviderConfig> = {};
const zaiBaseUrl = resolveZaiBaseUrlEnv(process.env);
if (zaiBaseUrl) {
// Override baseUrl for pi-ai built-in z.ai models without redefining models.
// This matches the "models: [] means baseUrl override only" pattern used for Copilot.
providers.zai = { baseUrl: zaiBaseUrl, models: [] };
}
const authStore = ensureAuthProfileStore(params.agentDir, {
allowKeychainPrompt: false,
});

View File

@ -0,0 +1,64 @@
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
import type { MoltbotConfig } from "../config/config.js";
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
return withTempHomeBase(fn, { prefix: "moltbot-zai-baseurl-" });
}
describe("models-config: ZAI_BASE_URL", () => {
let prevHome: string | undefined;
let prevZaiBaseUrl: string | undefined;
let prevZaiLegacyBaseUrl: string | undefined;
beforeEach(() => {
prevHome = process.env.HOME;
prevZaiBaseUrl = process.env.ZAI_BASE_URL;
prevZaiLegacyBaseUrl = process.env.Z_AI_BASE_URL;
});
afterEach(() => {
process.env.HOME = prevHome;
if (prevZaiBaseUrl === undefined) delete process.env.ZAI_BASE_URL;
else process.env.ZAI_BASE_URL = prevZaiBaseUrl;
if (prevZaiLegacyBaseUrl === undefined) delete process.env.Z_AI_BASE_URL;
else process.env.Z_AI_BASE_URL = prevZaiLegacyBaseUrl;
});
it("writes providers.zai.baseUrl from ZAI_BASE_URL (normalized)", async () => {
await withTempHome(async () => {
vi.resetModules();
process.env.ZAI_BASE_URL = "https://proxy.example.test/v1/";
delete process.env.Z_AI_BASE_URL;
const { ensureMoltbotModelsJson } = await import("./models-config.js");
const { resolveMoltbotAgentDir } = await import("./agent-paths.js");
await ensureMoltbotModelsJson({} as MoltbotConfig);
const raw = await fs.readFile(path.join(resolveMoltbotAgentDir(), "models.json"), "utf8");
const parsed = JSON.parse(raw) as { providers?: Record<string, { baseUrl?: string }> };
expect(parsed.providers?.zai?.baseUrl).toBe("https://proxy.example.test/v1");
});
});
it("supports legacy Z_AI_BASE_URL when ZAI_BASE_URL is unset", async () => {
await withTempHome(async () => {
vi.resetModules();
delete process.env.ZAI_BASE_URL;
process.env.Z_AI_BASE_URL = "http://localhost:9999/v1/";
const { ensureMoltbotModelsJson } = await import("./models-config.js");
const { resolveMoltbotAgentDir } = await import("./agent-paths.js");
await ensureMoltbotModelsJson({} as MoltbotConfig);
const raw = await fs.readFile(path.join(resolveMoltbotAgentDir(), "models.json"), "utf8");
const parsed = JSON.parse(raw) as { providers?: Record<string, { baseUrl?: string }> };
expect(parsed.providers?.zai?.baseUrl).toBe("http://localhost:9999/v1");
});
});
});