feat: 全链路追踪系统 — ai-core追踪事件 + ethend管道视图
ai-core: - 新增 TraceEvent 内存环形缓冲区(200条) - GET /api/v1/trace/events 端点(支持session_id过滤) - 预置 trace.go 追踪事件定义 ethend: - trace/recent 聚合 platform-bridge消息日志 + ai-core追踪事件 - 消息按管线分组: msg_received→llm_call→tool_call→msg_sent - 前端管线卡片视图: 每条消息显示完整处理步骤 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6284,15 +6284,51 @@ async function refreshTrace() {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是首次加载(content 只有 loading 提示),全量渲染
|
||||
// Try pipeline view first (new format)
|
||||
var pipelines = data.pipelines || [];
|
||||
if (pipelines.length > 0) {
|
||||
var html = '<div class="trace-timeline">';
|
||||
for (var pi = 0; pi < pipelines.length; pi++) {
|
||||
var pl = pipelines[pi];
|
||||
var steps = pl.steps || [];
|
||||
var hasMsg = steps.some(function(s){ return s.hop === 'msg_received'; });
|
||||
html += '<div class="trace-pipeline" style="margin:12px 0;border:1px solid var(--border);border-radius:8px;overflow:hidden">';
|
||||
// Pipeline header
|
||||
html += '<div style="padding:8px 12px;background:var(--bg2);display:flex;justify-content:space-between;align-items:center">';
|
||||
html += '<span style="font-weight:600;font-size:13px">📨 ' + escHtml(pl.sender || '?') + (pl.platform ? ' · ' + escHtml(pl.platform) : '') + '</span>';
|
||||
html += '<span style="font-size:11px;color:var(--text2)">' + (pl.timestamp || '').substring(11,19) + '</span>';
|
||||
html += '</div>';
|
||||
// Pipeline steps
|
||||
html += '<div style="padding:4px 0">';
|
||||
for (var si = 0; si < steps.length; si++) {
|
||||
var s = steps[si];
|
||||
var hopIcon = {msg_received:'📥',msg_sent:'📤',llm_call:'🧠',tool_call:'🔧',session:'👤'}[s.hop] || '●';
|
||||
var hopColor = s.status === 'error' ? 'var(--red)' : (s.hop === 'msg_received' ? 'var(--blue)' : s.hop === 'msg_sent' ? 'var(--green)' : 'var(--text2)');
|
||||
html += '<div style="display:flex;align-items:flex-start;padding:4px 12px;gap:8px;font-size:12px">';
|
||||
html += '<span style="color:' + hopColor + ';flex-shrink:0;width:20px;text-align:center">' + hopIcon + '</span>';
|
||||
html += '<span style="flex:1"><b>' + escHtml(s.label) + '</b>';
|
||||
if (s.detail) html += '<br><span style="color:var(--text2);font-size:11px">' + escHtml(s.detail) + '</span>';
|
||||
if (s.durationMs > 0) html += ' <span style="color:var(--text3);font-size:10px">(' + (s.durationMs >= 1000 ? (s.durationMs/1000).toFixed(1)+'s' : s.durationMs+'ms') + ')</span>';
|
||||
html += '</span></div>';
|
||||
}
|
||||
html += '</div></div>';
|
||||
}
|
||||
html += '</div>';
|
||||
contentEl.innerHTML = html;
|
||||
// Also update stats
|
||||
updateTraceStatsEl(statsEl, traces, stats, data.session);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: flat timeline
|
||||
var timeline = contentEl.querySelector('.trace-timeline');
|
||||
if (!timeline) {
|
||||
if (traces.length === 0) {
|
||||
if (traces.length === 0 && pipelines.length === 0) {
|
||||
contentEl.innerHTML = '<div class="trace-empty"><div class="icon">📭</div>暂无链路追踪数据<br><span style="font-size:11px;color:var(--text3)">请确保服务正在运行且有消息活动</span></div>';
|
||||
statsEl.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
// 全量渲染
|
||||
// 全量渲染 (old format)
|
||||
var html = '<div class="trace-timeline">';
|
||||
for (var i = 0; i < traces.length; i++) {
|
||||
html += traceHopHtml(traces[i], i > 0 ? traces[i-1] : null);
|
||||
|
||||
Reference in New Issue
Block a user