refactor(search): fold zai-search into the glm provider

The separate zai-search entry showed "No connections" on the web search
page because credentials live on the `glm` connection, not on it. Every
other provider that does both chat and search (antigravity, kimi, xai,
gemini) declares webSearch on the provider itself, so do the same here.

- glm gains serviceKinds ["llm", "webSearch"] and the MCP searchConfig
- the request builder / normalizer move from "zai-search" to "glm"
- drop the zai-search registry entry and its svg logo, which also
  removes the only need for svg logo support in getProviderIconSrc

ollama-search keeps its own entry and credentialFallback: its search
endpoint is unrelated to the ollama chat transport.
This commit is contained in:
decolua
2026-08-28 16:12:04 +07:00
parent 5a86f6a8d2
commit 9dbdca0e5e
7 changed files with 22 additions and 55 deletions

View File

@@ -394,12 +394,12 @@ function buildOllamaSearchRequest(config, params) {
};
}
// ── Z.AI Coding plan MCP web_search_prime ─────────────────────────────────
// ── GLM Coding plan MCP web_search_prime ─────────────────────────────────
// POST https://api.z.ai/api/mcp/web_search_prime/mcp
// JSON-RPC envelope: { jsonrpc, id, method: "tools/call",
// params: { name: "web_search_prime", arguments: { search_query, count } } }
// Response: { result: { content: [{ type: "text", text: "<json>" }] } }
function buildZaiSearchRequest(config, params) {
function buildGlmSearchRequest(config, params) {
const body = {
jsonrpc: "2.0",
id: `9r-${Date.now()}`,
@@ -437,7 +437,7 @@ const BUILDERS = {
"searxng": buildSearxngRequest,
"xquik": buildXquikRequest,
"ollama-search": buildOllamaSearchRequest,
"zai-search": buildZaiSearchRequest,
"glm": buildGlmSearchRequest,
};
/**

View File

@@ -257,7 +257,7 @@ function normalizeOllamaSearch(data, _query, _searchType) {
return { results, totalResults: results.length };
}
function normalizeZaiSearch(data, _query, _searchType) {
function normalizeGlmSearch(data, _query, _searchType) {
const now = new Date().toISOString();
// MCP envelope: { result: { content: [{ type: "text", text: "<json>" }] } }
let payload = data;
@@ -270,7 +270,7 @@ function normalizeZaiSearch(data, _query, _searchType) {
: Array.isArray(payload) ? payload
: [];
const results = items.map((item, idx) =>
makeResult("zai-search", {
makeResult("glm", {
title: item.title,
url: item.link || item.url,
snippet: item.content || "",
@@ -295,7 +295,7 @@ const NORMALIZERS = {
"searxng": normalizeSearxng,
"xquik": normalizeXquik,
"ollama-search": normalizeOllamaSearch,
"zai-search": normalizeZaiSearch,
"glm": normalizeGlmSearch,
};
/**

View File

@@ -53,6 +53,20 @@ export default {
{ id: "glm-4.7", name: "GLM 4.7" },
{ id: "glm-4.6v", name: "GLM 4.6V (Vision)" },
],
serviceKinds: ["llm", "webSearch"],
// Coding plan bundles web search on the same API key as chat.
searchConfig: {
baseUrl: "https://api.z.ai/api/mcp/web_search_prime/mcp",
method: "POST",
authType: "apikey",
authHeader: "bearer",
costPerQuery: 0,
searchTypes: ["web"],
defaultMaxResults: 5,
maxMaxResults: 50,
timeoutMs: 10000,
cacheTTLMs: 300000,
},
features: {
usage: true,
usageApikey: true,

View File

@@ -98,7 +98,6 @@ import p95 from "./xai.js";
import p96 from "./xiaomi-mimo.js";
import p97 from "./xiaomi-tokenplan.js";
import p98 from "./youcom.js";
import p124 from "./zai-search.js";
import p99 from "./alims-intl.js";
import p100 from "./codebuddy-intl.js";
// Temporarily hidden — no tool calling support (trae SOLO agent / windsurf gRPC skip ToolCallChunk).
@@ -225,7 +224,6 @@ export default [
p96,
p97,
p98,
p124,
p99,
p100,
// p102, // trae — hidden, no tool calling

View File

@@ -1,34 +0,0 @@
export default {
id: "zai-search",
alias: "zai-search",
display: {
name: "GLM Coding Search",
icon: "travel_explore",
color: "#2563EB",
textIcon: "GS",
website: "https://z.ai",
notice: {
text: "Web search via the Z.AI Coding plan MCP endpoint. Reuses the API key from the GLM Coding provider.",
apiKeyUrl: "https://open.bigmodel.cn/usercenter/apikeys",
},
},
category: "apikey",
authType: "apikey",
authModes: ["apikey"],
serviceKinds: ["webSearch"],
// Credential fallback: reuses the GLM Coding plan API key — one key, chat + search.
credentialFallback: "glm",
searchConfig: {
baseUrl: "https://api.z.ai/api/mcp/web_search_prime/mcp",
method: "POST",
authType: "apikey",
authHeader: "bearer",
costPerQuery: 0,
freeMonthlyQuota: 0,
searchTypes: ["web"],
defaultMaxResults: 5,
maxMaxResults: 50,
timeoutMs: 10000,
cacheTTLMs: 300000,
},
};

View File

@@ -1,5 +0,0 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="128" height="128" rx="28" fill="#6366f1"/>
<text x="64" y="64" text-anchor="middle" dominant-baseline="central" font-family="system-ui,-apple-system,sans-serif" font-size="48" font-weight="700" fill="#ffffff">Z</text>
<text x="64" y="98" text-anchor="middle" dominant-baseline="central" font-family="system-ui,-apple-system,sans-serif" font-size="16" font-weight="600" fill="#c7d2fe">.AI</text>
</svg>

Before

Width:  |  Height:  |  Size: 521 B

View File

@@ -8,9 +8,6 @@ const ICON_ALIASES = {
"ollama-search": "ollama",
};
// Providers that ship an .svg logo instead of .png.
const SVG_PROVIDER_IDS = new Set(["zai-search"]);
// Runtime only — first 404 remembers id for the whole session
const failedIds = new Set();
@@ -29,13 +26,10 @@ export function resolveProviderIconId(providerId) {
return aliased;
}
/** `/providers/{id}.{png|svg}` or null when previously failed. */
/** `/providers/{id}.png` or null when previously failed. */
export function getProviderIconSrc(providerId) {
const id = resolveProviderIconId(providerId);
if (!id) return null;
// svg for vector logos, png for everything else
const ext = SVG_PROVIDER_IDS.has(id) ? "svg" : "png";
return `/providers/${id}.${ext}`;
return id ? `/providers/${id}.png` : null;
}
/** Call from img onError so later mounts skip the request. */