tool specific step badges
3clyp50 committed
Jan 3, 2026 at 03:22 UTC
3e31dcbb421ecf6cafa5f5af290e6520dfbe4236
3 files changed
+116
-22
webui/components/messages/process-group/process-group-store.js
+47
-18
@@ -1,6 +1,43 @@
1
import { createStore } from "/js/AlpineStore.js";
2
3
// Process Group Store - manages collapsible process groups in chat
4
+
5
+// Unified mapping for both Tool Names and Step Types
6
+// Specific tool names (keys) take precedence over generic types
7
+const DISPLAY_CODES = {
8
+ // --- Specific Tools ---
9
+ 'call_subordinate': 'SUB',
10
+ 'search_engine': 'WEB',
11
+ 'a2a_chat': 'A2A',
12
+ 'behaviour_adjustment': 'ADJ',
13
+ 'document_query': 'DOC',
14
+ 'vision_load': 'EYE',
15
+ 'notify_user': 'NTF',
16
+ 'scheduler': 'SCH',
17
+ 'unknown': 'UNK',
18
+ // Memory operations group
19
+ 'memory_save': 'MEM',
20
+ 'memory_load': 'MEM',
21
+ 'memory_forget': 'MEM',
22
+ 'memory_delete': 'MEM',
23
+
24
+ // --- Step Types ---
25
+ 'agent': 'GEN',
26
+ 'response': 'END',
27
+ 'tool': 'USE', // Generic fallback for tools
28
+ 'code_exe': 'EXE',
29
+ 'browser': 'BRW',
30
+ 'progress': 'HLD',
31
+ 'subagent': 'SUB', // Type fallback if tool name missing
32
+ 'mcp': 'MCP',
33
+ 'info': 'INF',
34
+ 'hint': 'HNT',
35
+ 'warning': 'WRN',
36
+ 'error': 'ERR',
37
+ 'util': 'UTL',
38
+ 'done': 'END'
39
+};
40
+
41
const model = {
42
// Track which process groups are expanded (by group ID)
43
expandedGroups: {},
@@ -101,24 +138,16 @@ const model = {
138
},
139
140
// Status code (3-4 letter) for backend log types
104
- getStepCode(type) {
105
- const codes = {
106
- 'agent': 'GEN',
107
- 'response': 'END',
108
- 'tool': 'TOOL',
109
- 'mcp': 'MCP',
110
- 'subagent': 'SUB',
111
- 'code_exe': 'EXE',
112
- 'browser': 'BRW',
113
- 'progress': 'WAIT',
114
- 'info': 'INF',
115
- 'hint': 'HNT',
116
- 'warning': 'WRN',
117
- 'error': 'ERR',
118
- 'util': 'UTL',
119
- 'done': 'END'
120
- };
121
- return codes[type] || type?.toUpperCase()?.slice(0, 4) || 'GEN';
141
+ // Looks up tool name first (specific), then falls back to type (generic)
142
+ getStepCode(type, toolName = null) {
143
+ // Specific tool codes only apply to generic 'tool' steps
144
+ if (type === 'tool' && toolName && DISPLAY_CODES[toolName]) {
145
+ return DISPLAY_CODES[toolName];
146
+ }
147
+
148
+ return DISPLAY_CODES[type] ||
149
+ type?.toUpperCase()?.slice(0, 4) ||
150
+ 'GEN';
151
},
152
153
// CSS color class for backend log types
webui/components/messages/process-group/process-group.css
+2
-2
@@ -155,7 +155,7 @@
155
border: 1px solid rgba(34, 197, 94, 0.3);
156
}
157
158
-/* TOOL - tool type (amber/yellow) */
158
+/* USE - tool usage (amber/yellow) */
159
.status-tool {
160
background-color: rgba(251, 191, 36, 0.15);
161
color: #fbbf24;
@@ -512,7 +512,7 @@
512
.process-step-detail-content .step-kvps {
513
display: flex;
514
flex-direction: column;
515
- gap: var(--spacing-xxs);
515
+ gap: var(--spacing-sm);
516
}
517
518
.process-step-detail-content .step-kvp {
webui/js/messages.js
+67
-2
@@ -13,6 +13,49 @@ const chatHistory = document.getElementById("chat-history");
13
let messageGroup = null;
14
let currentProcessGroup = null; // Track current process group for collapsible UI
15
16
+/**
17
+ * Resolve tool name from kvps, existing attribute, or previous siblings
18
+ * For 'tool' type steps, inherits from preceding step if not directly available
19
+ */
20
+function resolveToolName(type, kvps, stepElement) {
21
+ // Direct from kvps
22
+ if (kvps?.tool_name) return kvps.tool_name;
23
+
24
+ // Keep existing if present (for non-tool types during updates)
25
+ if (type !== 'tool' && stepElement?.hasAttribute('data-tool-name')) {
26
+ return stepElement.getAttribute('data-tool-name');
27
+ }
28
+
29
+ // Inherit from previous sibling (for tool steps)
30
+ if (type === 'tool' && stepElement) {
31
+ let prev = stepElement.previousElementSibling;
32
+ while (prev) {
33
+ if (prev.hasAttribute('data-tool-name')) {
34
+ return prev.getAttribute('data-tool-name');
35
+ }
36
+ prev = prev.previousElementSibling;
37
+ }
38
+ }
39
+
40
+ return null;
41
+}
42
+
43
+/**
44
+ * Update status badge text content while preserving the icon
45
+ */
46
+function updateBadgeText(badge, newCode) {
47
+ if (!badge) return;
48
+
49
+ // Find and update text node, or append new one
50
+ for (const node of badge.childNodes) {
51
+ if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
52
+ node.textContent = newCode;
53
+ return;
54
+ }
55
+ }
56
+ badge.appendChild(document.createTextNode(newCode));
57
+}
58
+
59
// Process types that should be grouped into collapsible sections
60
const PROCESS_TYPES = ['agent', 'tool', 'code_exe', 'browser', 'progress', 'info', 'hint', 'util', 'warning'];
61
// Main types that should always be visible (not collapsed)
@@ -1182,6 +1225,19 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1225
step.setAttribute("data-type", type);
1226
step.setAttribute("data-step-id", id);
1227
1228
+ // Resolve tool name (direct, inherited, or null)
1229
+ // For new steps, pass null as stepElement - inheritance uses stepsContainer query
1230
+ let toolNameToUse = kvps?.tool_name;
1231
+ if (type === 'tool' && !toolNameToUse) {
1232
+ const existingSteps = stepsContainer.querySelectorAll('.process-step[data-tool-name]');
1233
+ if (existingSteps.length > 0) {
1234
+ toolNameToUse = existingSteps[existingSteps.length - 1].getAttribute("data-tool-name");
1235
+ }
1236
+ }
1237
+ if (toolNameToUse) {
1238
+ step.setAttribute("data-tool-name", toolNameToUse);
1239
+ }
1240
+
1241
// Store timestamp for duration calculation
1242
if (timestamp) {
1243
step.setAttribute("data-timestamp", timestamp);
@@ -1227,7 +1283,7 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1283
stepHeader.classList.add("process-step-header");
1284
1285
// Status code and color class from store (maps backend types)
1230
- const statusCode = processGroupStore.getStepCode(type);
1286
+ const statusCode = processGroupStore.getStepCode(type, toolNameToUse);
1287
const statusColorClass = processGroupStore.getStatusColorClass(type);
1288
1289
// Icon extracted from heading (backend sends icon://xxx), fallback to type-based
@@ -1290,6 +1346,14 @@ function updateProcessStep(stepElement, id, type, heading, content, kvps, durati
1346
stepElement.setAttribute("data-duration-ms", durationMs);
1347
}
1348
1349
+ // Resolve and update tool name + badge
1350
+ const toolNameToUse = resolveToolName(type, kvps, stepElement);
1351
+ if (toolNameToUse) {
1352
+ stepElement.setAttribute("data-tool-name", toolNameToUse);
1353
+ const newCode = processGroupStore.getStepCode(type, toolNameToUse);
1354
+ updateBadgeText(stepElement.querySelector(".status-badge"), newCode);
1355
+ }
1356
+
1357
// Update detail content
1358
const detailContent = stepElement.querySelector(".process-step-detail-content");
1359
if (detailContent) {
@@ -1644,12 +1708,13 @@ function updateProcessGroupHeader(group) {
1708
// Get the last step's type for status
1709
const lastStep = steps[steps.length - 1];
1710
const lastType = lastStep.getAttribute("data-type");
1711
+ const lastToolName = lastStep.getAttribute("data-tool-name");
1712
const lastTitle = lastStep.querySelector(".step-title")?.textContent || "";
1713
1714
// Update status badge with icon (keep status-active during execution)
1715
if (statusEl) {
1716
// Status code and color class from store (maps backend types)
1652
- const statusCode = processGroupStore.getStepCode(lastType);
1717
+ const statusCode = processGroupStore.getStepCode(lastType, lastToolName);
1718
const statusColorClass = processGroupStore.getStatusColorClass(lastType);
1719
// Get icon from the last step's badge, fallback to type-based
1720
const lastStepBadge = lastStep.querySelector(".badge-icon");