import type { GlobalSettings } from "@umt/contracts"; import { LoaderCircle } from "lucide-react"; import { toast } from "sonner"; import { useTestWebSearch, useUpdateSettings } from "~/api/settings.ts"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { Button } from "~/components/ui/button.tsx"; import { errorMessage } from "~/lib/api.ts"; import { num, str } from "./resource-section.tsx"; import { type CardSpec, n } from "./settings-card.tsx"; // Agent 分区「网页搜索」:provider / Key(只写,存 secret)/ 地区 / 语言 / 安全搜索 / 每次条数 / 每分钟上限 + 测试 export const WEBSEARCH_CARD: CardSpec = { anchor: "websearch", title: "网页搜索", description: "开启后 Agent 可以用 web.search 搜索站外公开信息,用 web.fetch 读取搜索结果里的网页。结果和正文按不可信数据交给模型,web.fetch 不能读内网地址和结果以外的链接。", fields: [ { name: "agent.webSearch.provider", label: "搜索服务", kind: "select", options: [ { value: "none", label: "关闭" }, { value: "brave", label: "Brave Search" }, ], }, { name: "agent.webSearch.apiKey", label: "API Key", kind: "password", hint: "Brave Search API 的 Subscription Token;留空不修改", visible: (v) => v["agent.webSearch.provider"] !== "none", }, { name: "agent.webSearch.country", label: "搜索地区", kind: "text", placeholder: "ALL", hint: "两位国家码,如 CN、US;ALL 表示不限", visible: (v) => v["agent.webSearch.provider"] !== "none", }, { name: "agent.webSearch.searchLang", label: "结果语言", kind: "text", placeholder: "不限", hint: "如 zh-hans、en;留空不限", visible: (v) => v["agent.webSearch.provider"] !== "none", }, { name: "agent.webSearch.safesearch", label: "安全搜索", kind: "select", options: [ { value: "off", label: "关闭" }, { value: "moderate", label: "适中" }, { value: "strict", label: "严格" }, ], visible: (v) => v["agent.webSearch.provider"] !== "none", }, { name: "agent.webSearch.maxResults", label: "每次条数", kind: "number", hint: "1–10,Agent 可以要更少", visible: (v) => v["agent.webSearch.provider"] !== "none", }, { name: "agent.webSearch.requestsPerMinute", label: "每分钟上限", kind: "number", hint: "三个进程共用;10 分钟内的相同搜索走缓存,不计次数", visible: (v) => v["agent.webSearch.provider"] !== "none", }, ], fromSettings: (s) => { const w = s.agent.webSearch; return { "agent.webSearch.provider": w.provider, "agent.webSearch.apiKey": "", "agent.webSearch.country": w.country, "agent.webSearch.searchLang": w.searchLang ?? "", "agent.webSearch.safesearch": w.safesearch, "agent.webSearch.maxResults": n(w.maxResults), "agent.webSearch.requestsPerMinute": n(w.requestsPerMinute), }; }, toPatch: (v) => { const key = str(v["agent.webSearch.apiKey"]); const safe = str(v["agent.webSearch.safesearch"]); return { agent: { webSearch: { provider: v["agent.webSearch.provider"] === "brave" ? "brave" : "none", country: str(v["agent.webSearch.country"]).toUpperCase() || "ALL", searchLang: str(v["agent.webSearch.searchLang"]).toLowerCase() || null, safesearch: safe === "off" || safe === "strict" ? safe : "moderate", maxResults: num(v["agent.webSearch.maxResults"]), requestsPerMinute: num(v["agent.webSearch.requestsPerMinute"]), ...(key ? { apiKey: key } : {}), }, }, }; }, extra: (s) => , }; function WebSearchKeyStatus({ settings }: { settings: GlobalSettings }) { const update = useUpdateSettings(); const test = useTestWebSearch(); const w = settings.agent.webSearch; if (w.provider === "none") return null; const saved = !!w.apiKeySecretRef; return (
{saved ? "API Key 已保存" : "未保存 API Key"} {saved ? ( <> ) : null} {test.data?.ok ? ( 「{test.data.query}」{test.data.count} 条 · {test.data.durationMs} ms ) : null}
); }