feat: add Z.AI env support and live test

This commit is contained in:
Peter Steinberger
2025-12-31 11:36:57 +01:00
parent 4bdc25d072
commit 21237dae98
6 changed files with 83 additions and 0 deletions

View File

@@ -5,6 +5,8 @@
### Features
- Talk mode: continuous speech conversations (macOS/iOS/Android) with ElevenLabs TTS, reply directives, and optional interrupt-on-speech.
- UI: add optional `ui.seamColor` accent to tint the Talk Mode side bubble (macOS/iOS/Android).
- Agent runtime: accept legacy `Z_AI_API_KEY` for Z.AI provider auth (maps to `ZAI_API_KEY`).
- Tests: add a Z.AI live test gate for smoke validation when keys are present.
### Fixes
- Docs/agent tools: clarify that browser `wait` should be avoided by default and used only in exceptional cases.

View File

@@ -259,6 +259,8 @@ Controls the embedded agent runtime (model/thinking/verbose/timeouts).
If `modelAliases` is configured, you may also use the alias key (e.g. `Opus`).
If you omit the provider, CLAWDIS currently assumes `anthropic` as a temporary
deprecation fallback.
Z.AI models are available as `zai/<model>` (e.g. `zai/glm-4.7`) and require
`ZAI_API_KEY` (or legacy `Z_AI_API_KEY`) in the environment.
`agent.heartbeat` configures periodic heartbeat runs:
- `every`: duration string (`ms`, `s`, `m`, `h`); default unit minutes. Omit or set

View File

@@ -0,0 +1,31 @@
import { completeSimple, getModel } from "@mariozechner/pi-ai";
import { describe, expect, it } from "vitest";
const ZAI_KEY = process.env.ZAI_API_KEY ?? process.env.Z_AI_API_KEY ?? "";
const LIVE = process.env.ZAI_LIVE_TEST === "1" || process.env.LIVE === "1";
const describeLive = LIVE && ZAI_KEY ? describe : describe.skip;
describeLive("zai live", () => {
it("returns assistant text", async () => {
const model = getModel("zai", "glm-4.7");
const res = await completeSimple(
model,
{
messages: [
{
role: "user",
content: "Reply with the word ok.",
timestamp: Date.now(),
},
],
},
{ apiKey: ZAI_KEY, maxTokens: 64 },
);
const text = res.content
.filter((block) => block.type === "text")
.map((block) => block.text.trim())
.join(" ");
expect(text.length).toBeGreaterThan(0);
});
});

View File

@@ -17,6 +17,7 @@ import {
saveSessionStore,
} from "./config/sessions.js";
import { ensureBinary } from "./infra/binaries.js";
import { normalizeEnv } from "./infra/env.js";
import { isMainModule } from "./infra/is-main.js";
import { ensureClawdisCliOnPath } from "./infra/path-env.js";
import {
@@ -32,6 +33,7 @@ import { monitorWebProvider } from "./provider-web.js";
import { assertProvider, normalizeE164, toWhatsappJid } from "./utils.js";
dotenv.config({ quiet: true });
normalizeEnv();
ensureClawdisCliOnPath();
// Capture all console output into structured logs while keeping stdout/stderr behavior.

37
src/infra/env.test.ts Normal file
View File

@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { normalizeZaiEnv } from "./env.js";
describe("normalizeZaiEnv", () => {
it("copies Z_AI_API_KEY to ZAI_API_KEY when missing", () => {
const prevZai = process.env.ZAI_API_KEY;
const prevZAi = process.env.Z_AI_API_KEY;
process.env.ZAI_API_KEY = "";
process.env.Z_AI_API_KEY = "zai-legacy";
normalizeZaiEnv();
expect(process.env.ZAI_API_KEY).toBe("zai-legacy");
if (prevZai === undefined) delete process.env.ZAI_API_KEY;
else process.env.ZAI_API_KEY = prevZai;
if (prevZAi === undefined) delete process.env.Z_AI_API_KEY;
else process.env.Z_AI_API_KEY = prevZAi;
});
it("does not override existing ZAI_API_KEY", () => {
const prevZai = process.env.ZAI_API_KEY;
const prevZAi = process.env.Z_AI_API_KEY;
process.env.ZAI_API_KEY = "zai-current";
process.env.Z_AI_API_KEY = "zai-legacy";
normalizeZaiEnv();
expect(process.env.ZAI_API_KEY).toBe("zai-current");
if (prevZai === undefined) delete process.env.ZAI_API_KEY;
else process.env.ZAI_API_KEY = prevZai;
if (prevZAi === undefined) delete process.env.Z_AI_API_KEY;
else process.env.Z_AI_API_KEY = prevZAi;
});
});

9
src/infra/env.ts Normal file
View File

@@ -0,0 +1,9 @@
export function normalizeZaiEnv(): void {
if (!process.env.ZAI_API_KEY?.trim() && process.env.Z_AI_API_KEY?.trim()) {
process.env.ZAI_API_KEY = process.env.Z_AI_API_KEY;
}
}
export function normalizeEnv(): void {
normalizeZaiEnv();
}