revert token counting logic

remove token counter; unified cleanText; icons coverage

3clyp50 committed Jan 2, 2026 at 15:26 UTC 8d94e676e470a687d7176b06005c4301183df1c4
3 files changed +107 -123
python/helpers/log.py
-23
@@ -134,8 +134,6 @@ class LogItem:
134 guid: str = ""
135 timestamp: float = 0.0 # Unix timestamp in seconds
136 duration_ms: Optional[int] = None # Duration until next step (set by Log.log)
137 - tokens_in: int = 0 # Input tokens consumed
138 - tokens_out: int = 0 # Output tokens generated
137 agent_number: int = 0 # Agent number (0 = main agent, 1+ = subordinate agents)
138
139 def __post_init__(self):
@@ -155,8 +153,6 @@ class LogItem:
153 kvps: dict | None = None,
154 temp: bool | None = None,
155 update_progress: ProgressUpdate | None = None,
158 - tokens_in: int | None = None,
159 - tokens_out: int | None = None,
156 **kwargs,
157 ):
158 if self.guid == self.log.guid:
@@ -168,18 +164,9 @@ class LogItem:
164 kvps=kvps,
165 temp=temp,
166 update_progress=update_progress,
171 - tokens_in=tokens_in,
172 - tokens_out=tokens_out,
167 **kwargs,
168 )
169
176 - def add_tokens(self, tokens_in: int = 0, tokens_out: int = 0):
177 - """Add tokens to this log item (accumulative)."""
178 - if self.guid == self.log.guid:
179 - self.tokens_in += tokens_in
180 - self.tokens_out += tokens_out
181 - self.log.updates += [self.no]
182 -
170 def stream(
171 self,
172 heading: str | None = None,
@@ -206,8 +193,6 @@ class LogItem:
193 "kvps": self.kvps,
194 "timestamp": self.timestamp, # Unix timestamp in seconds
195 "duration_ms": self.duration_ms, # Duration until next step
209 - "tokens_in": self.tokens_in, # Input tokens
210 - "tokens_out": self.tokens_out, # Output tokens
196 "agent_number": self.agent_number, # Agent number for identifying main/subordinate agents
197 }
198
@@ -270,8 +255,6 @@ class Log:
255 temp: bool | None = None,
256 update_progress: ProgressUpdate | None = None,
257 id: Optional[str] = None,
273 - tokens_in: int | None = None,
274 - tokens_out: int | None = None,
258 **kwargs,
259 ):
260 item = self.logs[no]
@@ -288,12 +271,6 @@ class Log:
271 if update_progress is not None:
272 item.update_progress = update_progress
273
291 - if tokens_in is not None:
292 - item.tokens_in = tokens_in
293 -
294 - if tokens_out is not None:
295 - item.tokens_out = tokens_out
296 -
274
275 # adjust all content before processing
276 if heading is not None:
webui/index.js
+4 -4
@@ -185,8 +185,8 @@ async function updateUserTime() {
185 updateUserTime();
186 setInterval(updateUserTime, 1000);
187
188 -function setMessage(id, type, heading, content, temp, kvps = null, timestamp = null, durationMs = null, tokensIn = 0, tokensOut = 0, agentNumber = 0) {
189 - const result = msgs.setMessage(id, type, heading, content, temp, kvps, timestamp, durationMs, tokensIn, tokensOut, agentNumber);
188 +function setMessage(id, type, heading, content, temp, kvps = null, timestamp = null, durationMs = null, /* tokensIn = 0, tokensOut = 0, */ agentNumber = 0) {
189 + const result = msgs.setMessage(id, type, heading, content, temp, kvps, timestamp, durationMs, /* tokensIn, tokensOut, */ agentNumber);
190 const chatHistoryEl = document.getElementById("chat-history");
191 if (preferencesStore.autoScroll && chatHistoryEl) {
192 chatHistoryEl.scrollTop = chatHistoryEl.scrollHeight;
@@ -313,8 +313,8 @@ export async function poll() {
313 log.kvps,
314 log.timestamp,
315 log.duration_ms,
316 - log.tokens_in,
317 - log.tokens_out,
316 + // log.tokens_in,
317 + // log.tokens_out,
318 log.agent_number || 0 // Agent number for identifying main/subordinate agents
319 );
320 }
webui/js/messages.js
+103 -96
@@ -17,7 +17,7 @@ const PROCESS_TYPES = ['agent', 'tool', 'code_exe', 'browser', 'info', 'hint', '
17 // Main types that should always be visible (not collapsed)
18 const MAIN_TYPES = ['user', 'response', 'error', 'rate_limit'];
19
20 -export function setMessage(id, type, heading, content, temp, kvps = null, timestamp = null, durationMs = null, tokensIn = 0, tokensOut = 0, agentNumber = 0) {
20 +export function setMessage(id, type, heading, content, temp, kvps = null, timestamp = null, durationMs = null, /* tokensIn = 0, tokensOut = 0, */ agentNumber = 0) {
21 // Check if this is a process type message
22 const isProcessType = PROCESS_TYPES.includes(type);
23 const isMainType = MAIN_TYPES.includes(type);
@@ -36,7 +36,7 @@ export function setMessage(id, type, heading, content, temp, kvps = null, timest
36 if (isProcessType) {
37 if (processStepElement) {
38 // Update existing process step
39 - updateProcessStep(processStepElement, id, type, heading, content, kvps, durationMs, tokensIn, tokensOut);
39 + updateProcessStep(processStepElement, id, type, heading, content, kvps, durationMs /*, tokensIn, tokensOut */);
40 return processStepElement;
41 }
42
@@ -47,7 +47,7 @@ export function setMessage(id, type, heading, content, temp, kvps = null, timest
47 }
48
49 // Add step to current process group
50 - processStepElement = addProcessStep(currentProcessGroup, id, type, heading, content, kvps, timestamp, durationMs, tokensIn, tokensOut);
50 + processStepElement = addProcessStep(currentProcessGroup, id, type, heading, content, kvps, timestamp, durationMs /*, tokensIn, tokensOut */);
51 return processStepElement;
52 }
53
@@ -55,7 +55,7 @@ export function setMessage(id, type, heading, content, temp, kvps = null, timest
55 // agentNumber: 0 = main agent, 1+ = subordinate agents
56 if (type === "response" && agentNumber !== 0) {
57 if (processStepElement) {
58 - updateProcessStep(processStepElement, id, "agent", heading, content, kvps, durationMs, tokensIn, tokensOut);
58 + updateProcessStep(processStepElement, id, "agent", heading, content, kvps, durationMs /*, tokensIn, tokensOut */);
59 return processStepElement;
60 }
61
@@ -66,7 +66,7 @@ export function setMessage(id, type, heading, content, temp, kvps = null, timest
66 }
67
68 // Add subordinate response as a step (type "agent" for appropriate styling)
69 - processStepElement = addProcessStep(currentProcessGroup, id, "agent", heading, content, kvps, timestamp, durationMs, tokensIn, tokensOut);
69 + processStepElement = addProcessStep(currentProcessGroup, id, "agent", heading, content, kvps, timestamp, durationMs /*, tokensIn, tokensOut */);
70 return processStepElement;
71 }
72
@@ -929,6 +929,22 @@ function convertToTitleCase(str) {
929 });
930 }
931
932 +/**
933 + * Clean text value by removing standalone bracket lines and trimming
934 + * Handles both strings and arrays (filters out bracket-only items)
935 + */
936 +function cleanTextValue(value) {
937 + if (Array.isArray(value)) {
938 + return value
939 + .filter(item => item && String(item).trim() && !/^[\[\]]$/.test(String(item).trim()))
940 + .join("\n");
941 + }
942 + if (typeof value === "object" && value !== null) {
943 + return JSON.stringify(value, null, 2);
944 + }
945 + return String(value).replace(/^\s*[\[\]]\s*$/gm, "").trim();
946 +}
947 +
948 function convertImageTags(content) {
949 // Regular expression to match <image> tags and extract base64 content
950 const imageTagRegex = /<image>(.*?)<\/image>/g;
@@ -1121,10 +1137,10 @@ function createProcessGroup(id) {
1137 <span class="group-title">Processing...</span>
1138 <span class="status-badge status-gen status-active group-status"><span class="badge-icon material-symbols-outlined">public</span>GEN</span>
1139 <span class="group-metrics">
1124 - <span class="metric-time" title="Start time"><span class="metric-value">--:--</span></span>
1125 - <span class="metric-steps" title="Steps"><span class="metric-value">0</span></span>
1126 - <span class="metric-duration" title="Duration"><span class="metric-value">0s</span></span>
1127 - <span class="metric-tokens" title="Tokens"><span class="metric-value">--</span></span>
1140 + <span class="metric-time" title="Start time"><span class="material-symbols-outlined">schedule</span><span class="metric-value">--:--</span></span>
1141 + <span class="metric-steps" title="Steps"><span class="material-symbols-outlined">list_alt</span><span class="metric-value">0</span></span>
1142 + <span class="metric-duration" title="Duration"><span class="material-symbols-outlined">timer</span><span class="metric-value">0s</span></span>
1143 + <!-- <span class="metric-tokens" title="Tokens"><span class="material-symbols-outlined">data_object</span><span class="metric-value">--</span></span> -->
1144 </span>
1145 `;
1146
@@ -1154,7 +1170,7 @@ function createProcessGroup(id) {
1170 /**
1171 * Add a step to a process group
1172 */
1157 -function addProcessStep(group, id, type, heading, content, kvps, timestamp = null, durationMs = null, tokensIn = 0, tokensOut = 0) {
1173 +function addProcessStep(group, id, type, heading, content, kvps, timestamp = null, durationMs = null /*, tokensIn = 0, tokensOut = 0 */) {
1174 const groupId = group.getAttribute("data-group-id");
1175 const stepsContainer = group.querySelector(".process-steps");
1176 const isGroupCompleted = group.classList.contains("process-group-completed");
@@ -1165,8 +1181,8 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1181 step.classList.add("process-step");
1182 step.setAttribute("data-type", type);
1183 step.setAttribute("data-step-id", id);
1168 - step.setAttribute("data-tokens-in", tokensIn || 0);
1169 - step.setAttribute("data-tokens-out", tokensOut || 0);
1184 + // step.setAttribute("data-tokens-in", tokensIn || 0);
1185 + // step.setAttribute("data-tokens-out", tokensOut || 0);
1186
1187 // Store timestamp for duration calculation
1188 if (timestamp) {
@@ -1256,7 +1272,7 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1272 /**
1273 * Update an existing process step
1274 */
1259 -function updateProcessStep(stepElement, id, type, heading, content, kvps, durationMs = null, tokensIn = 0, tokensOut = 0) {
1275 +function updateProcessStep(stepElement, id, type, heading, content, kvps, durationMs = null /*, tokensIn = 0, tokensOut = 0 */) {
1276 // Update title
1277 const titleEl = stepElement.querySelector(".step-title");
1278 if (titleEl) {
@@ -1265,12 +1281,12 @@ function updateProcessStep(stepElement, id, type, heading, content, kvps, durati
1281 }
1282
1283 // Update token data (use the latest values)
1268 - if (tokensIn > 0) {
1269 - stepElement.setAttribute("data-tokens-in", tokensIn);
1270 - }
1271 - if (tokensOut > 0) {
1272 - stepElement.setAttribute("data-tokens-out", tokensOut);
1273 - }
1284 + // if (tokensIn > 0) {
1285 + // stepElement.setAttribute("data-tokens-in", tokensIn);
1286 + // }
1287 + // if (tokensOut > 0) {
1288 + // stepElement.setAttribute("data-tokens-out", tokensOut);
1289 + // }
1290
1291 // Update detail content
1292 const detailContent = stepElement.querySelector(".process-step-detail-content");
@@ -1357,15 +1373,7 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1373 if (code || output) {
1374 const terminalDiv = document.createElement("div");
1375 terminalDiv.classList.add("step-terminal");
1360 -
1361 - // Show command line
1362 - if (code) {
1363 - const cmdLine = document.createElement("div");
1364 - cmdLine.classList.add("terminal-cmd");
1365 - cmdLine.innerHTML = `<span class="terminal-prompt">${escapeHTML(runtime)}></span> <span class="terminal-code">${escapeHTML(code)}</span>`;
1366 - terminalDiv.appendChild(cmdLine);
1367 - }
1368 -
1376 +
1377 // Show output if present
1378 if (output && output.trim()) {
1379 const outputPre = document.createElement("pre");
@@ -1419,18 +1427,7 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1427 thoughtsDiv.classList.add("hide-thoughts");
1428 }
1429
1422 - let thoughtText = value;
1423 - if (typeof value === "object") {
1424 - // Handle array of thoughts
1425 - if (Array.isArray(value)) {
1426 - thoughtText = value.filter(t => t && String(t).trim() && String(t).trim() !== "[" && String(t).trim() !== "]").join("\n");
1427 - } else {
1428 - thoughtText = JSON.stringify(value, null, 2);
1429 - }
1430 - }
1431 -
1432 - // Clean up the text - remove standalone brackets
1433 - thoughtText = String(thoughtText).replace(/^\s*[\[\]]\s*$/gm, "").trim();
1430 + const thoughtText = cleanTextValue(value);
1431
1432 if (thoughtText) {
1433 // Single icon + text block
@@ -1463,7 +1460,10 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1460 'content': 'article',
1461 'name': 'label',
1462 'id': 'tag',
1466 - 'type': 'category'
1463 + 'type': 'category',
1464 + 'document': 'description',
1465 + 'documents': 'folder_open',
1466 + 'queries': 'search'
1467 };
1468
1469 for (const [argKey, argValue] of Object.entries(value)) {
@@ -1484,11 +1484,9 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1484 const argVal = document.createElement("span");
1485 argVal.classList.add("tool-arg-value");
1486
1487 - let argText = argValue;
1488 - if (typeof argValue === "object") {
1489 - argText = JSON.stringify(argValue, null, 2);
1490 - }
1491 - argVal.textContent = truncateText(String(argText), 300);
1487 + const argText = cleanTextValue(argValue);
1488 +
1489 + argVal.textContent = truncateText(argText, 300);
1490
1491 argRow.appendChild(argLabel);
1492 argRow.appendChild(argVal);
@@ -1520,7 +1518,11 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1518 'id': 'tag',
1519 'type': 'category',
1520 'runtime': 'memory',
1523 - 'result': 'output'
1521 + 'result': 'output',
1522 + 'progress': 'pending',
1523 + 'document': 'description',
1524 + 'documents': 'folder_open',
1525 + 'queries': 'search'
1526 };
1527
1528 // lowerKey already defined above
@@ -1533,12 +1535,9 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1535 const valueSpan = document.createElement("span");
1536 valueSpan.classList.add("step-kvp-value");
1537
1536 - let valueText = value;
1537 - if (typeof value === "object") {
1538 - valueText = JSON.stringify(value, null, 2);
1539 - }
1538 + const valueText = cleanTextValue(value);
1539
1541 - valueSpan.textContent = truncateText(String(valueText), 500);
1540 + valueSpan.textContent = truncateText(valueText, 1000);
1541
1542 kvpDiv.appendChild(keySpan);
1543 kvpDiv.appendChild(valueSpan);
@@ -1573,16 +1572,7 @@ function renderThoughts(container, value) {
1572 thoughtsDiv.classList.add("hide-thoughts");
1573 }
1574
1576 - let thoughtText = value;
1577 - if (typeof value === "object") {
1578 - if (Array.isArray(value)) {
1579 - thoughtText = value.filter(t => t && String(t).trim() && String(t).trim() !== "[" && String(t).trim() !== "]").join("\n");
1580 - } else {
1581 - thoughtText = JSON.stringify(value, null, 2);
1582 - }
1583 - }
1584 -
1585 - thoughtText = String(thoughtText).replace(/^\s*[\[\]]\s*$/gm, "").trim();
1575 + const thoughtText = cleanTextValue(value);
1576
1577 if (thoughtText) {
1578 thoughtsDiv.innerHTML = `<span class="thought-icon material-symbols-outlined">lightbulb</span><span class="thought-text">${escapeHTML(thoughtText)}</span>`;
@@ -1600,6 +1590,22 @@ function updateProcessGroupHeader(group) {
1590 const metricsEl = group.querySelector(".group-metrics");
1591 const isCompleted = group.classList.contains("process-group-completed");
1592
1593 + // Update group title with the latest agent step heading
1594 + if (titleEl && !isCompleted) {
1595 + // Find the last "agent" type step
1596 + const agentSteps = Array.from(steps).filter(step => step.getAttribute("data-type") === "agent");
1597 + if (agentSteps.length > 0) {
1598 + const lastAgentStep = agentSteps[agentSteps.length - 1];
1599 + const lastHeading = lastAgentStep.querySelector(".step-title")?.textContent;
1600 + if (lastHeading) {
1601 + const cleanTitle = cleanStepTitle(lastHeading, 50);
1602 + if (cleanTitle) {
1603 + titleEl.textContent = cleanTitle;
1604 + }
1605 + }
1606 + }
1607 + }
1608 +
1609 // Update step count in metrics
1610 const stepsMetricEl = metricsEl?.querySelector(".metric-steps .metric-value");
1611 if (stepsMetricEl) {
@@ -1616,9 +1622,9 @@ function updateProcessGroupHeader(group) {
1622 timeMetricEl.textContent = `${hours}:${minutes}`;
1623 }
1624
1619 - // Update duration metric (elapsed time since start)
1625 + // Update duration metric (elapsed time since start) - but only if not completed
1626 const durationMetricEl = metricsEl?.querySelector(".metric-duration .metric-value");
1621 - if (durationMetricEl && startTimestamp) {
1627 + if (durationMetricEl && startTimestamp && !isCompleted) {
1628 const startMs = parseFloat(startTimestamp) * 1000;
1629 const elapsedMs = Date.now() - startMs;
1630 if (elapsedMs < 60000) {
@@ -1631,22 +1637,22 @@ function updateProcessGroupHeader(group) {
1637 }
1638
1639 // Update tokens metric (aggregate from all steps)
1634 - const tokensMetricEl = metricsEl?.querySelector(".metric-tokens .metric-value");
1635 - if (tokensMetricEl) {
1636 - let totalTokensIn = 0;
1637 - let totalTokensOut = 0;
1638 - steps.forEach(step => {
1639 - totalTokensIn += parseInt(step.getAttribute("data-tokens-in") || 0, 10);
1640 - totalTokensOut += parseInt(step.getAttribute("data-tokens-out") || 0, 10);
1641 - });
1642 - const totalTokens = totalTokensIn + totalTokensOut;
1643 - if (totalTokens > 0) {
1644 - // Format as compact notation (e.g., 20k/3k for input/output)
1645 - tokensMetricEl.textContent = formatTokenCount(totalTokensIn, totalTokensOut);
1646 - } else {
1647 - tokensMetricEl.textContent = "--";
1648 - }
1649 - }
1640 + // const tokensMetricEl = metricsEl?.querySelector(".metric-tokens .metric-value");
1641 + // if (tokensMetricEl) {
1642 + // let totalTokensIn = 0;
1643 + // let totalTokensOut = 0;
1644 + // steps.forEach(step => {
1645 + // totalTokensIn += parseInt(step.getAttribute("data-tokens-in") || 0, 10);
1646 + // totalTokensOut += parseInt(step.getAttribute("data-tokens-out") || 0, 10);
1647 + // });
1648 + // const totalTokens = totalTokensIn + totalTokensOut;
1649 + // if (totalTokens > 0) {
1650 + // // Format as compact notation (e.g., 20k/3k for input/output)
1651 + // tokensMetricEl.textContent = formatTokenCount(totalTokensIn, totalTokensOut);
1652 + // } else {
1653 + // tokensMetricEl.textContent = "--";
1654 + // }
1655 + // }
1656
1657 // Once a group is completed, never re-enable any loading spinners (status-active).
1658 // This prevents late util/tool messages from making a completed group look "running".
@@ -1762,23 +1768,24 @@ export function resetProcessGroups() {
1768
1769 /**
1770 * Format token counts in compact notation (e.g., "12k/3k" for input/output)
1771 + * (Currently disabled - token tracking not implemented)
1772 */
1766 -function formatTokenCount(tokensIn, tokensOut) {
1767 - const formatCompact = (n) => {
1768 - if (n >= 1000000) return `${(n / 1000000).toFixed(1)}m`;
1769 - if (n >= 1000) return `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k`;
1770 - return n.toString();
1771 - };
1772 -
1773 - if (tokensIn > 0 && tokensOut > 0) {
1774 - return `${formatCompact(tokensIn)}/${formatCompact(tokensOut)}`;
1775 - } else if (tokensIn > 0) {
1776 - return `${formatCompact(tokensIn)}↓`;
1777 - } else if (tokensOut > 0) {
1778 - return `${formatCompact(tokensOut)}↑`;
1779 - }
1780 - return "--";
1781 -}
1773 +// function formatTokenCount(tokensIn, tokensOut) {
1774 +// const formatCompact = (n) => {
1775 +// if (n >= 1000000) return `${(n / 1000000).toFixed(1)}m`;
1776 +// if (n >= 1000) return `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k`;
1777 +// return n.toString();
1778 +// };
1779 +//
1780 +// if (tokensIn > 0 && tokensOut > 0) {
1781 +// return `${formatCompact(tokensIn)}/${formatCompact(tokensOut)}`;
1782 +// } else if (tokensIn > 0) {
1783 +// return `${formatCompact(tokensIn)}↓`;
1784 +// } else if (tokensOut > 0) {
1785 +// return `${formatCompact(tokensOut)}↑`;
1786 +// }
1787 +// return "--";
1788 +// }
1789
1790 /**
1791 * Format Unix timestamp as date-time string (YYYY-MM-DD HH:MM:SS)