mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-08 07:52:12 +03:00
feat(providers): refresh Z.ai Web models and browser transport (#12524)
Validado em lote numa worktree combinada com #12524, #12538, #12277 e #12367 sobre o tip de release/v3.8.51: os quatro boardaram sem conflito (áreas disjuntas — zai-web, nvidia, clova, cursor/devin/fable). typecheck:core limpo, check:provider-consistency OK (272 entradas REGISTRY, 355 providers canônicos), check:known-symbols OK, e 305/305 nos testes tocados pelos quatro PRs. Os IDs de modelo adicionados foram conferidos individualmente. Obrigado, @backryun.
This commit is contained in:
@@ -14,30 +14,27 @@ export const zai_webProvider: RegistryEntry = {
|
||||
// Z.ai's visible "Tools" switch enables its internal VLM/MCP tools. It does
|
||||
// not accept caller-supplied OpenAI `tools`, which remains disabled here.
|
||||
models: [
|
||||
{
|
||||
id: "glm-5.3-flash",
|
||||
name: "GLM-5.3-Flash",
|
||||
toolCalling: false,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["low", "high", "max"],
|
||||
supportsVision: true,
|
||||
},
|
||||
{
|
||||
id: "glm-5.3",
|
||||
name: "GLM-5.3",
|
||||
toolCalling: false,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["low", "high", "max"],
|
||||
},
|
||||
{
|
||||
id: "glm-5.2",
|
||||
name: "GLM-5.2",
|
||||
toolCalling: false,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
{
|
||||
id: "GLM-5.1",
|
||||
name: "GLM-5.1",
|
||||
toolCalling: false,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
{
|
||||
id: "GLM-5-Turbo",
|
||||
name: "GLM-5-Turbo",
|
||||
toolCalling: false,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
{
|
||||
id: "GLM-5v-Turbo",
|
||||
name: "GLM-5V-Turbo",
|
||||
toolCalling: false,
|
||||
supportsReasoning: true,
|
||||
supportsVision: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
* browser-issued CAPTCHA proof for chat completions. The browser transport is
|
||||
* the default; callers with a short-lived proof can use the direct HTTP path.
|
||||
*
|
||||
* Completions go to /api/v2/chat/completions; the older unversioned
|
||||
* /api/chat/completions path is stale and 404s model-independently (#8014).
|
||||
* Completions go to /api/v2/chat/completions. Z.ai's CAPTCHA rejects true
|
||||
* headless Chromium with F001, so the browser transport uses off-screen headed
|
||||
* Chromium while retaining the shared browser pool.
|
||||
*/
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import { BaseExecutor, type ExecuteInput } from "./base.ts";
|
||||
@@ -67,6 +68,7 @@ export {
|
||||
parseZaiFrontendVersion,
|
||||
resolveZaiThinkingConfig,
|
||||
resolveZaiVlmConfig,
|
||||
zaiUpstreamModelId,
|
||||
} from "./zai-web/protocol.ts";
|
||||
export type {
|
||||
ZaiModelCapabilities,
|
||||
@@ -174,6 +176,7 @@ function buildZaiBrowserChatOptions(input: {
|
||||
userAgent: ZAI_USER_AGENT,
|
||||
locale: "en-US",
|
||||
timezone: "Asia/Seoul",
|
||||
headless: false,
|
||||
inputSelector: "#chat-input",
|
||||
submitButtonSelector: '[aria-label="Send Message"] button:not([disabled])',
|
||||
submitButtonMode: "dom",
|
||||
@@ -243,7 +246,7 @@ function resolveZaiRequest(
|
||||
const modelId = (bodyObj.model as string) || model || ZAI_DEFAULT_MODEL;
|
||||
if (imageUrls.length > 0 && !getZaiModelCapabilities(modelId).vision) {
|
||||
return fail(
|
||||
`Z.ai model ${unprefixedModelId(modelId)} does not accept image input; use GLM-5V-Turbo.`
|
||||
`Z.ai model ${unprefixedModelId(modelId)} does not accept image input; use GLM-5.3-Flash.`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,17 @@ async function runStage(name: string, action: () => Promise<void>): Promise<void
|
||||
async function selectZaiBrowserModel(page: Page, modelName: string): Promise<void> {
|
||||
const selector = page.locator('[aria-label="Select a model"]').first();
|
||||
await selector.waitFor({ state: "visible", timeout: 10_000 });
|
||||
if ((await selector.innerText()).includes(modelName)) return;
|
||||
if ((await selector.getByText(modelName, { exact: true }).count()) > 0) return;
|
||||
|
||||
// The landing-page hero animation can remain above the already-visible
|
||||
// selector and make coordinate-based clicks time out.
|
||||
await selector.evaluate((element) => (element as HTMLElement).click());
|
||||
const menu = page.locator('[role="menu"]').filter({ hasText: modelName }).first();
|
||||
await menu.waitFor({ state: "visible", timeout: 5_000 });
|
||||
const modelButton = menu.locator("button").filter({ hasText: modelName }).first();
|
||||
const modelButton = menu
|
||||
.getByText(modelName, { exact: true })
|
||||
.first()
|
||||
.locator("xpath=ancestor::button[1]");
|
||||
await modelButton.evaluate((element) => (element as HTMLElement).click());
|
||||
await page
|
||||
.locator('[aria-label="Select a model"]')
|
||||
@@ -73,13 +76,13 @@ async function setZaiBrowserWebSearch(page: Page, enabled: boolean): Promise<voi
|
||||
}
|
||||
}
|
||||
|
||||
/** Pick the High/Max effort button inside an already-open Deep Think menu. */
|
||||
/** Pick the Low/High/Max effort button inside an already-open Deep Think menu. */
|
||||
async function selectZaiBrowserEffortLevel(
|
||||
menu: ReturnType<Page["locator"]>,
|
||||
effort: ZaiThinkingConfig["effort"]
|
||||
): Promise<void> {
|
||||
const effortButton = menu.locator("button").filter({
|
||||
hasText: effort === "high" ? "High" : "Max",
|
||||
hasText: effort === "low" ? "Low" : effort === "high" ? "High" : "Max",
|
||||
});
|
||||
if ((await effortButton.getAttribute("data-selected")) === "true") return;
|
||||
await runStage(`select ${effort}`, () =>
|
||||
|
||||
@@ -7,8 +7,8 @@ import { normalizeCookie, sanitizeErrorMessage } from "../../utils/error.ts";
|
||||
export const ZAI_BASE_URL = "https://chat.z.ai";
|
||||
export const ZAI_NEW_CHAT_URL = `${ZAI_BASE_URL}/api/v1/chats/new`;
|
||||
export const ZAI_CHAT_URL = `${ZAI_BASE_URL}/api/v2/chat/completions`;
|
||||
export const ZAI_DEFAULT_MODEL = "GLM-5.1";
|
||||
export const ZAI_DEFAULT_FE_VERSION = "prod-fe-1.1.79";
|
||||
export const ZAI_DEFAULT_MODEL = "glm-5.3";
|
||||
export const ZAI_DEFAULT_FE_VERSION = "prod-fe-1.1.92";
|
||||
export const ZAI_USER_AGENT =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36";
|
||||
export const ZAI_FE_VERSION_CACHE_TTL_MS = 15 * 60 * 1000;
|
||||
@@ -21,7 +21,7 @@ export interface NewChatRequest {
|
||||
userMessageId: string;
|
||||
}
|
||||
|
||||
export type ZaiReasoningEffort = "high" | "max";
|
||||
export type ZaiReasoningEffort = "low" | "high" | "max";
|
||||
|
||||
export interface ZaiThinkingConfig {
|
||||
enabled: boolean;
|
||||
@@ -61,12 +61,23 @@ const NO_ZAI_MODEL_CAPABILITIES: ZaiModelCapabilities = Object.freeze({
|
||||
});
|
||||
|
||||
/**
|
||||
* Verified against chat.z.ai/api/models (prod-fe-1.1.79).
|
||||
* Verified against chat.z.ai/api/models (prod-fe-1.1.92).
|
||||
* `returnFc` is the site's internal function-call result capability; it is
|
||||
* distinct from accepting caller-supplied OpenAI `tools`.
|
||||
*/
|
||||
const ZAI_MODEL_CAPABILITIES: Record<string, ZaiModelCapabilities> = {
|
||||
"glm-5.2": {
|
||||
"glm-5.3-flash": {
|
||||
mcp: false,
|
||||
reasoningEffort: true,
|
||||
returnFc: true,
|
||||
thinking: true,
|
||||
vision: true,
|
||||
vlmTools: false,
|
||||
vlmWebSearch: false,
|
||||
vlmWebsiteMode: false,
|
||||
webSearch: true,
|
||||
},
|
||||
"glm-5.3": {
|
||||
mcp: true,
|
||||
reasoningEffort: true,
|
||||
returnFc: true,
|
||||
@@ -77,9 +88,9 @@ const ZAI_MODEL_CAPABILITIES: Record<string, ZaiModelCapabilities> = {
|
||||
vlmWebsiteMode: false,
|
||||
webSearch: true,
|
||||
},
|
||||
"glm-5.1": {
|
||||
"glm-5.2": {
|
||||
mcp: true,
|
||||
reasoningEffort: false,
|
||||
reasoningEffort: true,
|
||||
returnFc: true,
|
||||
thinking: true,
|
||||
vision: false,
|
||||
@@ -88,28 +99,6 @@ const ZAI_MODEL_CAPABILITIES: Record<string, ZaiModelCapabilities> = {
|
||||
vlmWebsiteMode: false,
|
||||
webSearch: true,
|
||||
},
|
||||
"glm-5-turbo": {
|
||||
mcp: true,
|
||||
reasoningEffort: false,
|
||||
returnFc: true,
|
||||
thinking: true,
|
||||
vision: false,
|
||||
vlmTools: false,
|
||||
vlmWebSearch: false,
|
||||
vlmWebsiteMode: false,
|
||||
webSearch: true,
|
||||
},
|
||||
"glm-5v-turbo": {
|
||||
mcp: false,
|
||||
reasoningEffort: false,
|
||||
returnFc: true,
|
||||
thinking: true,
|
||||
vision: true,
|
||||
vlmTools: true,
|
||||
vlmWebSearch: true,
|
||||
vlmWebsiteMode: true,
|
||||
webSearch: true,
|
||||
},
|
||||
};
|
||||
|
||||
export function asRecord(value: unknown): Record<string, unknown> | null {
|
||||
@@ -136,6 +125,7 @@ export function describeZaiBrowserFailure(result: {
|
||||
status: number;
|
||||
body: Buffer;
|
||||
observedPostUrls?: string[];
|
||||
observedPostResponses?: Array<{ url: string; status: number }>;
|
||||
timing: { captureResponseMs: number; totalMs: number };
|
||||
}): string {
|
||||
const status = result.status > 0 ? String(result.status) : "no matching response";
|
||||
@@ -144,10 +134,16 @@ export function describeZaiBrowserFailure(result: {
|
||||
result.observedPostUrls && result.observedPostUrls.length > 0
|
||||
? ` Observed POST targets: ${result.observedPostUrls.join(", ")}.`
|
||||
: "";
|
||||
const observedResponses =
|
||||
result.observedPostResponses && result.observedPostResponses.length > 0
|
||||
? ` Observed POST responses: ${result.observedPostResponses
|
||||
.map(({ url, status }) => `${url} [${status}]`)
|
||||
.join(", ")}.`
|
||||
: "";
|
||||
const detail =
|
||||
browserFailureDetail(result.body) ||
|
||||
(result.status === 0
|
||||
? `The page did not issue the expected authenticated chat completion request.${observed}`
|
||||
? `The page did not issue the expected authenticated chat completion request.${observed}${observedResponses}`
|
||||
: "The browser response body was empty.");
|
||||
return `Z.ai browser transport failed (${status}; ${timing}): ${detail}`;
|
||||
}
|
||||
@@ -307,17 +303,27 @@ export function unprefixedModelId(modelId: string): string {
|
||||
return modelId.trim().split("/").at(-1) || modelId.trim();
|
||||
}
|
||||
|
||||
export function browserModelName(modelId: string): string {
|
||||
/** Map OmniRoute's public Flash id to the opaque id used by chat.z.ai's wire API. */
|
||||
export function zaiUpstreamModelId(modelId: string): string {
|
||||
const unprefixed = unprefixedModelId(modelId);
|
||||
if (unprefixed.toLowerCase() === "glm-5.2") return "GLM-5.2";
|
||||
if (unprefixed.toLowerCase() === "glm-5v-turbo") return "GLM-5V-Turbo";
|
||||
return unprefixed;
|
||||
return unprefixed.toLowerCase() === "glm-5.3-flash" ? "x-preview-l" : unprefixed;
|
||||
}
|
||||
|
||||
function zaiCapabilityModelId(modelId: string): string {
|
||||
const unprefixed = unprefixedModelId(modelId).toLowerCase();
|
||||
return unprefixed === "x-preview-l" ? "glm-5.3-flash" : unprefixed;
|
||||
}
|
||||
|
||||
export function browserModelName(modelId: string): string {
|
||||
const normalized = zaiCapabilityModelId(modelId);
|
||||
if (normalized === "glm-5.3-flash") return "GLM-5.3-Flash";
|
||||
if (normalized === "glm-5.3") return "GLM-5.3";
|
||||
if (normalized === "glm-5.2") return "GLM-5.2";
|
||||
return unprefixedModelId(modelId);
|
||||
}
|
||||
|
||||
export function getZaiModelCapabilities(modelId: string): ZaiModelCapabilities {
|
||||
return (
|
||||
ZAI_MODEL_CAPABILITIES[unprefixedModelId(modelId).toLowerCase()] ?? NO_ZAI_MODEL_CAPABILITIES
|
||||
);
|
||||
return ZAI_MODEL_CAPABILITIES[zaiCapabilityModelId(modelId)] ?? NO_ZAI_MODEL_CAPABILITIES;
|
||||
}
|
||||
|
||||
function getFeatureOption(body: Record<string, unknown>, key: string): unknown {
|
||||
@@ -325,7 +331,7 @@ function getFeatureOption(body: Record<string, unknown>, key: string): unknown {
|
||||
return asRecord(body.features)?.[key];
|
||||
}
|
||||
|
||||
/** Resolve each model's Deep Think control; only GLM-5.2 accepts High/Max effort. */
|
||||
/** Resolve each model's Deep Think control using its currently exposed effort vocabulary. */
|
||||
export function resolveZaiThinkingConfig(
|
||||
modelId: string,
|
||||
body: Record<string, unknown>
|
||||
@@ -339,19 +345,26 @@ export function resolveZaiThinkingConfig(
|
||||
: typeof reasoning?.effort === "string"
|
||||
? reasoning.effort.trim().toLowerCase()
|
||||
: "";
|
||||
const disabled = body.enable_thinking === false || rawEffort === "none" || rawEffort === "off";
|
||||
const supportsLowEffort = zaiCapabilityModelId(modelId) !== "glm-5.2";
|
||||
const effort: ZaiReasoningEffort =
|
||||
rawEffort === "low" || rawEffort === "medium" || rawEffort === "high" ? "high" : "max";
|
||||
rawEffort === "low" && supportsLowEffort
|
||||
? "low"
|
||||
: rawEffort === "low" || rawEffort === "medium" || rawEffort === "high"
|
||||
? "high"
|
||||
: "max";
|
||||
|
||||
return {
|
||||
supported,
|
||||
enabled: supported && !disabled,
|
||||
// The current GLM-5.3/5.2 consumer models expose effort selection but no
|
||||
// non-thinking mode. Keep Deep Think enabled even when a generic client
|
||||
// sends an off/none compatibility value.
|
||||
enabled: supported,
|
||||
effort,
|
||||
effortSupported: capabilities.reasoningEffort,
|
||||
};
|
||||
}
|
||||
|
||||
/** Resolve GLM-5V-Turbo's visible Web Search and Tools controls. */
|
||||
/** Resolve the selected model's visible Web Search and Tools controls. */
|
||||
export function resolveZaiVlmConfig(modelId: string, body: Record<string, unknown>): ZaiVlmConfig {
|
||||
const capabilities = getZaiModelCapabilities(modelId);
|
||||
const toolsOption = getFeatureOption(body, "vlm_tools_enable");
|
||||
@@ -425,7 +438,7 @@ export function buildZaiCompletionUrl(input: {
|
||||
hostname: "chat.z.ai",
|
||||
protocol: "https:",
|
||||
referrer: "",
|
||||
title: "Z.ai - Advanced AI Chatbot & Agent powered by GLM-5.2",
|
||||
title: "Z.ai - Advanced AI Chatbot & Agent powered by GLM-5.3",
|
||||
timezone_offset: "0",
|
||||
local_time: now.toISOString(),
|
||||
utc_time: now.toUTCString(),
|
||||
@@ -448,13 +461,14 @@ export function buildZaiNewChatBody(
|
||||
): NewChatRequest {
|
||||
const prompt = latestUserPrompt(messages);
|
||||
const userMessageId = randomUUID();
|
||||
const upstreamModelId = zaiUpstreamModelId(modelId);
|
||||
return {
|
||||
userMessageId,
|
||||
payload: {
|
||||
chat: {
|
||||
id: "",
|
||||
title: "New Chat",
|
||||
models: [modelId],
|
||||
models: [upstreamModelId],
|
||||
params: {},
|
||||
history: {
|
||||
messages: {
|
||||
@@ -465,7 +479,7 @@ export function buildZaiNewChatBody(
|
||||
role: "user",
|
||||
content: prompt,
|
||||
timestamp: Math.floor(Date.now() / 1000),
|
||||
models: [modelId],
|
||||
models: [upstreamModelId],
|
||||
},
|
||||
},
|
||||
currentId: userMessageId,
|
||||
@@ -530,7 +544,7 @@ export function buildZaiRequestBody(input: {
|
||||
}
|
||||
return {
|
||||
stream: true,
|
||||
model: input.modelId,
|
||||
model: zaiUpstreamModelId(input.modelId),
|
||||
messages: foldMessages(input.messages),
|
||||
signature_prompt: input.prompt,
|
||||
params,
|
||||
|
||||
@@ -236,6 +236,7 @@ export async function browserBackedChat(
|
||||
userAgent,
|
||||
locale,
|
||||
timezone,
|
||||
headless,
|
||||
inputSelector,
|
||||
submitButtonSelector,
|
||||
submitButtonMode = "playwright",
|
||||
@@ -257,11 +258,13 @@ export async function browserBackedChat(
|
||||
userAgent,
|
||||
locale,
|
||||
timezone,
|
||||
headless,
|
||||
});
|
||||
const acquireContextMs = Date.now() - tAcquireStart;
|
||||
|
||||
const page = await openPage(pooled);
|
||||
const observedPostUrls: string[] = [];
|
||||
const observedPostResponses: Array<{ url: string; status: number }> = [];
|
||||
page.on("request", (request) => {
|
||||
if (request.method() !== "POST") return;
|
||||
try {
|
||||
@@ -273,6 +276,19 @@ export async function browserBackedChat(
|
||||
// Ignore malformed/non-HTTP request URLs.
|
||||
}
|
||||
});
|
||||
page.on("response", (response) => {
|
||||
if (response.request().method() !== "POST") return;
|
||||
try {
|
||||
const url = new URL(response.url());
|
||||
if (!url.hostname.endsWith(chatUrlMatchDomain)) return;
|
||||
observedPostResponses.push({
|
||||
url: `${url.origin}${url.pathname}`,
|
||||
status: response.status(),
|
||||
});
|
||||
} catch {
|
||||
// Ignore malformed/non-HTTP response URLs.
|
||||
}
|
||||
});
|
||||
try {
|
||||
const tNavStart = Date.now();
|
||||
await withAbort(
|
||||
@@ -379,6 +395,7 @@ export async function browserBackedChat(
|
||||
body,
|
||||
isStealth: pooled.isStealth,
|
||||
observedPostUrls,
|
||||
observedPostResponses,
|
||||
timing: {
|
||||
acquireContextMs,
|
||||
navigateMs,
|
||||
@@ -404,6 +421,7 @@ export async function browserBackedChat(
|
||||
body,
|
||||
isStealth: pooled.isStealth,
|
||||
observedPostUrls,
|
||||
observedPostResponses,
|
||||
timing: {
|
||||
acquireContextMs,
|
||||
navigateMs: 0,
|
||||
|
||||
@@ -29,6 +29,8 @@ export interface BrowserBackedChatRequest {
|
||||
locale?: string;
|
||||
/** Browser IANA timezone. Defaults to America/New_York. */
|
||||
timezone?: string;
|
||||
/** Launch a headed browser when the provider rejects true headless mode. */
|
||||
headless?: boolean;
|
||||
/** Selector for the provider chat input. */
|
||||
inputSelector: string;
|
||||
/** Optional selector for the provider submit button. */
|
||||
@@ -64,6 +66,8 @@ export interface BrowserBackedChatResult {
|
||||
isStealth: boolean;
|
||||
/** Sanitized POST targets observed while submitting. */
|
||||
observedPostUrls?: string[];
|
||||
/** Sanitized POST response targets and statuses observed while submitting. */
|
||||
observedPostResponses?: Array<{ url: string; status: number }>;
|
||||
timing: {
|
||||
acquireContextMs: number;
|
||||
navigateMs: number;
|
||||
|
||||
@@ -30,7 +30,7 @@ function installZaiFetch(
|
||||
const value = String(url);
|
||||
if (value === ZAI_HOME_URL) {
|
||||
return new Response(
|
||||
'<script src="https://z-cdn.chatglm.cn/z-ai/frontend/prod-fe-1.1.79/assets/index.js"></script>'
|
||||
'<script src="https://z-cdn.chatglm.cn/z-ai/frontend/prod-fe-1.1.92/assets/index.js"></script>'
|
||||
);
|
||||
}
|
||||
if (value === ZAI_NEW_CHAT_URL) {
|
||||
@@ -90,13 +90,18 @@ describe("ZaiWebExecutor", () => {
|
||||
"Z.ai browser transport failed (502; capture 30001ms, total 33412ms): " +
|
||||
"browserBackedChat failed: response.body unavailable"
|
||||
);
|
||||
assert.match(
|
||||
assert.equal(
|
||||
mod.describeZaiBrowserFailure({
|
||||
status: 0,
|
||||
body: Buffer.alloc(0),
|
||||
observedPostUrls: ["https://chat.z.ai/api/v1/chats/new"],
|
||||
observedPostResponses: [{ url: "https://chat.z.ai/api/v1/chats/new", status: 200 }],
|
||||
timing: { captureResponseMs: 30_000, totalMs: 33_000 },
|
||||
}),
|
||||
/no matching response.*did not issue the expected authenticated chat completion request/
|
||||
"Z.ai browser transport failed (no matching response; capture 30000ms, total 33000ms): " +
|
||||
"The page did not issue the expected authenticated chat completion request. " +
|
||||
"Observed POST targets: https://chat.z.ai/api/v1/chats/new. " +
|
||||
"Observed POST responses: https://chat.z.ai/api/v1/chats/new [200]."
|
||||
);
|
||||
});
|
||||
|
||||
@@ -128,9 +133,9 @@ describe("ZaiWebExecutor", () => {
|
||||
it("parses the deployed frontend version from the homepage asset path", () => {
|
||||
assert.equal(
|
||||
mod.parseZaiFrontendVersion(
|
||||
"https://z-cdn.chatglm.cn/z-ai/frontend/prod-fe-1.1.79/assets/index.js"
|
||||
"https://z-cdn.chatglm.cn/z-ai/frontend/prod-fe-1.1.92/assets/index.js"
|
||||
),
|
||||
"prod-fe-1.1.79"
|
||||
"prod-fe-1.1.92"
|
||||
);
|
||||
assert.equal(mod.parseZaiFrontendVersion("<html></html>"), null);
|
||||
});
|
||||
@@ -211,86 +216,87 @@ describe("ZaiWebExecutor", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("enables Deep Think for every public model and limits effort to GLM-5.2", () => {
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.2", {}), {
|
||||
it("maps the three public models to their current Deep Think effort vocabularies", () => {
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.3-flash", { reasoning_effort: "low" }), {
|
||||
supported: true,
|
||||
enabled: true,
|
||||
effort: "max",
|
||||
effort: "low",
|
||||
effortSupported: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("zw/glm-5.2", { reasoning_effort: "medium" }), {
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("zw/glm-5.3", { reasoning_effort: "medium" }), {
|
||||
supported: true,
|
||||
enabled: true,
|
||||
effort: "high",
|
||||
effortSupported: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.2", { reasoning: { effort: "high" } }), {
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.3", { reasoning: { effort: "high" } }), {
|
||||
supported: true,
|
||||
enabled: true,
|
||||
effort: "high",
|
||||
effortSupported: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.2", { reasoning_effort: "off" }), {
|
||||
supported: true,
|
||||
enabled: false,
|
||||
effort: "max",
|
||||
effortSupported: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("GLM-5.1", { reasoning_effort: "max" }), {
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.3", { reasoning_effort: "off" }), {
|
||||
supported: true,
|
||||
enabled: true,
|
||||
effort: "max",
|
||||
effortSupported: false,
|
||||
effortSupported: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.3", { enable_thinking: false }), {
|
||||
supported: true,
|
||||
enabled: true,
|
||||
effort: "max",
|
||||
effortSupported: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiThinkingConfig("glm-5.2", { reasoning_effort: "low" }), {
|
||||
supported: true,
|
||||
enabled: true,
|
||||
effort: "high",
|
||||
effortSupported: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("maps GLM-5V-Turbo vision and internal VLM controls from live capabilities", () => {
|
||||
assert.deepEqual(mod.getZaiModelCapabilities("zw/GLM-5v-Turbo"), {
|
||||
it("maps GLM-5.3-Flash vision and web controls from live capabilities", () => {
|
||||
assert.deepEqual(mod.getZaiModelCapabilities("zw/glm-5.3-flash"), {
|
||||
mcp: false,
|
||||
reasoningEffort: false,
|
||||
reasoningEffort: true,
|
||||
returnFc: true,
|
||||
thinking: true,
|
||||
vision: true,
|
||||
vlmTools: true,
|
||||
vlmWebSearch: true,
|
||||
vlmWebsiteMode: true,
|
||||
vlmTools: false,
|
||||
vlmWebSearch: false,
|
||||
vlmWebsiteMode: false,
|
||||
webSearch: true,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiVlmConfig("GLM-5v-Turbo", {}), {
|
||||
toolsEnabled: true,
|
||||
webSearchEnabled: true,
|
||||
websiteModeEnabled: true,
|
||||
assert.deepEqual(mod.getZaiModelCapabilities("x-preview-l"), {
|
||||
mcp: false,
|
||||
reasoningEffort: true,
|
||||
returnFc: true,
|
||||
thinking: true,
|
||||
vision: true,
|
||||
vlmTools: false,
|
||||
vlmWebSearch: false,
|
||||
vlmWebsiteMode: false,
|
||||
webSearch: true,
|
||||
});
|
||||
assert.deepEqual(
|
||||
mod.resolveZaiVlmConfig("GLM-5v-Turbo", {
|
||||
features: {
|
||||
vlm_tools_enable: false,
|
||||
vlm_web_search_enable: false,
|
||||
vlm_website_mode: false,
|
||||
},
|
||||
}),
|
||||
{
|
||||
toolsEnabled: false,
|
||||
webSearchEnabled: false,
|
||||
websiteModeEnabled: true,
|
||||
}
|
||||
);
|
||||
assert.deepEqual(mod.resolveZaiVlmConfig("GLM-5.1", {}), {
|
||||
assert.deepEqual(mod.resolveZaiVlmConfig("glm-5.3-flash", { web_search: true }), {
|
||||
toolsEnabled: false,
|
||||
webSearchEnabled: true,
|
||||
websiteModeEnabled: false,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiVlmConfig("glm-5.3", {}), {
|
||||
toolsEnabled: false,
|
||||
webSearchEnabled: false,
|
||||
websiteModeEnabled: false,
|
||||
});
|
||||
assert.deepEqual(mod.resolveZaiVlmConfig("GLM-5.1", { web_search: true }), {
|
||||
toolsEnabled: false,
|
||||
webSearchEnabled: true,
|
||||
websiteModeEnabled: false,
|
||||
});
|
||||
assert.equal(mod.zaiUpstreamModelId("zai-web/glm-5.3-flash"), "x-preview-l");
|
||||
assert.equal(mod.zaiUpstreamModelId("zai-web/glm-5.3"), "glm-5.3");
|
||||
assert.equal(mod.getZaiModelCapabilities("GLM-5.1").thinking, false);
|
||||
});
|
||||
|
||||
it("returns a credential error when no session credential is provided", async () => {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5.1",
|
||||
model: "glm-5.3",
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
stream: false,
|
||||
credentials: { apiKey: "" },
|
||||
@@ -313,7 +319,6 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "glm-5.2",
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
stream: false,
|
||||
credentials: { apiKey: TEST_TOKEN },
|
||||
@@ -322,8 +327,10 @@ describe("ZaiWebExecutor", () => {
|
||||
|
||||
const completion = await result.response.json();
|
||||
assert.equal(completion.choices[0].message.content, "Browser");
|
||||
assert.equal(completion.model, "glm-5.3");
|
||||
assert.equal(capturedRequest?.localStorage?.token, TEST_TOKEN);
|
||||
assert.equal(capturedRequest?.localStorageOrigin, "https://chat.z.ai");
|
||||
assert.equal(capturedRequest?.headless, false);
|
||||
assert.equal(capturedRequest?.inputSelector, "#chat-input");
|
||||
assert.equal(
|
||||
capturedRequest?.submitButtonSelector,
|
||||
@@ -331,7 +338,7 @@ describe("ZaiWebExecutor", () => {
|
||||
);
|
||||
assert.equal(capturedRequest?.submitButtonMode, "dom");
|
||||
assert.equal(capturedRequest?.userMessage, "hi");
|
||||
assert.match(capturedRequest?.chatPageUrl ?? "", /model=GLM-5\.2/);
|
||||
assert.match(capturedRequest?.chatPageUrl ?? "", /model=GLM-5\.3/);
|
||||
assert.equal(typeof capturedRequest?.beforeSubmit, "function");
|
||||
assert.equal(result.headers["X-OmniRoute-Transport"], "browser");
|
||||
assert.equal(result.transformedBody.browser_backed, true);
|
||||
@@ -342,7 +349,7 @@ describe("ZaiWebExecutor", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("configures GLM-5V-Turbo controls on the browser transport", async () => {
|
||||
it("configures GLM-5.3-Flash on the browser transport", async () => {
|
||||
let capturedRequest: BrowserBackedChatRequest | null = null;
|
||||
browserChat.__setBrowserBackedChatOverrideForTesting(async (request) => {
|
||||
capturedRequest = request;
|
||||
@@ -352,8 +359,8 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5v-Turbo",
|
||||
body: { messages: [{ role: "user", content: "use the model tools" }] },
|
||||
model: "glm-5.3-flash",
|
||||
body: { messages: [{ role: "user", content: "use flash" }] },
|
||||
stream: false,
|
||||
credentials: { apiKey: TEST_TOKEN },
|
||||
signal: null,
|
||||
@@ -361,19 +368,19 @@ describe("ZaiWebExecutor", () => {
|
||||
|
||||
const completion = await result.response.json();
|
||||
assert.equal(completion.choices[0].message.content, "VLM");
|
||||
assert.match(capturedRequest?.chatPageUrl ?? "", /model=GLM-5V-Turbo/);
|
||||
assert.match(capturedRequest?.chatPageUrl ?? "", /model=GLM-5\.3-Flash/);
|
||||
assert.equal(typeof capturedRequest?.beforeSubmit, "function");
|
||||
assert.equal(result.transformedBody.enable_thinking, true);
|
||||
assert.equal(result.transformedBody.vlm_tools_enable, true);
|
||||
assert.equal(result.transformedBody.vlm_web_search_enable, true);
|
||||
assert.equal(result.transformedBody.vlm_website_mode, true);
|
||||
assert.equal("reasoning_effort" in result.transformedBody, false);
|
||||
assert.equal(result.transformedBody.reasoning_effort, "max");
|
||||
assert.equal(result.transformedBody.vlm_tools_enable, false);
|
||||
assert.equal(result.transformedBody.vlm_web_search_enable, false);
|
||||
assert.equal(result.transformedBody.vlm_website_mode, false);
|
||||
} finally {
|
||||
browserChat.__resetBrowserBackedChatOverrideForTesting();
|
||||
}
|
||||
});
|
||||
|
||||
it("uploads GLM-5V-Turbo image input through the authenticated browser page", async () => {
|
||||
it("uploads GLM-5.3-Flash image input through the authenticated browser page", async () => {
|
||||
let capturedRequest: BrowserBackedChatRequest | null = null;
|
||||
browserChat.__setBrowserBackedChatOverrideForTesting(async (request) => {
|
||||
capturedRequest = request;
|
||||
@@ -383,7 +390,7 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5v-Turbo",
|
||||
model: "glm-5.3-flash",
|
||||
body: {
|
||||
messages: [
|
||||
{
|
||||
@@ -445,7 +452,7 @@ describe("ZaiWebExecutor", () => {
|
||||
|
||||
assert.equal(result.response.status, 400);
|
||||
const parsed = await result.response.json();
|
||||
assert.match(parsed.error.message, /use GLM-5V-Turbo/);
|
||||
assert.match(parsed.error.message, /use GLM-5\.3-Flash/);
|
||||
});
|
||||
|
||||
it("creates a chat, signs the v2 request, and forwards the CAPTCHA proof", async () => {
|
||||
@@ -461,9 +468,9 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5.1",
|
||||
model: "glm-5.3",
|
||||
body: {
|
||||
model: "GLM-5.1",
|
||||
model: "glm-5.3",
|
||||
messages: [{ role: "user", content: "hello" }],
|
||||
temperature: 0.4,
|
||||
web_search: true,
|
||||
@@ -477,7 +484,7 @@ describe("ZaiWebExecutor", () => {
|
||||
const newChatHeaders = capture.newChatInit?.headers as Record<string, string>;
|
||||
assert.equal(newChatHeaders.Authorization, `Bearer ${TEST_TOKEN}`);
|
||||
const newChatBody = JSON.parse(String(capture.newChatInit?.body));
|
||||
assert.deepEqual(newChatBody.chat.models, ["GLM-5.1"]);
|
||||
assert.deepEqual(newChatBody.chat.models, ["glm-5.3"]);
|
||||
assert.equal(newChatBody.chat.history.currentId.length, 36);
|
||||
assert.equal(newChatBody.chat.enable_thinking, true);
|
||||
assert.equal(newChatBody.chat.auto_web_search, true);
|
||||
@@ -494,11 +501,11 @@ describe("ZaiWebExecutor", () => {
|
||||
|
||||
const headers = capture.completionInit?.headers as Record<string, string>;
|
||||
assert.equal(headers.Authorization, `Bearer ${TEST_TOKEN}`);
|
||||
assert.equal(headers["X-FE-Version"], "prod-fe-1.1.79");
|
||||
assert.equal(headers["X-FE-Version"], "prod-fe-1.1.92");
|
||||
assert.match(headers["X-Signature"], /^[a-f0-9]{64}$/);
|
||||
|
||||
const parsedBody = JSON.parse(String(capture.completionInit?.body));
|
||||
assert.equal(parsedBody.model, "GLM-5.1");
|
||||
assert.equal(parsedBody.model, "glm-5.3");
|
||||
assert.equal(parsedBody.stream, true);
|
||||
assert.deepEqual(parsedBody.messages, [{ role: "user", content: "hello" }]);
|
||||
assert.equal(parsedBody.signature_prompt, "hello");
|
||||
@@ -508,7 +515,7 @@ describe("ZaiWebExecutor", () => {
|
||||
assert.equal(parsedBody.features.web_search, false);
|
||||
assert.equal(parsedBody.features.auto_web_search, true);
|
||||
assert.equal(parsedBody.features.enable_thinking, true);
|
||||
assert.equal("reasoning_effort" in parsedBody.features, false);
|
||||
assert.equal(parsedBody.features.reasoning_effort, "max");
|
||||
assert.equal(result.headers.Authorization, "Bearer [REDACTED]");
|
||||
assert.equal(result.transformedBody.captcha_verify_param, "[REDACTED]");
|
||||
} finally {
|
||||
@@ -516,7 +523,7 @@ describe("ZaiWebExecutor", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("sends GLM-5.2 Deep Think High through the direct request path", async () => {
|
||||
it("sends GLM-5.3 Deep Think Low through the direct request path", async () => {
|
||||
const capture: ZaiFetchCapture = {};
|
||||
const originalFetch = installZaiFetch(
|
||||
() =>
|
||||
@@ -529,36 +536,36 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
await executor.execute({
|
||||
model: "glm-5.2",
|
||||
model: "glm-5.3",
|
||||
body: {
|
||||
model: "glm-5.2",
|
||||
model: "glm-5.3",
|
||||
messages: [{ role: "user", content: "think carefully" }],
|
||||
reasoning_effort: "high",
|
||||
reasoning_effort: "low",
|
||||
},
|
||||
stream: false,
|
||||
credentials: { apiKey: TEST_CREDENTIAL },
|
||||
signal: null,
|
||||
});
|
||||
|
||||
// #8014: completions must target the versioned v2 path. The query string
|
||||
// The query string
|
||||
// carries the per-request signature payload, so match the endpoint prefix.
|
||||
assert.ok(
|
||||
String(capture.completionUrl).startsWith("https://chat.z.ai/api/v2/chat/completions?"),
|
||||
`expected the v2 completions endpoint, got ${capture.completionUrl}`
|
||||
`expected the current completions endpoint, got ${capture.completionUrl}`
|
||||
);
|
||||
const newChatBody = JSON.parse(String(capture.newChatInit?.body));
|
||||
assert.equal(newChatBody.chat.enable_thinking, true);
|
||||
assert.equal(newChatBody.chat.reasoning_effort, "high");
|
||||
assert.equal(newChatBody.chat.reasoning_effort, "low");
|
||||
|
||||
const completionBody = JSON.parse(String(capture.completionInit?.body));
|
||||
assert.equal(completionBody.features.enable_thinking, true);
|
||||
assert.equal(completionBody.features.reasoning_effort, "high");
|
||||
assert.equal(completionBody.features.reasoning_effort, "low");
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
it("sends GLM-5V-Turbo VLM tools and web-search flags through the direct path", async () => {
|
||||
it("maps GLM-5.3-Flash to its opaque wire id on the direct path", async () => {
|
||||
const capture: ZaiFetchCapture = {};
|
||||
const originalFetch = installZaiFetch(
|
||||
() =>
|
||||
@@ -571,10 +578,11 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
await executor.execute({
|
||||
model: "GLM-5v-Turbo",
|
||||
model: "glm-5.3-flash",
|
||||
body: {
|
||||
model: "GLM-5v-Turbo",
|
||||
messages: [{ role: "user", content: "inspect this image" }],
|
||||
model: "glm-5.3-flash",
|
||||
messages: [{ role: "user", content: "answer quickly" }],
|
||||
web_search: true,
|
||||
},
|
||||
stream: false,
|
||||
credentials: { apiKey: TEST_CREDENTIAL },
|
||||
@@ -582,19 +590,26 @@ describe("ZaiWebExecutor", () => {
|
||||
});
|
||||
|
||||
const newChatBody = JSON.parse(String(capture.newChatInit?.body));
|
||||
assert.deepEqual(newChatBody.chat.models, ["x-preview-l"]);
|
||||
assert.deepEqual(
|
||||
newChatBody.chat.history.messages[newChatBody.chat.history.currentId].models,
|
||||
["x-preview-l"]
|
||||
);
|
||||
assert.equal(newChatBody.chat.enable_thinking, true);
|
||||
assert.equal(newChatBody.chat.auto_web_search, true);
|
||||
assert.equal(newChatBody.chat.extra.vlm_tools_enable, true);
|
||||
assert.equal(newChatBody.chat.extra.vlm_web_search_enable, true);
|
||||
assert.equal(newChatBody.chat.extra.vlm_website_mode, true);
|
||||
assert.equal(newChatBody.chat.reasoning_effort, "max");
|
||||
assert.equal(newChatBody.chat.extra.vlm_tools_enable, false);
|
||||
assert.equal(newChatBody.chat.extra.vlm_web_search_enable, false);
|
||||
assert.equal(newChatBody.chat.extra.vlm_website_mode, false);
|
||||
|
||||
const completionBody = JSON.parse(String(capture.completionInit?.body));
|
||||
assert.equal(completionBody.model, "x-preview-l");
|
||||
assert.equal(completionBody.features.enable_thinking, true);
|
||||
assert.equal(completionBody.features.auto_web_search, false);
|
||||
assert.equal(completionBody.features.vlm_tools_enable, true);
|
||||
assert.equal(completionBody.features.vlm_web_search_enable, true);
|
||||
assert.equal(completionBody.features.vlm_website_mode, true);
|
||||
assert.equal("reasoning_effort" in completionBody.features, false);
|
||||
assert.equal(completionBody.features.reasoning_effort, "max");
|
||||
assert.equal(completionBody.features.auto_web_search, true);
|
||||
assert.equal(completionBody.features.vlm_tools_enable, false);
|
||||
assert.equal(completionBody.features.vlm_web_search_enable, false);
|
||||
assert.equal(completionBody.features.vlm_website_mode, false);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
@@ -619,7 +634,7 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5.1",
|
||||
model: "glm-5.3",
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
stream: false,
|
||||
credentials: { apiKey: TEST_CREDENTIAL },
|
||||
@@ -651,7 +666,7 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5.1",
|
||||
model: "glm-5.3",
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
stream: true,
|
||||
credentials: { apiKey: TEST_CREDENTIAL },
|
||||
@@ -673,7 +688,7 @@ describe("ZaiWebExecutor", () => {
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "GLM-5.1",
|
||||
model: "glm-5.3",
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
stream: false,
|
||||
credentials: { apiKey: TEST_CREDENTIAL },
|
||||
|
||||
@@ -314,7 +314,7 @@ test("resolveModelTestTimeoutMs defaults ordinary model checks to 30 seconds", (
|
||||
|
||||
test("resolveModelTestTimeoutMs gives zai-web checks up to 60 seconds", () => {
|
||||
assert.equal(resolveModelTestTimeoutMs("zai-web", "glm-5.2", 30_000), 60_000);
|
||||
assert.equal(resolveModelTestTimeoutMs("zai-web", "zai-web/GLM-5V-Turbo", 90_000), 90_000);
|
||||
assert.equal(resolveModelTestTimeoutMs("zai-web", "zai-web/glm-5.3-flash", 90_000), 90_000);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -9,7 +9,7 @@ const TEST_TOKEN = "e30.eyJpZCI6InVzZXItMTIzIn0.sig";
|
||||
/**
|
||||
* #8014 guard: the executor must target the versioned v2 completions endpoint,
|
||||
* never the stale unversioned `/api/chat/completions` path, which 404s
|
||||
* model-independently as of 2026-07.
|
||||
* model-independently.
|
||||
*
|
||||
* Setup notes for this flow (the executor now creates a remote chat first and
|
||||
* signs the completion request):
|
||||
@@ -43,7 +43,7 @@ test("#8014: ZaiWebExecutor must POST to the current chat.z.ai v2 chat-completio
|
||||
try {
|
||||
const executor = new mod.ZaiWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "glm-4.6",
|
||||
model: "glm-5.3",
|
||||
body: { messages: [{ role: "user", content: "hello" }] },
|
||||
stream: false,
|
||||
credentials: {
|
||||
@@ -55,7 +55,6 @@ test("#8014: ZaiWebExecutor must POST to the current chat.z.ai v2 chat-completio
|
||||
assert.ok(requested.length > 0, "the direct path must actually reach fetch");
|
||||
|
||||
assert.ok(
|
||||
// Exact-URL match (not a substring test): `requested` holds whole URLs.
|
||||
!requested.some((url) => url === STALE_URL),
|
||||
`zai-web executor POSTed to the stale endpoint — matches #8014's model-independent 404 "Not Found"`
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ const providersDb = await import("../../src/lib/db/providers.ts");
|
||||
const modelsRoute = await import("../../src/app/api/providers/[id]/models/route.ts");
|
||||
const registry = await import("../../open-sse/config/providers/registry/zai-web/index.ts");
|
||||
|
||||
const CURATED_ZAI_WEB_MODEL_IDS = ["glm-5.2", "GLM-5.1", "GLM-5-Turbo", "GLM-5v-Turbo"];
|
||||
const CURATED_ZAI_WEB_MODEL_IDS = ["glm-5.3-flash", "glm-5.3", "glm-5.2"];
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
@@ -31,34 +31,32 @@ test("zai-web publishes the live reasoning and vision capabilities", () => {
|
||||
registry.zai_webProvider.models.map((model) => ({
|
||||
id: model.id,
|
||||
supportsReasoning: model.supportsReasoning === true,
|
||||
supportedThinkingEfforts: model.supportedThinkingEfforts,
|
||||
supportsVision: model.supportsVision === true,
|
||||
toolCalling: model.toolCalling === true,
|
||||
})),
|
||||
[
|
||||
{
|
||||
id: "glm-5.3-flash",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["low", "high", "max"],
|
||||
supportsVision: true,
|
||||
toolCalling: false,
|
||||
},
|
||||
{
|
||||
id: "glm-5.3",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["low", "high", "max"],
|
||||
supportsVision: false,
|
||||
toolCalling: false,
|
||||
},
|
||||
{
|
||||
id: "glm-5.2",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
supportsVision: false,
|
||||
toolCalling: false,
|
||||
},
|
||||
{
|
||||
id: "GLM-5.1",
|
||||
supportsReasoning: true,
|
||||
supportsVision: false,
|
||||
toolCalling: false,
|
||||
},
|
||||
{
|
||||
id: "GLM-5-Turbo",
|
||||
supportsReasoning: true,
|
||||
supportsVision: false,
|
||||
toolCalling: false,
|
||||
},
|
||||
{
|
||||
id: "GLM-5v-Turbo",
|
||||
supportsReasoning: true,
|
||||
supportsVision: true,
|
||||
toolCalling: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user