// dashboard/src/features/agent/AuditLogViewer.tsx import { useState, useEffect } from 'react'; import axios from 'axios'; import { ScrollText, Clock, CheckCircle2, XCircle, AlertTriangle, Loader, ChevronDown, ChevronUp } from 'lucide-react'; import type { AuditLogEntry } from '../../types'; interface AuditLogViewerProps { sessionId: string; onClose?: () => void; } function getToolDisplayName(name: string | null): string { if (!name) return '—'; const map: Record = { read_file: '读取文件', grep_files: '搜索文件', glob_files: '匹配文件', run_bash: 'Shell 命令', file_write: '写入文件', file_edit: '编辑文件', search_papers: '文献检索', get_paper_metadata: '获取元数据', download_paper: '下载文献', parse_paper: '解析文献', get_paper_content: '获取内容', rag_search: 'RAG 检索', query_target: '天体查询', save_note: '保存笔记', todo_write: '任务管理', compress_context: '压缩上下文', load_skill: '加载技能', subagent: '派发子代理', delegate_research: '子代理研究(旧)', ask_user: '用户提问', save_memory: '保存记忆', load_memory: '读取记忆', bg_task_run: '后台任务', bg_task_check: '检查后台', spawn_teammate: '创建队友', send_teammate_message: '队友消息', team_broadcast: '团队广播', check_team_inbox: '收件箱检查', }; return map[name] || name; } export function AuditLogViewer({ sessionId, onClose }: AuditLogViewerProps) { const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [expandedPreview, setExpandedPreview] = useState>({}); useEffect(() => { if (!sessionId) return; setLoading(true); setError(null); axios .get(`/api/chat/sessions/${sessionId}/audit`) .then(res => setEntries(res.data)) .catch(e => { console.error('加载审计日志失败:', e); setError('审计日志加载失败'); }) .finally(() => setLoading(false)); }, [sessionId]); const okCount = entries.filter(e => e.status === 'OK').length; const failCount = entries.filter(e => e.status === 'FAIL').length; const totalElapsed = entries.reduce((sum, e) => sum + e.elapsed_ms, 0); return (
{/* 头部 */}

会话审计日志

{onClose && ( )}
{/* 汇总 */} {entries.length > 0 && (
{okCount}
成功
{failCount}
失败
{(totalElapsed / 1000).toFixed(1)}s
总耗时
)} {/* 加载/错误状态 */} {loading && (
加载审计日志中...
)} {error && (
{error}
)} {/* 日志表格 */} {!loading && entries.length === 0 && !error && (

该会话暂无审计日志记录

)} {entries.length > 0 && (
{entries.map(entry => { const isExpanded = expandedPreview[entry.id] || false; const hasPreview = entry.output_preview && entry.output_preview.length > 0; return ( ); })}
步骤 工具 状态 耗时 输出预览
#{entry.step} {getToolDisplayName(entry.tool_name)} {entry.tool_name && ( {entry.tool_name} )} {entry.status === 'OK' ? ( OK ) : entry.status === 'FAIL' ? ( FAIL ) : ( {entry.status} )} {entry.elapsed_ms >= 1000 ? `${(entry.elapsed_ms / 1000).toFixed(1)}s` : `${entry.elapsed_ms}ms`} {hasPreview ? (
{isExpanded ? (
                                {entry.output_preview}
                              
) : ( )}
) : ( )}
)}
); }