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)