import { Suspense, lazy, useEffect, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { AlertCircle, CheckCircle2, ShieldCheck, Sparkles } from "lucide-react"; import { apiBaseURL, getAuthToken } from "@/lib/api"; import { devSyncApi } from "@/api/devSync"; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { SectionLoadingShell } from "@/components/layout/LazyBoundary"; import { type FeishuEventMode } from "@/lib/feishuEventMode"; import { buildIntegrationSources } from "./constants"; import type { AIConfigSuggestion, ConfigStatus, FeishuConfigForm, GithubConfigForm, KnowledgeDoc, MonitorSettings, PageMessage, WecomConfigForm, } from "./types"; const PromptRoleSettings = lazy(() => import("@/components/ai-steward/PromptRoleSettings")); const GovernanceToolsCard = lazy(() => import("./components/GovernanceToolsCard")); const AdvancedTab = lazy(() => import("./sections/AdvancedTab")); const ConfigureTab = lazy(() => import("./sections/ConfigureTab")); const KnowledgeTab = lazy(() => import("./sections/KnowledgeTab")); const OverviewTab = lazy(() => import("./sections/OverviewTab")); export default function AIConfigPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [, setLoading] = useState(true); const [checking, setChecking] = useState(null); const [status, setStatus] = useState(null); const [suggestion, setSuggestion] = useState(null); const [activeTab, setActiveTab] = useState("overview"); const [scrollToSection, setScrollToSection] = useState(null); // Feishu config form const [feishuConfig, setFeishuConfig] = useState({ app_id: "", app_secret: "", event_mode: "long_connection" as FeishuEventMode }); // WeChat Work config form const [wecomConfig, setWecomConfig] = useState({ corp_id: "", agent_id: "", app_secret: "", token: "", encoding_aes_key: "" }); // GitHub config form const [githubConfig, setGithubConfig] = useState({ install_url: "" }); // Monitor settings const [monitorSettings, setMonitorSettings] = useState({ hn_enabled: false, hn_interval: 30, browser_enabled: false }); const [message, setMessage] = useState(null); const [wecomCopyTip, setWecomCopyTip] = useState(""); const [knowledgeDocs, setKnowledgeDocs] = useState([]); const [knowledgeFile, setKnowledgeFile] = useState(null); const [knowledgeURL, setKnowledgeURL] = useState(""); const [knowledgeURLTitle, setKnowledgeURLTitle] = useState(""); // Guide expansion state const [showFeishuGuide, setShowFeishuGuide] = useState(false); const [showWecomGuide, setShowWecomGuide] = useState(false); const [editingFeishu, setEditingFeishu] = useState(false); const [editingWecom, setEditingWecom] = useState(false); const integrationSources = buildIntegrationSources(status); // ==================== API Calls ==================== const loadStatus = async () => { setLoading(true); try { const token = getAuthToken(); if (!token) { navigate("/login"); return; } // Check Feishu config const feishuRes = await fetch(`${apiBaseURL}/feishu/config`, { headers: { Authorization: `Bearer ${token}` } }); if (feishuRes.ok) { const feishuData = await feishuRes.json(); const mode = "long_connection" as FeishuEventMode; setStatus(prev => ({ ...prev!, feishu: { configured: feishuData.is_configured, app_id: feishuData.app_id, event_mode: mode, }, })); setFeishuConfig({ app_id: feishuData.app_id || "", app_secret: feishuData.app_secret || "", event_mode: mode, }); } // Check WeChat Work config const wecomRes = await fetch(`${apiBaseURL}/wecom/config`, { headers: { Authorization: `Bearer ${token}` } }); if (wecomRes.ok) { const wecomData = await wecomRes.json(); setStatus(prev => ({ ...prev!, wecom: { configured: wecomData.is_configured, corp_id: wecomData.corp_id, agent_id: wecomData.agent_id, token_preview: wecomData.token_preview || "", encoding_aes_key_preview: wecomData.encoding_aes_key_preview || "", }, })); setWecomConfig({ corp_id: wecomData.corp_id || "", agent_id: wecomData.agent_id || "", app_secret: wecomData.app_secret || "", token: wecomData.token || "", encoding_aes_key: wecomData.encoding_aes_key || "", }); } // Get AI suggestions const suggestRes = await fetch(`${apiBaseURL}/ai-steward/config/suggest`, { headers: { Authorization: `Bearer ${token}` } }); if (suggestRes.ok) { const suggestData = await suggestRes.json(); setSuggestion(suggestData); } try { const githubData = await devSyncApi.getGithubInstallations(); setStatus(prev => ({ ...prev!, github: { configured: Boolean(githubData.installations?.length), install_url: githubData.install_url, callback_url: githubData.callback_url, webhook_signature_configured: githubData.webhook_signature_configured, app_slug: githubData.app_slug, installations: githubData.installations.map((item) => ({ id: item.id, installation_id: item.installation_id, account_login: item.account_login, account_type: item.account_type, status: item.status, last_synced_at: item.last_synced_at, })), recent_deliveries: githubData.recent_deliveries, }, })); setGithubConfig({ install_url: githubData.install_url || "", }); } catch (err) { console.warn("Failed to load GitHub App status:", err); } } catch (err) { console.error("Failed to load status:", err); } finally { setLoading(false); } }; const saveFeishuConfig = async () => { setChecking("feishu"); setMessage(null); if (!feishuConfig.app_id || !feishuConfig.app_secret) { setMessage({ type: "error", text: "请填写应用编号和应用密钥" }); setChecking(null); return; } try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/feishu/config`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify(feishuConfig) }); if (response.ok) { setMessage({ type: "success", text: "飞书配置已保存" }); setEditingFeishu(false); await loadStatus(); } else { const error = await response.json(); setMessage({ type: "error", text: error.detail || "保存失败" }); } } catch (err) { setMessage({ type: "error", text: "网络错误,请重试" }); } finally { setChecking(null); } }; const testFeishuConnection = async () => { setChecking("feishu-test"); setMessage(null); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/feishu/test-connection`, { method: "POST", headers: { Authorization: `Bearer ${token}` } }); const data = await response.json(); if (data.status === "success") { setMessage({ type: "success", text: "飞书连接测试成功!" }); } else { setMessage({ type: "error", text: data.message || "连接测试失败" }); } } catch (err) { setMessage({ type: "error", text: "测试失败" }); } finally { setChecking(null); } }; const deleteFeishuConfig = async () => { if (!confirm("确定要删除飞书配置吗?删除后将无法从飞书群聊捕获信号。")) return; setChecking("feishu-delete"); setMessage(null); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/feishu/config`, { method: "DELETE", headers: { Authorization: `Bearer ${token}` } }); if (response.ok) { setMessage({ type: "success", text: "配置已删除" }); setFeishuConfig({ app_id: "", app_secret: "", event_mode: "long_connection" }); setEditingFeishu(false); await loadStatus(); } else { setMessage({ type: "error", text: "删除失败" }); } } catch (err) { setMessage({ type: "error", text: "网络错误" }); } finally { setChecking(null); } }; const saveWecomConfig = async () => { setChecking("wecom"); setMessage(null); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/wecom/config`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify(wecomConfig) }); if (response.ok) { setMessage({ type: "success", text: "企业微信配置已保存" }); setEditingWecom(false); await loadStatus(); } else { const error = await response.json(); setMessage({ type: "error", text: error.detail || "保存失败" }); } } catch (err) { setMessage({ type: "error", text: "网络错误,请重试" }); } finally { setChecking(null); } }; const testWecomConnection = async () => { setChecking("wecom-test"); setMessage(null); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/wecom/test-connection`, { method: "POST", headers: { Authorization: `Bearer ${token}` } }); const data = await response.json(); if (data.status === "success") { setMessage({ type: "success", text: "企业微信连接测试成功!" }); } else { setMessage({ type: "error", text: data.message || "连接测试失败" }); } } catch (err) { setMessage({ type: "error", text: "测试失败" }); } finally { setChecking(null); } }; const deleteWecomConfig = async () => { if (!confirm("确定要删除企业微信配置吗?删除后将无法从企业微信群聊捕获信号。")) return; setChecking("wecom-delete"); setMessage(null); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/wecom/config`, { method: "DELETE", headers: { Authorization: `Bearer ${token}` } }); if (response.ok) { setMessage({ type: "success", text: "配置已删除" }); setWecomConfig({ corp_id: "", agent_id: "", app_secret: "", token: "", encoding_aes_key: "" }); setEditingWecom(false); await loadStatus(); } else { setMessage({ type: "error", text: "删除失败" }); } } catch (err) { setMessage({ type: "error", text: "网络错误" }); } finally { setChecking(null); } }; const getPublicBaseUrl = () => { const normalized = apiBaseURL.replace(/\/api\/?$/, ""); if (/^https?:\/\//i.test(normalized)) { return normalized.replace(/\/$/, ""); } return window.location.origin.replace(/\/$/, ""); }; const getWecomWebhookUrl = () => `${getPublicBaseUrl()}/wecom/webhook`; const startHNMonitor = async () => { setChecking("hn"); setMessage(null); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/ai-steward/signal/start-hn-monitor?interval_minutes=${monitorSettings.hn_interval}`, { method: "POST", headers: { Authorization: `Bearer ${token}` } }); if (response.ok) { setMessage({ type: "success", text: "技术资讯监控已启动" }); setMonitorSettings(prev => ({ ...prev!, hn_enabled: true })); } else { setMessage({ type: "error", text: "启动失败" }); } } catch (err) { setMessage({ type: "error", text: "网络错误" }); } finally { setChecking(null); } }; const copyText = async (text: string) => { try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); return true; } } catch { // fallback below } try { const input = document.createElement("textarea"); input.value = text; input.style.position = "fixed"; input.style.opacity = "0"; input.style.left = "-9999px"; document.body.appendChild(input); input.focus(); input.select(); const ok = document.execCommand("copy"); document.body.removeChild(input); return ok; } catch { return false; } }; const handleCopyWebhook = async (text: string) => { const ok = await copyText(text); if (ok) { setMessage({ type: "success", text: "已复制到剪切板" }); setWecomCopyTip("已复制到剪切板"); setTimeout(() => setWecomCopyTip(""), 1500); return; } setMessage({ type: "error", text: "复制失败,请手动复制地址" }); setWecomCopyTip("复制失败"); setTimeout(() => setWecomCopyTip(""), 1500); }; const loadKnowledgeDocs = async () => { try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/rag/knowledge-base/docs`, { headers: { Authorization: `Bearer ${token}` } }); if (!response.ok) return; const payload = await response.json(); const docs = (payload?.data ?? payload) || []; setKnowledgeDocs(Array.isArray(docs) ? docs : []); } catch (err) { console.error("Failed to load knowledge docs:", err); } }; const uploadKnowledgeFile = async () => { if (!knowledgeFile) { setMessage({ type: "error", text: "请先选择文件" }); return; } setChecking("knowledge-upload"); try { const token = getAuthToken(); const formData = new FormData(); formData.append("file", knowledgeFile); const response = await fetch(`${apiBaseURL}/rag/knowledge-base/upload`, { method: "POST", headers: { Authorization: `Bearer ${token}` }, body: formData, }); const payload = await response.json().catch(() => ({})); if (!response.ok) { setMessage({ type: "error", text: payload?.detail || "上传失败" }); return; } setKnowledgeFile(null); setMessage({ type: "success", text: "文件已提交,正在向量化处理" }); await loadKnowledgeDocs(); } catch (err) { setMessage({ type: "error", text: "上传失败,请检查网络" }); } finally { setChecking(null); } }; const addKnowledgeURL = async () => { if (!knowledgeURL.trim()) { setMessage({ type: "error", text: "请输入 URL" }); return; } setChecking("knowledge-url"); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/rag/knowledge-base/urls`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ url: knowledgeURL.trim(), title: knowledgeURLTitle.trim() || undefined, }), }); const payload = await response.json().catch(() => ({})); if (!response.ok) { setMessage({ type: "error", text: payload?.detail || "URL 添加失败" }); return; } setKnowledgeURL(""); setKnowledgeURLTitle(""); setMessage({ type: "success", text: "URL 已加入知识库,正在抓取与向量化" }); await loadKnowledgeDocs(); } catch (err) { setMessage({ type: "error", text: "URL 添加失败,请稍后重试" }); } finally { setChecking(null); } }; const refreshKnowledgeDoc = async (docId: number) => { setChecking(`knowledge-refresh-${docId}`); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/rag/knowledge-base/docs/${docId}/refresh`, { method: "POST", headers: { Authorization: `Bearer ${token}` }, }); if (!response.ok) { const payload = await response.json().catch(() => ({})); setMessage({ type: "error", text: payload?.detail || "刷新失败" }); return; } setMessage({ type: "success", text: "已触发刷新,正在处理" }); await loadKnowledgeDocs(); } catch (err) { setMessage({ type: "error", text: "刷新失败,请稍后重试" }); } finally { setChecking(null); } }; const deleteKnowledgeDoc = async (docId: number) => { if (!confirm("确认删除该知识库条目及其向量数据?")) return; setChecking(`knowledge-delete-${docId}`); try { const token = getAuthToken(); const response = await fetch(`${apiBaseURL}/rag/knowledge-base/docs/${docId}`, { method: "DELETE", headers: { Authorization: `Bearer ${token}` }, }); if (!response.ok) { const payload = await response.json().catch(() => ({})); setMessage({ type: "error", text: payload?.detail || "删除失败" }); return; } setMessage({ type: "success", text: "已删除知识库条目" }); await loadKnowledgeDocs(); } catch (err) { setMessage({ type: "error", text: "删除失败,请稍后重试" }); } finally { setChecking(null); } }; // ==================== Initial Load ==================== // Scroll to section when tab changes to configure useEffect(() => { if (activeTab === "configure" && scrollToSection) { // Small delay to ensure the tab content is rendered setTimeout(() => { const element = document.getElementById(`config-${scrollToSection}`); if (element) { element.scrollIntoView({ behavior: "smooth", block: "start" }); } setScrollToSection(null); }, 100); } }, [activeTab, scrollToSection]); const handleConfigureClick = (sourceId: string) => { setScrollToSection(sourceId); setActiveTab("configure"); }; useEffect(() => { setStatus({ feishu: { configured: false }, wecom: { configured: false }, github: { configured: false, installations: [], recent_deliveries: [] }, browser: { installed: false }, hn_monitor: { active: false } }); loadStatus(); }, []); useEffect(() => { const requestedTab = searchParams.get("tab"); if (requestedTab && ["overview", "configure", "knowledge", "advanced", "prompts"].includes(requestedTab)) { setActiveTab(requestedTab); } }, [searchParams]); useEffect(() => { if (activeTab !== "knowledge") return; loadKnowledgeDocs(); }, [activeTab]); useEffect(() => { if (activeTab !== "knowledge") return; if (!knowledgeDocs.some((item) => item.status === "processing")) return; const timer = setInterval(() => { loadKnowledgeDocs(); }, 3000); return () => clearInterval(timer); }, [activeTab, knowledgeDocs]); const renderActiveTab = () => { switch (activeTab) { case "overview": return ( }> ); case "configure": return ( }> ); case "knowledge": return ( }> ); case "advanced": return ( }> ); case "prompts": return ( }> ); default: return null; } }; // ==================== Render ==================== return (
{/* Header */}
团队级 AI 运营

AI 运营中心

管理团队 AI 策略、供应商连接、路由诊断和语义治理;这里不是项目级配置页。

策略
速度 / 质量 / 成本
连接
供应商 / 工具
治理
诊断 / 审计

这些配置是团队级配置,会影响当前团队空间内所有项目;项目内只展示 AI 处理模式和可信状态,不展示底层模型参数。

{/* AI Suggestion Card */} {suggestion && (

AI 运营建议

{suggestion.reasoning}

预期影响: {suggestion.estimated_impact}

)} {/* Message */} {message && (
{message.type === "success" ? ( ) : ( )} {message.text}
)} }> {/* Main Content */} 运行总览 连接配置 知识治理 高级设置 Prompt 设置 {renderActiveTab()}
); }