feat(AgentAudit): 添加进度日志功能支持

添加 progress 日志类型,支持通过 progressKey 更新或添加进度日志
在日志组件中增加进度日志的样式和显示
处理进度消息的匹配和更新逻辑,避免重复添加日志
This commit is contained in:
lintsinghua 2025-12-16 18:04:09 +08:00
parent 18a91f25b2
commit 2bba972272
5 changed files with 136 additions and 27 deletions

View File

@ -24,6 +24,7 @@ const LOG_TYPE_LABELS: Record<string, string> = {
info: 'INFO', info: 'INFO',
error: 'ERROR', error: 'ERROR',
user: 'USER', user: 'USER',
progress: 'PROGRESS',
}; };
// Helper to format title (remove emojis and clean up) // Helper to format title (remove emojis and clean up)
@ -73,6 +74,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
const isFinding = item.type === 'finding'; const isFinding = item.type === 'finding';
const isError = item.type === 'error'; const isError = item.type === 'error';
const isInfo = item.type === 'info'; const isInfo = item.type === 'info';
const isProgress = item.type === 'progress';
const showContent = isThinking || isExpanded; const showContent = isThinking || isExpanded;
const isCollapsible = !isThinking && item.content; const isCollapsible = !isThinking && item.content;
@ -124,6 +126,7 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
${isFinding ? 'bg-rose-500/25 text-rose-300' : ''} ${isFinding ? 'bg-rose-500/25 text-rose-300' : ''}
${isError ? 'bg-red-500/25 text-red-300' : ''} ${isError ? 'bg-red-500/25 text-red-300' : ''}
${isInfo ? 'bg-slate-500/25 text-slate-300' : ''} ${isInfo ? 'bg-slate-500/25 text-slate-300' : ''}
${isProgress ? 'bg-cyan-500/25 text-cyan-300' : ''}
${item.type === 'dispatch' ? 'bg-sky-500/25 text-sky-300' : ''} ${item.type === 'dispatch' ? 'bg-sky-500/25 text-sky-300' : ''}
${item.type === 'phase' ? 'bg-teal-500/25 text-teal-300' : ''} ${item.type === 'phase' ? 'bg-teal-500/25 text-teal-300' : ''}
${item.type === 'user' ? 'bg-indigo-500/25 text-indigo-300' : ''} ${item.type === 'user' ? 'bg-indigo-500/25 text-indigo-300' : ''}

View File

@ -78,6 +78,11 @@ export const LOG_TYPE_CONFIG: Record<string, {
borderColor: "border-l-indigo-500", borderColor: "border-l-indigo-500",
bgColor: "bg-indigo-500/10" bgColor: "bg-indigo-500/10"
}, },
progress: {
icon: React.createElement(Loader2, { className: "w-4 h-4 text-cyan-400 animate-spin" }),
borderColor: "border-l-cyan-500",
bgColor: "bg-cyan-500/10"
},
}; };
// ============ Agent Status Configurations ============ // ============ Agent Status Configurations ============

View File

