feat: add Z.AI env support and live test
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
31
src/agents/zai.live.test.ts
Normal file
31
src/agents/zai.live.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
@@ -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
37
src/infra/env.test.ts
Normal 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
9
src/infra/env.ts
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user