/** * 规划器监控组件 */ import { Clock, TrendingUp, FileText, Zap, Brain, List, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, ArrowLeft, MessageSquare, Search } from 'lucide-react' import { ScrollArea } from '@/components/ui/scroll-area' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { useState, useEffect, useCallback } from 'react' import { getPlannerOverview, getChatLogs, getLogDetail, type PlannerOverview, type PlanLogDetail, type PaginatedChatLogs, type ChatSummary } from '@/lib/planner-api' import { Skeleton } from '@/components/ui/skeleton' import { useChatNameMap, formatTimestamp, formatRelativeTime, useAutoRefresh } from './use-monitor' interface PlannerMonitorProps { autoRefresh: boolean refreshKey: number } export function PlannerMonitor({ autoRefresh, refreshKey }: PlannerMonitorProps) { // 视图状态: 'overview' | 'chat-logs' const [view, setView] = useState<'overview' | 'chat-logs'>('overview') const [selectedChat, setSelectedChat] = useState(null) // 聊天名称映射 const { getChatName } = useChatNameMap() // 总览数据 const [overview, setOverview] = useState(null) const [overviewLoading, setOverviewLoading] = useState(true) // 聊天日志数据 const [chatLogs, setChatLogs] = useState(null) const [chatLogsLoading, setChatLogsLoading] = useState(false) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) const [jumpToPage, setJumpToPage] = useState('') const [searchQuery, setSearchQuery] = useState('') const [searchInput, setSearchInput] = useState('') // 详情弹窗 const [selectedLog, setSelectedLog] = useState(null) const [dialogOpen, setDialogOpen] = useState(false) const [detailLoading, setDetailLoading] = useState(false) // 加载总览数据 const loadOverview = useCallback(async () => { try { setOverviewLoading(true) const data = await getPlannerOverview() setOverview(data) } catch (error) { console.error('加载规划器总览失败:', error) } finally { setOverviewLoading(false) } }, []) // 加载聊天日志 const loadChatLogs = useCallback(async () => { if (!selectedChat) return try { setChatLogsLoading(true) const data = await getChatLogs(selectedChat.chat_id, page, pageSize, searchQuery || undefined) setChatLogs(data) } catch (error) { console.error('加载聊天日志失败:', error) } finally { setChatLogsLoading(false) } }, [selectedChat, page, pageSize, searchQuery]) // 初始加载 useEffect(() => { loadOverview() }, [loadOverview]) // 响应外部刷新 useEffect(() => { if (refreshKey > 0) { if (view === 'overview') { loadOverview() } else { loadChatLogs() } } }, [refreshKey, view, loadOverview, loadChatLogs]) // 加载聊天日志 useEffect(() => { if (view === 'chat-logs' && selectedChat) { loadChatLogs() } }, [view, selectedChat, loadChatLogs]) // 自动刷新 useAutoRefresh( autoRefresh, useCallback(() => { if (view === 'overview') { loadOverview() } else { loadChatLogs() } }, [view, loadOverview, loadChatLogs]) ) const handleChatClick = (chat: ChatSummary) => { setSelectedChat(chat) setPage(1) setSearchQuery('') setSearchInput('') setView('chat-logs') } const handleBackToOverview = () => { setView('overview') setSelectedChat(null) setChatLogs(null) setSearchQuery('') setSearchInput('') } const handleSearch = () => { setSearchQuery(searchInput) setPage(1) } const handleClearSearch = () => { setSearchInput('') setSearchQuery('') setPage(1) } const handleLogClick = async (chatId: string, filename: string) => { try { setDetailLoading(true) setDialogOpen(true) const detail = await getLogDetail(chatId, filename) setSelectedLog(detail) } catch (error) { console.error('加载计划详情失败:', error) } finally { setDetailLoading(false) } } const handlePageSizeChange = (value: string) => { setPageSize(Number(value)) setPage(1) } const handleJumpToPage = () => { const targetPage = parseInt(jumpToPage) const totalPages = chatLogs ? Math.ceil(chatLogs.total / chatLogs.page_size) : 0 if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= totalPages) { setPage(targetPage) setJumpToPage('') } } const totalPages = chatLogs ? Math.ceil(chatLogs.total / chatLogs.page_size) : 0 return ( <>
{view === 'overview' ? ( // ========== 第一级:总览视图 ========== <> {/* 统计卡片 */}
聊天数量 {overviewLoading ? ( ) : (
{overview?.total_chats || 0}
)}
计划总数 {overviewLoading ? ( ) : (
{overview?.total_plans || 0}
)}
{/* 聊天卡片列表 */} 聊天列表 点击查看该聊天的所有计划记录 {overviewLoading ? (
{[...Array(6)].map((_, i) => ( ))}
) : overview?.chats && overview.chats.length > 0 ? (
{overview.chats.map((chat) => (
handleChatClick(chat)} >
{getChatName(chat.chat_id)}
{chat.plan_count}
最后活动: {formatRelativeTime(chat.latest_timestamp)}
))}
) : (
暂无聊天记录
)}
) : ( // ========== 第二级:聊天日志列表 ========== <> {/* 返回按钮和聊天信息 */}
当前聊天: {selectedChat ? getChatName(selectedChat.chat_id) : ''} 共 {chatLogs?.total || 0} 条计划记录
计划执行记录 {selectedChat ? getChatName(selectedChat.chat_id) : ''}
setSearchInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="w-full sm:w-48" /> {searchQuery && ( )}
{searchQuery && (
搜索关键词: "{searchQuery}"
)}
{chatLogsLoading ? (
{[...Array(5)].map((_, i) => ( ))}
) : chatLogs?.data && chatLogs.data.length > 0 ? ( <>
{chatLogs.data.map((plan) => (
handleLogClick(plan.chat_id, plan.filename)} >
{formatTimestamp(plan.timestamp)}
{plan.action_count} 个动作 {plan.total_plan_ms.toFixed(0)}ms
{plan.action_types && plan.action_types.length > 0 && (
{plan.action_types.map((type, idx) => ( {type} ))}
)}

{plan.reasoning_preview || '无推理内容'}

))}
{/* 分页控件 */}
共 {chatLogs.total} 条记录,第 {page} / {totalPages} 页
setJumpToPage(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleJumpToPage()} placeholder="跳转" className="w-20 h-8" />
{page}/{totalPages}
) : (
暂无计划记录
)}
)}
{/* ========== 第三级:计划详情弹窗 ========== */} 计划执行详情 查看麦麦的详细计划推理过程和执行动作
{detailLoading ? (
{[...Array(5)].map((_, i) => ( ))}
) : selectedLog ? ( <> {/* 基本信息 */}

基本信息

聊天
{getChatName(selectedLog.chat_id)}
时间戳
{formatTimestamp(selectedLog.timestamp)}
类型
{selectedLog.type}
动作数量
{selectedLog.actions.length} 个动作
{/* 时间统计 */}

性能统计

提示词构建
{selectedLog.timing.prompt_build_ms?.toFixed(2) || 0}ms
LLM 推理
{selectedLog.timing.llm_duration_ms?.toFixed(2) || 0}ms
总计划时间
{selectedLog.timing.total_plan_ms?.toFixed(2) || 0}ms
{/* 推理内容 */}

推理过程

{selectedLog.reasoning || '无推理内容'}

{/* 执行动作 */}

执行动作 ({selectedLog.actions.length})

{selectedLog.actions.map((action, index) => (
动作 {index + 1} {action.action_type}
{action.reasoning && (
推理依据

{typeof action.reasoning === 'string' ? action.reasoning : JSON.stringify(action.reasoning)}

)} {action.action_message && (
动作消息
{typeof action.action_message === 'string' ? (

{action.action_message}

) : (
                                    {JSON.stringify(action.action_message, null, 2)}
                                  
)}
)} {action.action_data && Object.keys(action.action_data).length > 0 && (
动作数据
                                  {JSON.stringify(action.action_data, null, 2)}
                                
)} {action.action_reasoning && (
动作推理

{typeof action.action_reasoning === 'string' ? action.action_reasoning : JSON.stringify(action.action_reasoning)}

)}
))}
{/* 原始输出 */} {selectedLog.raw_output && (

原始输出

点击展开查看完整原始输出
{selectedLog.raw_output}
)} {/* 提示词 */} {selectedLog.prompt && (

完整提示词

点击展开查看完整提示词
{selectedLog.prompt}
)} ) : (

无数据

)}
) }