@ -101,6 +101,34 @@ function agentAuditReducer(state: AgentAuditState, action: AgentAuditAction): Ag
return { ...state, logs: updatedLogs }; return { ...state, logs: updatedLogs };
} }
case 'UPDATE_OR_ADD_PROGRESS_LOG': {
const { progressKey, title, agentName } = action.payload;
// 查找是否已存在相同 progressKey 的进度日志
const existingIndex = state.logs.findIndex(
log => log.type === 'progress' && log.progressKey === progressKey
);
if (existingIndex >= 0) {
// 更新现有日志的 title 和 time
const updatedLogs = [...state.logs];
updatedLogs[existingIndex] = {
...updatedLogs[existingIndex],
title,
time: new Date().toLocaleTimeString('en-US', { hour12: false }),
};
return { ...state, logs: updatedLogs };
} else {
// 添加新的进度日志
const newLog = createLogItem({
type: 'progress',
title,
progressKey,
agentName,
});
return { ...state, logs: [...state.logs, newLog] };
}
}
case 'SELECT_AGENT': case 'SELECT_AGENT':
return { return {
...state, ...state,

View File

@ -317,16 +317,36 @@ function AgentAuditPageContent() {
// 进度事件 // 进度事件
case 'progress': case 'progress':
// 进度事件可以选择显示或跳过 // 进度事件使用 UPDATE_OR_ADD_PROGRESS_LOG 来更新而不是添加
if (event.message) { if (event.message) {
dispatch({ const progressPatterns: { pattern: RegExp; key: string }[] = [
type: 'ADD_LOG', { pattern: /索引进度[:]?\s*\d+\/\d+/, key: 'index_progress' },
payload: { { pattern: /克隆进度[:]?\s*\d+%/, key: 'clone_progress' },
type: 'info', { pattern: /下载进度[:]?\s*\d+%/, key: 'download_progress' },
title: event.message, { pattern: /上传进度[:]?\s*\d+%/, key: 'upload_progress' },
agentName, { pattern: /扫描进度[:]?\s*\d+/, key: 'scan_progress' },
} { pattern: /分析进度[:]?\s*\d+/, key: 'analyze_progress' },
}); ];
const matchedProgress = progressPatterns.find(p => p.pattern.test(event.message || ''));
if (matchedProgress) {
dispatch({
type: 'UPDATE_OR_ADD_PROGRESS_LOG',
payload: {
progressKey: matchedProgress.key,
title: event.message,
agentName,
}
});
} else {
dispatch({
type: 'ADD_LOG',
payload: {
type: 'info',
title: event.message,
agentName,
}
});
}
processedCount++; processedCount++;
} }
break; break;
@ -335,17 +355,40 @@ function AgentAuditPageContent() {
case 'info': case 'info':
case 'complete': case 'complete':
case 'error': case 'error':
case 'warning': case 'warning': {
dispatch({ const message = event.message || `${event.event_type}`;
type: 'ADD_LOG', // 检测进度类型消息
payload: { const progressPatterns: { pattern: RegExp; key: string }[] = [
type: event.event_type === 'error' ? 'error' : 'info', { pattern: /索引进度[:]?\s*\d+\/\d+/, key: 'index_progress' },
title: event.message || `${event.event_type}`, { pattern: /克隆进度[:]?\s*\d+%/, key: 'clone_progress' },
agentName, { pattern: /下载进度[:]?\s*\d+%/, key: 'download_progress' },
} { pattern: /上传进度[:]?\s*\d+%/, key: 'upload_progress' },
}); { pattern: /扫描进度[:]?\s*\d+/, key: 'scan_progress' },
{ pattern: /分析进度[:]?\s*\d+/, key: 'analyze_progress' },
];
const matchedProgress = progressPatterns.find(p => p.pattern.test(message));
if (matchedProgress) {
dispatch({
type: 'UPDATE_OR_ADD_PROGRESS_LOG',
payload: {
progressKey: matchedProgress.key,
title: message,
agentName,
}
});
} else {
dispatch({
type: 'ADD_LOG',
payload: {
type: event.event_type === 'error' ? 'error' : 'info',
title: message,
agentName,
}
});
}
processedCount++; processedCount++;
break; break;
}
// 跳过 thinking_token 等高频事件(它们不会被保存到数据库) // 跳过 thinking_token 等高频事件(它们不会被保存到数据库)
case 'thinking_token': case 'thinking_token':
@ -410,14 +453,41 @@ function AgentAuditPageContent() {
// 🔥 处理 info、warning、error 类型事件(克隆进度、索引进度等) // 🔥 处理 info、warning、error 类型事件(克隆进度、索引进度等)
const infoEvents = ['info', 'warning', 'error', 'progress']; const infoEvents = ['info', 'warning', 'error', 'progress'];
if (infoEvents.includes(event.type)) { if (infoEvents.includes(event.type)) {
dispatch({ const message = event.message || event.type;
type: 'ADD_LOG',
payload: { // 🔥 检测进度类型消息,使用更新而不是添加
type: event.type === 'error' ? 'error' : 'info', const progressPatterns: { pattern: RegExp; key: string }[] = [
title: event.message || event.type, { pattern: /索引进度[:]?\s*\d+\/\d+/, key: 'index_progress' },
agentName: getCurrentAgentName() || undefined, { pattern: /克隆进度[:]?\s*\d+%/, key: 'clone_progress' },
} { pattern: /下载进度[:]?\s*\d+%/, key: 'download_progress' },
}); { pattern: /上传进度[:]?\s*\d+%/, key: 'upload_progress' },
{ pattern: /扫描进度[:]?\s*\d+/, key: 'scan_progress' },
{ pattern: /分析进度[:]?\s*\d+/, key: 'analyze_progress' },
];
const matchedProgress = progressPatterns.find(p => p.pattern.test(message));
if (matchedProgress) {
// 使用 UPDATE_OR_ADD_PROGRESS_LOG 来更新进度而不是添加新日志
dispatch({
type: 'UPDATE_OR_ADD_PROGRESS_LOG',
payload: {
progressKey: matchedProgress.key,
title: message,
agentName: getCurrentAgentName() || undefined,
}
});
} else {
// 非进度消息正常添加
dispatch({
type: 'ADD_LOG',
payload: {
type: event.type === 'error' ? 'error' : 'info',
title: message,
agentName: getCurrentAgentName() || undefined,
}
});
}
return; return;
} }
}, },

View File

@ -15,7 +15,8 @@ export type LogType =
| 'info' | 'info'
| 'error' | 'error'
| 'user' | 'user'
| 'dispatch'; | 'dispatch'
| 'progress';
export type ToolStatus = 'running' | 'completed' | 'failed'; export type ToolStatus = 'running' | 'completed' | 'failed';
@ -33,6 +34,7 @@ export interface LogItem {
}; };
severity?: string; severity?: string;
agentName?: string; agentName?: string;
progressKey?: string; // 用于标识进度日志的唯一键,如 "index_progress"
} }
// ============ Connection Types ============ // ============ Connection Types ============
@ -76,6 +78,7 @@ export type AgentAuditAction =
| { type: 'SET_LOGS'; payload: LogItem[] } | { type: 'SET_LOGS'; payload: LogItem[] }
| { type: 'ADD_LOG'; payload: Omit<LogItem, 'id' | 'time'> & { id?: string } } | { type: 'ADD_LOG'; payload: Omit<LogItem, 'id' | 'time'> & { id?: string } }
| { type: 'UPDATE_LOG'; payload: { id: string; updates: Partial<LogItem> } } | { type: 'UPDATE_LOG'; payload: { id: string; updates: Partial<LogItem> } }
| { type: 'UPDATE_OR_ADD_PROGRESS_LOG'; payload: { progressKey: string; title: string; agentName?: string } }
| { type: 'COMPLETE_TOOL_LOG'; payload: { toolName: string; output: string; duration: number } } | { type: 'COMPLETE_TOOL_LOG'; payload: { toolName: string; output: string; duration: number } }
| { type: 'REMOVE_LOG'; payload: string } | { type: 'REMOVE_LOG'; payload: string }
| { type: 'SELECT_AGENT'; payload: string | null } | { type: 'SELECT_AGENT'; payload: string | null }