ai-agent-inspector 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_inspector/__init__.py +30 -0
- agent_inspector/cli.py +22 -3
- agent_inspector/ui/static/app.css +1202 -629
- agent_inspector/ui/templates/index.html +452 -144
- {ai_agent_inspector-1.0.0.dist-info → ai_agent_inspector-1.1.0.dist-info}/METADATA +122 -26
- ai_agent_inspector-1.1.0.dist-info/RECORD +11 -0
- ai_agent_inspector-1.0.0.dist-info/RECORD +0 -11
- {ai_agent_inspector-1.0.0.dist-info → ai_agent_inspector-1.1.0.dist-info}/WHEEL +0 -0
- {ai_agent_inspector-1.0.0.dist-info → ai_agent_inspector-1.1.0.dist-info}/entry_points.txt +0 -0
- {ai_agent_inspector-1.0.0.dist-info → ai_agent_inspector-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {ai_agent_inspector-1.0.0.dist-info → ai_agent_inspector-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -7,27 +7,42 @@
|
|
|
7
7
|
<link rel="stylesheet" href="/ui/static/app.css">
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
|
-
<
|
|
10
|
+
<header class="app-header">
|
|
11
|
+
<h1>Agent Inspector</h1>
|
|
12
|
+
<button class="theme-toggle" id="themeToggle" type="button">
|
|
13
|
+
<span>Theme</span>
|
|
14
|
+
<span id="themeLabel">Auto</span>
|
|
15
|
+
</button>
|
|
16
|
+
</header>
|
|
17
|
+
|
|
11
18
|
<div class="container">
|
|
12
19
|
<!-- Left Panel: Run List -->
|
|
13
20
|
<div class="panel">
|
|
14
21
|
<div class="panel-header">Runs</div>
|
|
15
|
-
<div class="filters">
|
|
16
|
-
<input type="text" class="search-input" id="searchInput" placeholder="Search runs...">
|
|
17
|
-
<select class="filter-select" id="statusFilter">
|
|
18
|
-
<option value="">All Status</option>
|
|
19
|
-
<option value="completed">Completed</option>
|
|
20
|
-
<option value="running">Running</option>
|
|
21
|
-
<option value="failed">Failed</option>
|
|
22
|
-
</select>
|
|
23
|
-
<select class="filter-select" id="eventTypeFilter">
|
|
24
|
-
<option value="">All Events</option>
|
|
25
|
-
<option value="llm_call">LLM Calls</option>
|
|
26
|
-
<option value="tool_call">Tool Calls</option>
|
|
27
|
-
<option value="error">Errors</option>
|
|
28
|
-
</select>
|
|
29
|
-
</div>
|
|
30
22
|
<div class="panel-content">
|
|
23
|
+
<div class="filters">
|
|
24
|
+
<input type="text" class="search-input" id="searchInput" placeholder="Search runs...">
|
|
25
|
+
<div class="filter-row">
|
|
26
|
+
<select class="filter-select" id="statusFilter">
|
|
27
|
+
<option value="">All Status</option>
|
|
28
|
+
<option value="completed">Completed</option>
|
|
29
|
+
<option value="running">Running</option>
|
|
30
|
+
<option value="failed">Failed</option>
|
|
31
|
+
</select>
|
|
32
|
+
<select class="filter-select" id="eventTypeFilter">
|
|
33
|
+
<option value="">All Events</option>
|
|
34
|
+
<option value="llm_call">LLM Calls</option>
|
|
35
|
+
<option value="tool_call">Tool Calls</option>
|
|
36
|
+
<option value="memory_read">Memory Access</option>
|
|
37
|
+
<option value="error">Errors</option>
|
|
38
|
+
<option value="agent_spawn">Agent Spawn</option>
|
|
39
|
+
<option value="agent_join">Agent Join</option>
|
|
40
|
+
<option value="agent_communication">Agent Communication</option>
|
|
41
|
+
<option value="agent_handoff">Agent Handoff</option>
|
|
42
|
+
<option value="task_assignment">Task Assignment</option>
|
|
43
|
+
</select>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
31
46
|
<ul class="run-list" id="runList">
|
|
32
47
|
<li class="loading">Loading runs...</li>
|
|
33
48
|
</ul>
|
|
@@ -35,9 +50,9 @@
|
|
|
35
50
|
</div>
|
|
36
51
|
|
|
37
52
|
<!-- Center Panel: Timeline -->
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
<div class="panel">
|
|
54
|
+
<div class="panel-header">Timeline</div>
|
|
55
|
+
<div class="panel-content">
|
|
41
56
|
<div class="timeline" id="timeline">
|
|
42
57
|
<div class="loading">Select a run to view timeline</div>
|
|
43
58
|
</div>
|
|
@@ -72,6 +87,7 @@
|
|
|
72
87
|
const statusFilter = document.getElementById('statusFilter');
|
|
73
88
|
const eventTypeFilter = document.getElementById('eventTypeFilter');
|
|
74
89
|
const themeToggle = document.getElementById('themeToggle');
|
|
90
|
+
const themeLabel = document.getElementById('themeLabel');
|
|
75
91
|
|
|
76
92
|
// Load runs on page load
|
|
77
93
|
window.addEventListener('load', () => {
|
|
@@ -108,21 +124,39 @@
|
|
|
108
124
|
|
|
109
125
|
function renderRuns(runsToRender) {
|
|
110
126
|
if (runsToRender.length === 0) {
|
|
111
|
-
runList.innerHTML = '<li class="loading" style="padding: 40px;">No runs found</li>';
|
|
127
|
+
runList.innerHTML = '<li class="loading" style="padding: 40px; background: none;">No runs found</li>';
|
|
112
128
|
return;
|
|
113
129
|
}
|
|
114
130
|
|
|
115
|
-
runList.innerHTML = runsToRender.map(run =>
|
|
116
|
-
|
|
131
|
+
runList.innerHTML = runsToRender.map(run => {
|
|
132
|
+
const statusIcon = getStatusIcon(run.status);
|
|
133
|
+
return `
|
|
134
|
+
<li class="run-card ${run.status} ${currentRunId === run.id ? 'active' : ''}"
|
|
117
135
|
data-run-id="${run.id}"
|
|
118
136
|
onclick="selectRun('${run.id}')">
|
|
119
|
-
<div class="run-
|
|
137
|
+
<div class="run-header">
|
|
138
|
+
<div class="run-id">${escapeHtml(run.name || run.id || 'Unnamed Run')}</div>
|
|
139
|
+
<span class="run-status-badge ${run.status}">
|
|
140
|
+
${statusIcon} ${run.status}
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="run-description">${escapeHtml(run.description || `Run ${run.id} with ${run.event_count || 0} events`)}</div>
|
|
120
144
|
<div class="run-meta">
|
|
121
|
-
|
|
122
|
-
<span
|
|
145
|
+
<span class="run-time">${formatTimeOnly(run.started_at)}</span>
|
|
146
|
+
<span>${formatDateOnly(run.started_at)}</span>
|
|
123
147
|
</div>
|
|
124
148
|
</li>
|
|
125
|
-
`).join('');
|
|
149
|
+
`}).join('');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getStatusIcon(status) {
|
|
153
|
+
const icons = {
|
|
154
|
+
'completed': '✓',
|
|
155
|
+
'running': '⟳',
|
|
156
|
+
'failed': '✕',
|
|
157
|
+
'pending': '○'
|
|
158
|
+
};
|
|
159
|
+
return icons[status] || '●';
|
|
126
160
|
}
|
|
127
161
|
|
|
128
162
|
function filterRuns() {
|
|
@@ -131,7 +165,9 @@
|
|
|
131
165
|
|
|
132
166
|
const filtered = runs.filter(run => {
|
|
133
167
|
const matchesSearch = !searchTerm ||
|
|
134
|
-
(run.name && run.name.toLowerCase().includes(searchTerm))
|
|
168
|
+
(run.name && run.name.toLowerCase().includes(searchTerm)) ||
|
|
169
|
+
(run.id && run.id.toLowerCase().includes(searchTerm)) ||
|
|
170
|
+
(run.description && run.description.toLowerCase().includes(searchTerm));
|
|
135
171
|
const matchesStatus = !status || run.status === status;
|
|
136
172
|
return matchesSearch && matchesStatus;
|
|
137
173
|
});
|
|
@@ -143,7 +179,7 @@
|
|
|
143
179
|
currentRunId = runId;
|
|
144
180
|
|
|
145
181
|
// Update UI
|
|
146
|
-
document.querySelectorAll('.run-
|
|
182
|
+
document.querySelectorAll('.run-card').forEach(item => {
|
|
147
183
|
item.classList.remove('active');
|
|
148
184
|
if (item.dataset.runId === runId) {
|
|
149
185
|
item.classList.add('active');
|
|
@@ -151,7 +187,7 @@
|
|
|
151
187
|
});
|
|
152
188
|
|
|
153
189
|
// Clear detail view
|
|
154
|
-
detailView.innerHTML = '<div class="detail-empty">
|
|
190
|
+
detailView.innerHTML = '<div class="detail-empty">Select an event to view details</div>';
|
|
155
191
|
|
|
156
192
|
// Load timeline
|
|
157
193
|
await loadTimeline(runId);
|
|
@@ -161,7 +197,7 @@
|
|
|
161
197
|
try {
|
|
162
198
|
timeline.innerHTML = '<div class="loading">Loading timeline...</div>';
|
|
163
199
|
|
|
164
|
-
const response = await fetch(`${API_BASE}/runs/${runId}/timeline`);
|
|
200
|
+
const response = await fetch(`${API_BASE}/runs/${runId}/timeline?include_data=true`);
|
|
165
201
|
const data = await response.json();
|
|
166
202
|
const events = data.events || [];
|
|
167
203
|
|
|
@@ -174,7 +210,7 @@
|
|
|
174
210
|
|
|
175
211
|
function renderTimeline(events) {
|
|
176
212
|
if (events.length === 0) {
|
|
177
|
-
timeline.innerHTML = '<div class="loading">No events in this run</div>';
|
|
213
|
+
timeline.innerHTML = '<div class="loading" style="background: none;">No events in this run</div>';
|
|
178
214
|
return;
|
|
179
215
|
}
|
|
180
216
|
|
|
@@ -184,32 +220,111 @@
|
|
|
184
220
|
: events;
|
|
185
221
|
|
|
186
222
|
if (filteredEvents.length === 0) {
|
|
187
|
-
timeline.innerHTML = '<div class="loading">No events match filter</div>';
|
|
223
|
+
timeline.innerHTML = '<div class="loading" style="background: none;">No events match filter</div>';
|
|
188
224
|
return;
|
|
189
225
|
}
|
|
190
226
|
|
|
227
|
+
const eventClasses = {
|
|
228
|
+
'llm_call': 'llm',
|
|
229
|
+
'tool_call': 'tool',
|
|
230
|
+
'memory_read': 'memory',
|
|
231
|
+
'memory_write': 'memory',
|
|
232
|
+
'error': 'error',
|
|
233
|
+
'final_answer': 'final',
|
|
234
|
+
'run_start': 'llm',
|
|
235
|
+
// Multi-agent classes
|
|
236
|
+
'agent_spawn': 'agent',
|
|
237
|
+
'agent_join': 'agent',
|
|
238
|
+
'agent_leave': 'agent',
|
|
239
|
+
'agent_communication': 'communication',
|
|
240
|
+
'agent_handoff': 'handoff',
|
|
241
|
+
'task_assignment': 'task',
|
|
242
|
+
'task_completion': 'task'
|
|
243
|
+
};
|
|
244
|
+
|
|
191
245
|
timeline.innerHTML = `
|
|
192
|
-
<div class="
|
|
193
|
-
${filteredEvents.map(event =>
|
|
194
|
-
|
|
246
|
+
<div class="timeline-connector"></div>
|
|
247
|
+
${filteredEvents.map(event => {
|
|
248
|
+
const eventClass = eventClasses[event.type] || 'llm';
|
|
249
|
+
const icon = getEventIcon(event.type);
|
|
250
|
+
const badge = getEventTypeBadge(event.type);
|
|
251
|
+
const time = formatTimeOnly(event.timestamp);
|
|
252
|
+
return `
|
|
253
|
+
<div class="timeline-event ${eventClass}"
|
|
195
254
|
onclick="showEventDetail('${event.id}')"
|
|
196
255
|
data-event-id="${event.id}">
|
|
197
|
-
<div class="event-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
256
|
+
<div class="timeline-event-time">${time}</div>
|
|
257
|
+
<div class="timeline-event-card">
|
|
258
|
+
<div class="event-icon-wrapper">${icon}</div>
|
|
259
|
+
<div class="event-details">
|
|
260
|
+
<div class="event-type-label">
|
|
261
|
+
${formatEventType(event.type)}
|
|
262
|
+
<span class="event-type-badge">${badge}</span>
|
|
263
|
+
</div>
|
|
264
|
+
<div class="event-summary">${getEventSummary(event)}</div>
|
|
265
|
+
${getEventPreview(event)}
|
|
266
|
+
</div>
|
|
204
267
|
</div>
|
|
205
268
|
</div>
|
|
206
|
-
`).join('')}
|
|
269
|
+
`}).join('')}
|
|
207
270
|
`;
|
|
208
271
|
}
|
|
209
272
|
|
|
273
|
+
function getEventTypeBadge(type) {
|
|
274
|
+
const badges = {
|
|
275
|
+
'llm_call': 'LLM',
|
|
276
|
+
'tool_call': 'TOOL',
|
|
277
|
+
'memory_read': 'MEMORY',
|
|
278
|
+
'memory_write': 'MEMORY',
|
|
279
|
+
'error': 'ERROR',
|
|
280
|
+
'final_answer': 'FINAL',
|
|
281
|
+
// Multi-agent badges
|
|
282
|
+
'agent_spawn': 'AGENT',
|
|
283
|
+
'agent_join': 'JOIN',
|
|
284
|
+
'agent_leave': 'LEAVE',
|
|
285
|
+
'agent_communication': 'MSG',
|
|
286
|
+
'agent_handoff': 'HANDOFF',
|
|
287
|
+
'task_assignment': 'TASK',
|
|
288
|
+
'task_completion': 'DONE'
|
|
289
|
+
};
|
|
290
|
+
return badges[type] || type.toUpperCase();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function getEventPreview(event) {
|
|
294
|
+
const summary = getEventSummary(event);
|
|
295
|
+
let preview = '';
|
|
296
|
+
if (event.type === 'llm_call' && event.prompt) {
|
|
297
|
+
preview = event.prompt.substring(0, 60) + (event.prompt.length > 60 ? '...' : '');
|
|
298
|
+
} else if (event.type === 'tool_call' && event.tool_name) {
|
|
299
|
+
preview = `${event.tool_name}(${JSON.stringify(event.tool_args || {}).substring(0, 50)})`;
|
|
300
|
+
} else if ((event.type === 'memory_read' || event.type === 'memory_write') && event.memory_key) {
|
|
301
|
+
preview = `Key: ${event.memory_key}`;
|
|
302
|
+
} else if (event.type === 'agent_communication' && event.message_content) {
|
|
303
|
+
preview = event.message_content.substring(0, 80) + (event.message_content.length > 80 ? '...' : '');
|
|
304
|
+
} else if (event.type === 'agent_handoff' && event.handoff_reason) {
|
|
305
|
+
preview = `Reason: ${event.handoff_reason}`;
|
|
306
|
+
} else if (event.type === 'task_assignment' || event.type === 'task_completion') {
|
|
307
|
+
const taskName = event.task_name || event.data?.task_name;
|
|
308
|
+
if (taskName && taskName.length > 45) {
|
|
309
|
+
preview = taskName.length > 120 ? taskName.substring(0, 120) + '…' : taskName;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!preview || preview === summary) return '';
|
|
313
|
+
return `<div class="event-data-preview">${escapeHtml(preview)}</div>`;
|
|
314
|
+
}
|
|
315
|
+
|
|
210
316
|
async function showEventDetail(eventId) {
|
|
211
317
|
currentEventId = eventId;
|
|
212
318
|
|
|
319
|
+
// Highlight selected event
|
|
320
|
+
document.querySelectorAll('.timeline-event').forEach(el => {
|
|
321
|
+
el.style.opacity = '0.6';
|
|
322
|
+
});
|
|
323
|
+
const selectedEvent = document.querySelector(`[data-event-id="${eventId}"]`);
|
|
324
|
+
if (selectedEvent) {
|
|
325
|
+
selectedEvent.style.opacity = '1';
|
|
326
|
+
}
|
|
327
|
+
|
|
213
328
|
try {
|
|
214
329
|
const response = await fetch(`${API_BASE}/runs/${currentRunId}/steps/${eventId}/data`);
|
|
215
330
|
const data = await response.json();
|
|
@@ -227,122 +342,243 @@
|
|
|
227
342
|
return;
|
|
228
343
|
}
|
|
229
344
|
|
|
230
|
-
|
|
231
|
-
const richBlocks = [];
|
|
232
|
-
|
|
233
|
-
// Basic info
|
|
234
|
-
sections.push({
|
|
235
|
-
label: 'Event ID',
|
|
236
|
-
value: event.event_id || 'N/A'
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
sections.push({
|
|
240
|
-
label: 'Type',
|
|
241
|
-
value: formatEventType(event.type)
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
sections.push({
|
|
245
|
-
label: 'Timestamp',
|
|
246
|
-
value: formatTimestamp(event.timestamp_ms)
|
|
247
|
-
});
|
|
345
|
+
let html = '';
|
|
248
346
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
347
|
+
// Basic Info Section
|
|
348
|
+
html += `
|
|
349
|
+
<div class="detail-section">
|
|
350
|
+
<div class="detail-section-header">
|
|
351
|
+
<span class="detail-section-title">Event Information</span>
|
|
352
|
+
</div>
|
|
353
|
+
<div class="detail-grid">
|
|
354
|
+
<div class="detail-label">Event ID</div>
|
|
355
|
+
<div class="detail-value">${event.event_id || 'N/A'}</div>
|
|
356
|
+
|
|
357
|
+
<div class="detail-label">Type</div>
|
|
358
|
+
<div class="detail-value">${formatEventType(event.type)}</div>
|
|
359
|
+
|
|
360
|
+
<div class="detail-label">Timestamp</div>
|
|
361
|
+
<div class="detail-value">${formatTimestamp(event.timestamp_ms || event.timestamp)}</div>
|
|
362
|
+
|
|
363
|
+
<div class="detail-label">Status</div>
|
|
364
|
+
<div class="detail-value">${event.status || 'N/A'}</div>
|
|
365
|
+
|
|
366
|
+
${event.duration_ms ? `
|
|
367
|
+
<div class="detail-label">Duration</div>
|
|
368
|
+
<div class="detail-value">${event.duration_ms}ms</div>
|
|
369
|
+
` : ''}
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
`;
|
|
260
373
|
|
|
261
|
-
// Type-specific
|
|
374
|
+
// Type-specific sections
|
|
262
375
|
if (event.type === 'llm_call') {
|
|
263
376
|
if (event.model) {
|
|
264
|
-
|
|
377
|
+
html += createDetailSection('Model', event.model);
|
|
265
378
|
}
|
|
266
379
|
if (event.prompt) {
|
|
267
380
|
const parsed = parseMaybeJson(event.prompt);
|
|
268
381
|
if (parsed && Array.isArray(parsed)) {
|
|
269
|
-
|
|
270
|
-
label: 'Prompt',
|
|
271
|
-
html: renderChatMessages(parsed)
|
|
272
|
-
});
|
|
382
|
+
html += createChatSection('Prompt', parsed);
|
|
273
383
|
} else {
|
|
274
|
-
|
|
384
|
+
html += createCodeSection('Prompt', event.prompt);
|
|
275
385
|
}
|
|
276
386
|
}
|
|
277
387
|
if (event.response) {
|
|
278
|
-
|
|
388
|
+
html += createCodeSection('Response', event.response);
|
|
279
389
|
}
|
|
280
|
-
if (event.total_tokens) {
|
|
281
|
-
|
|
390
|
+
if (event.total_tokens !== undefined) {
|
|
391
|
+
html += createDetailSection('Tokens', `
|
|
392
|
+
<div style="display: flex; gap: 16px; font-size: 12px;">
|
|
393
|
+
<span>Input: <strong>${event.input_tokens || 0}</strong></span>
|
|
394
|
+
<span>Output: <strong>${event.output_tokens || 0}</strong></span>
|
|
395
|
+
<span>Total: <strong>${event.total_tokens}</strong></span>
|
|
396
|
+
</div>
|
|
397
|
+
`, true);
|
|
282
398
|
}
|
|
283
399
|
} else if (event.type === 'tool_call') {
|
|
284
400
|
if (event.tool_name) {
|
|
285
|
-
|
|
401
|
+
html += createDetailSection('Tool Name', event.tool_name);
|
|
286
402
|
}
|
|
287
403
|
if (event.tool_args) {
|
|
288
|
-
|
|
289
|
-
label: 'Arguments',
|
|
290
|
-
value: JSON.stringify(event.tool_args, null, 2)
|
|
291
|
-
});
|
|
404
|
+
html += createCodeSection('Arguments', syntaxHighlightJson(event.tool_args));
|
|
292
405
|
}
|
|
293
|
-
if (event.tool_result) {
|
|
294
|
-
|
|
295
|
-
label: 'Result',
|
|
296
|
-
value: JSON.stringify(event.tool_result, null, 2)
|
|
297
|
-
});
|
|
406
|
+
if (event.tool_result !== undefined) {
|
|
407
|
+
html += createCodeSection('Result', syntaxHighlightJson(event.tool_result));
|
|
298
408
|
}
|
|
299
409
|
} else if (event.type === 'memory_read' || event.type === 'memory_write') {
|
|
300
410
|
if (event.memory_key) {
|
|
301
|
-
|
|
411
|
+
html += createDetailSection('Memory Key', event.memory_key);
|
|
302
412
|
}
|
|
303
|
-
if (event.memory_value) {
|
|
304
|
-
|
|
305
|
-
label: 'Value',
|
|
306
|
-
value: JSON.stringify(event.memory_value, null, 2)
|
|
307
|
-
});
|
|
413
|
+
if (event.memory_value !== undefined) {
|
|
414
|
+
html += createCodeSection('Value', syntaxHighlightJson(event.memory_value));
|
|
308
415
|
}
|
|
309
416
|
} else if (event.type === 'error') {
|
|
310
417
|
if (event.error_type) {
|
|
311
|
-
|
|
418
|
+
html += createDetailSection('Error Type', event.error_type);
|
|
312
419
|
}
|
|
313
420
|
if (event.error_message) {
|
|
314
|
-
|
|
421
|
+
html += createCodeSection('Message', event.error_message);
|
|
422
|
+
}
|
|
423
|
+
if (event.stack_trace) {
|
|
424
|
+
html += createCodeSection('Stack Trace', event.stack_trace);
|
|
315
425
|
}
|
|
316
426
|
} else if (event.type === 'final_answer') {
|
|
317
427
|
if (event.answer) {
|
|
318
|
-
|
|
428
|
+
html += createCodeSection('Answer', event.answer);
|
|
429
|
+
}
|
|
430
|
+
} else if (event.type === 'agent_spawn') {
|
|
431
|
+
if (event.agent_name) {
|
|
432
|
+
html += createDetailSection('Agent Name', event.agent_name);
|
|
433
|
+
}
|
|
434
|
+
if (event.agent_id) {
|
|
435
|
+
html += createDetailSection('Agent ID', event.agent_id);
|
|
436
|
+
}
|
|
437
|
+
if (event.agent_role) {
|
|
438
|
+
html += createDetailSection('Role', event.agent_role);
|
|
439
|
+
}
|
|
440
|
+
if (event.parent_run_id) {
|
|
441
|
+
html += createDetailSection('Parent Run', event.parent_run_id);
|
|
442
|
+
}
|
|
443
|
+
if (event.agent_config && Object.keys(event.agent_config).length > 0) {
|
|
444
|
+
html += createCodeSection('Configuration', syntaxHighlightJson(event.agent_config));
|
|
445
|
+
}
|
|
446
|
+
} else if (event.type === 'agent_join') {
|
|
447
|
+
if (event.agent_name) {
|
|
448
|
+
html += createDetailSection('Agent Name', event.agent_name);
|
|
449
|
+
}
|
|
450
|
+
if (event.group_name) {
|
|
451
|
+
html += createDetailSection('Group', event.group_name);
|
|
452
|
+
}
|
|
453
|
+
if (event.group_id) {
|
|
454
|
+
html += createDetailSection('Group ID', event.group_id);
|
|
455
|
+
}
|
|
456
|
+
} else if (event.type === 'agent_leave') {
|
|
457
|
+
if (event.agent_name) {
|
|
458
|
+
html += createDetailSection('Agent Name', event.agent_name);
|
|
459
|
+
}
|
|
460
|
+
if (event.reason) {
|
|
461
|
+
html += createDetailSection('Reason', event.reason);
|
|
462
|
+
}
|
|
463
|
+
} else if (event.type === 'agent_communication') {
|
|
464
|
+
if (event.from_agent_name) {
|
|
465
|
+
html += createDetailSection('From', event.from_agent_name);
|
|
466
|
+
}
|
|
467
|
+
if (event.to_agent_name) {
|
|
468
|
+
html += createDetailSection('To', event.to_agent_name);
|
|
469
|
+
}
|
|
470
|
+
if (event.message_type) {
|
|
471
|
+
html += createDetailSection('Type', event.message_type);
|
|
472
|
+
}
|
|
473
|
+
if (event.message_content) {
|
|
474
|
+
html += createCodeSection('Message', event.message_content);
|
|
475
|
+
}
|
|
476
|
+
if (event.group_id) {
|
|
477
|
+
html += createDetailSection('Group ID', event.group_id);
|
|
478
|
+
}
|
|
479
|
+
} else if (event.type === 'agent_handoff') {
|
|
480
|
+
if (event.from_agent_name) {
|
|
481
|
+
html += createDetailSection('From Agent', event.from_agent_name);
|
|
482
|
+
}
|
|
483
|
+
if (event.to_agent_name) {
|
|
484
|
+
html += createDetailSection('To Agent', event.to_agent_name);
|
|
485
|
+
}
|
|
486
|
+
if (event.handoff_reason) {
|
|
487
|
+
html += createDetailSection('Reason', event.handoff_reason);
|
|
488
|
+
}
|
|
489
|
+
if (event.context_summary) {
|
|
490
|
+
html += createDetailSection('Context', event.context_summary);
|
|
491
|
+
}
|
|
492
|
+
} else if (event.type === 'task_assignment') {
|
|
493
|
+
if (event.task_name) {
|
|
494
|
+
html += createDetailSection('Task', event.task_name);
|
|
495
|
+
}
|
|
496
|
+
if (event.assigned_to_agent_name) {
|
|
497
|
+
html += createDetailSection('Assigned To', event.assigned_to_agent_name);
|
|
498
|
+
}
|
|
499
|
+
if (event.priority) {
|
|
500
|
+
html += createDetailSection('Priority', event.priority);
|
|
501
|
+
}
|
|
502
|
+
if (event.task_data && Object.keys(event.task_data).length > 0) {
|
|
503
|
+
html += createCodeSection('Task Data', syntaxHighlightJson(event.task_data));
|
|
504
|
+
}
|
|
505
|
+
} else if (event.type === 'task_completion') {
|
|
506
|
+
if (event.task_name) {
|
|
507
|
+
html += createDetailSection('Task', event.task_name);
|
|
508
|
+
}
|
|
509
|
+
if (event.completed_by_agent_name) {
|
|
510
|
+
html += createDetailSection('Completed By', event.completed_by_agent_name);
|
|
511
|
+
}
|
|
512
|
+
if (event.success !== undefined) {
|
|
513
|
+
html += createDetailSection('Success', event.success ? 'Yes' : 'No');
|
|
514
|
+
}
|
|
515
|
+
if (event.result !== undefined) {
|
|
516
|
+
html += createCodeSection('Result', syntaxHighlightJson(event.result));
|
|
517
|
+
}
|
|
518
|
+
if (event.completion_time_ms) {
|
|
519
|
+
html += createDetailSection('Time', `${event.completion_time_ms}ms`);
|
|
319
520
|
}
|
|
320
521
|
}
|
|
321
522
|
|
|
322
|
-
// Metadata
|
|
523
|
+
// Metadata section
|
|
323
524
|
if (event.metadata && Object.keys(event.metadata).length > 0) {
|
|
324
|
-
|
|
325
|
-
label: 'Metadata',
|
|
326
|
-
value: JSON.stringify(event.metadata, null, 2)
|
|
327
|
-
});
|
|
525
|
+
html += createCodeSection('Metadata', syntaxHighlightJson(event.metadata));
|
|
328
526
|
}
|
|
329
527
|
|
|
330
|
-
|
|
331
|
-
|
|
528
|
+
detailView.innerHTML = html;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function createDetailSection(label, value, isHtml = false) {
|
|
532
|
+
return `
|
|
332
533
|
<div class="detail-section">
|
|
333
|
-
<div class="detail-
|
|
334
|
-
|
|
534
|
+
<div class="detail-section-header">
|
|
535
|
+
<span class="detail-section-title">${label}</span>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="detail-value ${isHtml ? '' : ''}">${isHtml ? value : escapeHtml(value)}</div>
|
|
335
538
|
</div>
|
|
336
|
-
|
|
539
|
+
`;
|
|
540
|
+
}
|
|
337
541
|
|
|
338
|
-
|
|
542
|
+
function createCodeSection(label, content) {
|
|
543
|
+
return `
|
|
339
544
|
<div class="detail-section">
|
|
340
|
-
<div class="detail-
|
|
341
|
-
|
|
545
|
+
<div class="detail-section-header">
|
|
546
|
+
<span class="detail-section-title">${label}</span>
|
|
547
|
+
</div>
|
|
548
|
+
<div class="detail-value code">${content}</div>
|
|
549
|
+
</div>
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function createChatSection(label, messages) {
|
|
554
|
+
const messagesHtml = messages.map(msg => `
|
|
555
|
+
<div class="chat-message ${escapeHtml(msg.role || 'unknown')}">
|
|
556
|
+
<div class="chat-role">${escapeHtml(msg.role || 'unknown')}</div>
|
|
557
|
+
<div class="chat-content">${escapeHtml(msg.content || '')}</div>
|
|
342
558
|
</div>
|
|
343
559
|
`).join('');
|
|
344
560
|
|
|
345
|
-
|
|
561
|
+
return `
|
|
562
|
+
<div class="detail-section">
|
|
563
|
+
<div class="detail-section-header">
|
|
564
|
+
<span class="detail-section-title">${label}</span>
|
|
565
|
+
</div>
|
|
566
|
+
<div class="chat-container">${messagesHtml}</div>
|
|
567
|
+
</div>
|
|
568
|
+
`;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function syntaxHighlightJson(obj) {
|
|
572
|
+
const json = JSON.stringify(obj, null, 2);
|
|
573
|
+
return json
|
|
574
|
+
.replace(/&/g, '&')
|
|
575
|
+
.replace(/</g, '<')
|
|
576
|
+
.replace(/>/g, '>')
|
|
577
|
+
.replace(/(".*?"):/g, '<span class="json-key">$1</span>:')
|
|
578
|
+
.replace(/: (".*?")/g, ': <span class="json-string">$1</span>')
|
|
579
|
+
.replace(/: (\d+)/g, ': <span class="json-number">$1</span>')
|
|
580
|
+
.replace(/: (true|false)/g, ': <span class="json-boolean">$1</span>')
|
|
581
|
+
.replace(/: (null)/g, ': <span class="json-null">$1</span>');
|
|
346
582
|
}
|
|
347
583
|
|
|
348
584
|
function getEventIcon(type) {
|
|
@@ -353,45 +589,137 @@
|
|
|
353
589
|
'memory_write': '✍️',
|
|
354
590
|
'error': '❌',
|
|
355
591
|
'final_answer': '✅',
|
|
356
|
-
'run_start': '▶️'
|
|
592
|
+
'run_start': '▶️',
|
|
593
|
+
// Multi-agent icons
|
|
594
|
+
'agent_spawn': '👤',
|
|
595
|
+
'agent_join': '📥',
|
|
596
|
+
'agent_leave': '📤',
|
|
597
|
+
'agent_communication': '💬',
|
|
598
|
+
'agent_handoff': '🔀',
|
|
599
|
+
'task_assignment': '📝',
|
|
600
|
+
'task_completion': '✓'
|
|
357
601
|
};
|
|
358
602
|
return icons[type] || '📌';
|
|
359
603
|
}
|
|
360
604
|
|
|
361
605
|
function formatEventType(type) {
|
|
362
|
-
|
|
606
|
+
const labels = {
|
|
607
|
+
'llm_call': 'LLM Call',
|
|
608
|
+
'tool_call': 'Tool Call',
|
|
609
|
+
'memory_read': 'Memory Read',
|
|
610
|
+
'memory_write': 'Memory Write',
|
|
611
|
+
'error': 'Error',
|
|
612
|
+
'final_answer': 'Final Answer',
|
|
613
|
+
'run_start': 'Run Started',
|
|
614
|
+
// Multi-agent labels
|
|
615
|
+
'agent_spawn': 'Agent Spawned',
|
|
616
|
+
'agent_join': 'Agent Joined',
|
|
617
|
+
'agent_leave': 'Agent Left',
|
|
618
|
+
'agent_communication': 'Agent Message',
|
|
619
|
+
'agent_handoff': 'Agent Handoff',
|
|
620
|
+
'task_assignment': 'Task Assigned',
|
|
621
|
+
'task_completion': 'Task Completed'
|
|
622
|
+
};
|
|
623
|
+
return labels[type] || type.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
363
624
|
}
|
|
364
625
|
|
|
365
626
|
function getEventSummary(event) {
|
|
627
|
+
let role = '';
|
|
628
|
+
let agentName = '';
|
|
629
|
+
let agentRole = '';
|
|
630
|
+
let reason = '';
|
|
631
|
+
|
|
366
632
|
switch (event.type) {
|
|
367
633
|
case 'llm_call':
|
|
368
|
-
return event.model ? `Model: ${event.model}` : 'LLM
|
|
634
|
+
return event.model ? `Model: ${event.model}` : 'LLM processing';
|
|
369
635
|
case 'tool_call':
|
|
370
|
-
return event.tool_name ? `Tool: ${event.tool_name}` : 'Tool
|
|
636
|
+
return event.tool_name ? `Tool: ${event.tool_name}` : 'Tool execution';
|
|
371
637
|
case 'memory_read':
|
|
372
|
-
return event.memory_key ? `
|
|
638
|
+
return event.memory_key ? `Reading: ${event.memory_key}` : 'Memory access';
|
|
373
639
|
case 'memory_write':
|
|
374
|
-
return event.memory_key ? `
|
|
640
|
+
return event.memory_key ? `Writing: ${event.memory_key}` : 'Memory update';
|
|
375
641
|
case 'error':
|
|
376
|
-
return event.error_message || '
|
|
642
|
+
return event.error_message || 'An error occurred';
|
|
377
643
|
case 'final_answer':
|
|
378
|
-
return
|
|
644
|
+
return 'Final response generated';
|
|
645
|
+
// Multi-agent events - check both direct fields and data field
|
|
646
|
+
case 'agent_spawn':
|
|
647
|
+
agentName = event.agent_name || (event.data?.agent_name || '');
|
|
648
|
+
agentRole = event.agent_role || (event.data?.agent_role || '');
|
|
649
|
+
role = agentRole ? `(${agentRole})` : '';
|
|
650
|
+
return agentName ? `${agentName}${role} - Spawned` : (event.agent_id || event.name || 'Agent spawned');
|
|
651
|
+
case 'agent_join':
|
|
652
|
+
agentName = event.agent_name || (event.data?.agent_name || '');
|
|
653
|
+
agentRole = event.agent_role || (event.data?.agent_role || '');
|
|
654
|
+
role = agentRole ? `(${agentRole})` : '';
|
|
655
|
+
return agentName ? `${agentName}${role} - Joined` : (event.agent_id || event.name || 'Agent joined');
|
|
656
|
+
case 'agent_leave':
|
|
657
|
+
agentName = event.agent_name || (event.data?.agent_name || '');
|
|
658
|
+
reason = event.data?.reason || event.reason ? `(${event.data?.reason || event.reason})` : '';
|
|
659
|
+
return agentName ? `${agentName} left${reason}` : (event.agent_id || event.name || 'Agent left');
|
|
660
|
+
case 'agent_communication': {
|
|
661
|
+
const fromLabel = event.from_agent_name || event.from_agent_id || event.data?.from_agent_name || event.data?.from_agent_id;
|
|
662
|
+
const toLabel = event.to_agent_name || event.to_agent_id || event.data?.to_agent_name || event.data?.to_agent_id;
|
|
663
|
+
if (fromLabel) return toLabel ? `${fromLabel} → ${toLabel}` : `${fromLabel} → All`;
|
|
664
|
+
return event.name || 'Agent message';
|
|
665
|
+
}
|
|
666
|
+
case 'agent_handoff': {
|
|
667
|
+
const fromLabel = event.from_agent_name || event.from_agent_id || event.data?.from_agent_name || event.data?.from_agent_id;
|
|
668
|
+
const toLabel = event.to_agent_name || event.to_agent_id || event.data?.to_agent_name || event.data?.to_agent_id;
|
|
669
|
+
return (fromLabel && toLabel) ? `${fromLabel} → ${toLabel}` : (event.name || 'Agent handoff');
|
|
670
|
+
}
|
|
671
|
+
case 'task_assignment': {
|
|
672
|
+
const taskLabel = event.task_name || event.task_id || event.data?.task_name || event.data?.task_id;
|
|
673
|
+
const assigneeLabel = event.assigned_to_agent_name || event.assigned_to_agent_id || event.data?.assigned_to_agent_name || event.data?.assigned_to_agent_id;
|
|
674
|
+
if (!taskLabel || !assigneeLabel) return event.name || 'Task assigned';
|
|
675
|
+
const shortTask = taskLabel.length > 45 ? taskLabel.substring(0, 45) + '…' : taskLabel;
|
|
676
|
+
return `${shortTask} → ${assigneeLabel}`;
|
|
677
|
+
}
|
|
678
|
+
case 'task_completion': {
|
|
679
|
+
const taskLabel = event.task_name || event.task_id || event.data?.task_name || event.data?.task_id;
|
|
680
|
+
if (!taskLabel) return event.name || 'Task completed';
|
|
681
|
+
const shortTask = taskLabel.length > 45 ? taskLabel.substring(0, 45) + '…' : taskLabel;
|
|
682
|
+
return `${shortTask} completed`;
|
|
683
|
+
}
|
|
379
684
|
default:
|
|
380
685
|
return event.name || event.type;
|
|
381
686
|
}
|
|
382
687
|
}
|
|
383
688
|
|
|
384
689
|
function formatTimestamp(ms) {
|
|
690
|
+
if (!ms) return 'N/A';
|
|
385
691
|
const date = new Date(ms);
|
|
386
692
|
return date.toLocaleString();
|
|
387
693
|
}
|
|
388
694
|
|
|
695
|
+
function formatTimeOnly(ms) {
|
|
696
|
+
if (!ms) return '';
|
|
697
|
+
const date = new Date(ms);
|
|
698
|
+
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function formatDateOnly(ms) {
|
|
702
|
+
if (!ms) return '';
|
|
703
|
+
const date = new Date(ms);
|
|
704
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
705
|
+
}
|
|
706
|
+
|
|
389
707
|
function escapeHtml(text) {
|
|
708
|
+
if (text === null || text === undefined) return '';
|
|
390
709
|
const div = document.createElement('div');
|
|
391
710
|
div.textContent = text;
|
|
392
711
|
return div.innerHTML;
|
|
393
712
|
}
|
|
394
713
|
|
|
714
|
+
function parseMaybeJson(text) {
|
|
715
|
+
if (!text) return null;
|
|
716
|
+
try {
|
|
717
|
+
return JSON.parse(text);
|
|
718
|
+
} catch (e) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
395
723
|
function initTheme() {
|
|
396
724
|
const saved = localStorage.getItem('agent_inspector_theme') || 'auto';
|
|
397
725
|
applyTheme(saved);
|
|
@@ -413,27 +741,7 @@
|
|
|
413
741
|
root.classList.add('theme-dark');
|
|
414
742
|
}
|
|
415
743
|
localStorage.setItem('agent_inspector_theme', theme);
|
|
416
|
-
|
|
417
|
-
? 'Theme: Auto'
|
|
418
|
-
: `Theme: ${theme[0].toUpperCase()}${theme.slice(1)}`;
|
|
419
|
-
themeToggle.textContent = label;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function parseMaybeJson(text) {
|
|
423
|
-
try {
|
|
424
|
-
return JSON.parse(text);
|
|
425
|
-
} catch (e) {
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function renderChatMessages(messages) {
|
|
431
|
-
return messages.map(msg => `
|
|
432
|
-
<div class="chat-row ${escapeHtml(msg.role || 'unknown')}">
|
|
433
|
-
<div class="chat-role">${escapeHtml(msg.role || 'unknown')}</div>
|
|
434
|
-
<div class="chat-content">${escapeHtml(msg.content || '')}</div>
|
|
435
|
-
</div>
|
|
436
|
-
`).join('');
|
|
744
|
+
themeLabel.textContent = theme === 'auto' ? 'Auto' : theme.charAt(0).toUpperCase() + theme.slice(1);
|
|
437
745
|
}
|
|
438
746
|
</script>
|
|
439
747
|
<script src="/ui/static/app.js" defer></script>
|