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");