import type { PiUpstream, PiUpstreamTestResult } from "@umt/contracts"; import { LoaderCircle, PlugZap, Send } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { useTestPiUpstream } from "~/api/pi.ts"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { Button } from "~/components/ui/button.tsx"; import { errorMessage } from "~/lib/api.ts"; // 上游测试:默认只验凭据与模型(不产生费用);"发一次最小补全"真实调用一次模型 const ERROR_KIND: Record = { auth: "凭证被拒", oauth: "登录失效", model: "模型不可用", network: "网络错误", config: "配置错误", other: "失败", }; const SOURCE_TEXT: Record = { "stored credential": "已存凭证" }; /** 上游返回的多是英文原文:先给一句怎么办,原文留作排查 */ const ERROR_HINT: Record = { network: "连不上模型服务,检查 Base URL、端口和网络", auth: "凭证被拒,检查 API Key 或重新订阅登录", model: "模型不可用,换一个模型 id", }; export function PiTestPanel({ upstream, model }: { upstream: PiUpstream; model?: string | null }) { const test = useTestPiUpstream(); const [result, setResult] = useState(null); const [running, setRunning] = useState<"auth" | "completion" | null>(null); const run = async (mode: "auth" | "completion") => { setRunning(mode); try { const r = await test.mutateAsync({ id: upstream.id, body: { mode, ...(model ? { model } : {}) } }); setResult(r); if (r.ok) toast.success(mode === "auth" ? "凭证可用" : "补全成功"); else toast.error(r.message ?? "测试失败"); } catch (err) { setResult(null); toast.error(errorMessage(err, "测试失败")); } finally { setRunning(null); } }; return (

「测试」只检查凭证和模型,不产生费用。最小补全会调用一次模型(最多 16 个 token)。

{result ? (
{result.ok ? "通过" : ERROR_KIND[result.errorKind ?? "other"]} {result.latencyMs} ms{result.model ? ` · ${result.model}` : ""} {result.source ? ` · ${SOURCE_TEXT[result.source] ?? result.source}` : ""} {!result.ok && result.message ? ( {ERROR_HINT[result.errorKind ?? ""] ? `${ERROR_HINT[result.errorKind ?? ""]}。原始错误:` : ""} {result.message} ) : null}
) : null}
); }