"use client"; import { Brain, CheckCircle2, ChevronLeft, ChevronRight, Eye, EyeOff, Loader2, MoreHorizontal, Pencil, Plug, Plus, Search, Trash2, XCircle, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { useState } from "react"; type ModelType = "LLM" | "ASR" | "TTS" | "Realtime" | "Embedding"; type InterfaceType = "openai" | "xfyun" | "dashscope" | "gemini"; /** 各资源类型可选的接口类型 */ const interfaceOptionsByType: Record = { LLM: ["openai"], ASR: ["openai", "xfyun", "dashscope"], TTS: ["openai", "xfyun", "dashscope"], Realtime: ["openai", "gemini"], Embedding: ["openai"], }; const modelTypes: ModelType[] = ["LLM", "ASR", "TTS", "Realtime", "Embedding"]; type ModelResource = { id: string; name: string; type: ModelType; interfaceType: InterfaceType; apiUrl: string; apiKey: string; }; const mockModels: ModelResource[] = [ { id: "model_001", name: "DeepSeek-V3", type: "LLM", interfaceType: "openai", apiUrl: "https://api.deepseek.com/v1", apiKey: "sk-deepseek-7f3a9c2e1b", }, { id: "model_002", name: "Qwen-Max", type: "LLM", interfaceType: "openai", apiUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1", apiKey: "sk-qwen-4d8e2a6f0c", }, { id: "model_003", name: "讯飞语音识别", type: "ASR", interfaceType: "xfyun", apiUrl: "https://iat-api.xfyun.cn/v2/iat", apiKey: "xf-asr-9b1c3d5e7a", }, { id: "model_004", name: "Paraformer 识别", type: "ASR", interfaceType: "dashscope", apiUrl: "https://dashscope.aliyuncs.com/api/v1/services/audio/asr", apiKey: "sk-paraformer-2e4f6a", }, { id: "model_005", name: "讯飞语音合成", type: "TTS", interfaceType: "xfyun", apiUrl: "https://tts-api.xfyun.cn/v2/tts", apiKey: "xf-tts-6c8a0b2d4f", }, { id: "model_006", name: "CosyVoice 合成", type: "TTS", interfaceType: "dashscope", apiUrl: "https://dashscope.aliyuncs.com/api/v1/services/audio/tts", apiKey: "sk-cosyvoice-1a3c5e", }, { id: "model_007", name: "OpenAI TTS", type: "TTS", interfaceType: "openai", apiUrl: "https://api.openai.com/v1/audio/speech", apiKey: "sk-openai-tts-8f0a2c", }, { id: "model_008", name: "GPT Realtime", type: "Realtime", interfaceType: "openai", apiUrl: "https://api.openai.com/v1/realtime", apiKey: "sk-realtime-3b5d7f9a1c", }, { id: "model_009", name: "Gemini Live", type: "Realtime", interfaceType: "gemini", apiUrl: "https://generativelanguage.googleapis.com/v1beta", apiKey: "gm-live-5e7a9c1b3d", }, { id: "model_010", name: "text-embedding-3", type: "Embedding", interfaceType: "openai", apiUrl: "https://api.openai.com/v1/embeddings", apiKey: "sk-embed-0c2e4a6f8b", }, { id: "model_011", name: "Kimi-K2", type: "LLM", interfaceType: "openai", apiUrl: "https://api.moonshot.cn/v1", apiKey: "sk-kimi-7a9c1e3b5d", }, { id: "model_012", name: "BGE Embedding", type: "Embedding", interfaceType: "openai", apiUrl: "https://api.siliconflow.cn/v1/embeddings", apiKey: "sk-bge-2d4f6a8c0e", }, ]; type ModelForm = { name: string; type: ModelType; interfaceType: InterfaceType; apiUrl: string; apiKey: string; }; const emptyForm: ModelForm = { name: "", type: "LLM", interfaceType: "openai", apiUrl: "", apiKey: "", }; type TypeFilter = "全部" | ModelType; const typeFilters: TypeFilter[] = ["全部", ...modelTypes]; export function ComponentsModelsPage() { const [searchQuery, setSearchQuery] = useState(""); const [typeFilter, setTypeFilter] = useState("全部"); const [currentPage, setCurrentPage] = useState(1); const [dialogOpen, setDialogOpen] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState(emptyForm); const [showKey, setShowKey] = useState(false); const [testing, setTesting] = useState(false); const [testResult, setTestResult] = useState<"idle" | "ok" | "fail">("idle"); function updateForm(key: K, value: ModelForm[K]) { setForm((prev) => ({ ...prev, [key]: value })); // 任何配置变更后,旧的测试结果不再可信,重置为待测状态 setTestResult("idle"); } async function handleTestConnection() { setTesting(true); setTestResult("idle"); try { // TODO: 接入真实疎通接口(按 form.interfaceType 区分调用方式) await new Promise((resolve) => setTimeout(resolve, 900)); const reachable = Boolean(form.apiUrl.trim() && form.apiKey.trim()); setTestResult(reachable ? "ok" : "fail"); } catch { setTestResult("fail"); } finally { setTesting(false); } } function handleTypeChange(type: ModelType) { const options = interfaceOptionsByType[type]; setForm((prev) => ({ ...prev, type, // 资源类型变化时,若当前接口类型不在新类型的可选项内则重置为首项 interfaceType: options.includes(prev.interfaceType) ? prev.interfaceType : options[0], })); setTestResult("idle"); } function openCreate() { setEditingId(null); setForm(emptyForm); setShowKey(false); setTestResult("idle"); setDialogOpen(true); } function openEdit(model: ModelResource) { setEditingId(model.id); setForm({ name: model.name, type: model.type, interfaceType: model.interfaceType, apiUrl: model.apiUrl, apiKey: model.apiKey, }); setShowKey(false); setTestResult("idle"); setDialogOpen(true); } const filteredModels = mockModels.filter((model) => { if (typeFilter !== "全部" && model.type !== typeFilter) { return false; } const keyword = searchQuery.trim().toLowerCase(); if (!keyword) { return true; } return [model.name, model.id, model.type, model.interfaceType, model.apiUrl] .join(" ") .toLowerCase() .includes(keyword); }); const pageSize = 5; const totalPages = Math.max(1, Math.ceil(filteredModels.length / pageSize)); const safeCurrentPage = Math.min(currentPage, totalPages); const pageStart = (safeCurrentPage - 1) * pageSize; const pageEnd = pageStart + pageSize; const paginatedModels = filteredModels.slice(pageStart, pageEnd); function handleSearchChange(value: string) { setSearchQuery(value); setCurrentPage(1); } function handleFilterChange(filter: TypeFilter) { setTypeFilter(filter); setCurrentPage(1); } const interfaceOptions = interfaceOptionsByType[form.type]; const canSave = form.name.trim() && form.apiUrl.trim(); return (

模型资源

统一管理 LLM、ASR、TTS、Realtime 与 Embedding 模型,配置各自的接口类型与访问凭证。

{typeFilters.map((filter) => ( ))}
handleSearchChange(event.target.value)} className="h-10 border-hairline-strong bg-background pl-9 text-sm text-foreground placeholder:text-muted-soft" placeholder="搜索模型名称、接口类型或 URL..." />
模型名称
资源类型
接口类型
API URL
操作
{paginatedModels.map((model) => (
{model.name}
{model.id}
{model.type}
{model.interfaceType}
{model.apiUrl}
删除
))} {filteredModels.length === 0 && (
未找到匹配的模型
请调整关键词或筛选条件后再试。
)}
{filteredModels.length === 0 ? "没有数据" : `显示 ${pageStart + 1}-${Math.min(pageEnd, filteredModels.length)} / 共 ${filteredModels.length} 个模型`}
{Array.from({ length: totalPages }, (_, index) => index + 1).map( (page) => ( ), )}
{editingId ? "编辑模型" : "添加模型"} 填写模型名称、资源类型、接口类型与访问凭证。
资源类型
接口类型
{testResult === "ok" && ( 连接成功 )} {testResult === "fail" && ( 连接失败 )}
); }