ui: process group metrics polishing, group completion bugfix
frdel committed
Jan 31, 2026 at 11:22 UTC
84b867116bc5d4c8a527d87a9bb3d70223729a54
3 files changed
+42
-36
webui/css/messages.css
+1
-1
@@ -781,4 +781,4 @@
781
to {
782
opacity: 0;
783
}
784
-}
784
+}
\ No newline at end of file
webui/index.css
+4
@@ -1558,3 +1558,7 @@ nav ul li a img {
1558
}
1559
1560
.light-mode .dropdown-item:hover { background-color: var(--color-background-hover); }
1561
+
1562
+.display-none{
1563
+ display: none !important;
1564
+}
\ No newline at end of file
webui/js/messages.js
+37
-35
@@ -794,6 +794,10 @@ export function drawMessageUser({
794
kvps = null,
795
...additional
796
}) {
797
+
798
+ // end last process group on any user message
799
+ completeLastProcessGroup();
800
+
801
const messageContainer = getOrCreateMessageContainer(
802
id,
803
"right",
@@ -801,9 +805,6 @@ export function drawMessageUser({
805
true,
806
);
807
804
- // end last process group on any user message
805
- completeLastProcessGroup();
806
-
808
// Find existing message div or create new one
809
let messageDiv = messageContainer.querySelector(".message");
810
if (!messageDiv) {
@@ -1644,8 +1645,10 @@ function createProcessGroup(id) {
1645
<span class="step-badge GEN">GEN</span>
1646
<span class="group-metrics">
1647
<span class="metric-time" title="Start time"><span class="material-symbols-outlined">schedule</span><span class="metric-value">--:--</span></span>
1647
- <span class="metric-steps" title="Steps"><span class="material-symbols-outlined">footprint</span><span class="metric-value">0</span></span>
1648
+ <span class="metric-steps display-none" title="Steps"><span class="material-symbols-outlined">footprint</span><span class="metric-value">0</span></span>
1649
<span class="metric-notifications" title="Warnings/Info/Hint" hidden><span class="material-symbols-outlined">priority_high</span><span class="metric-value">0</span></span>
1650
+ <span class="metric-duration display-none" title="Duration"><span class="material-symbols-outlined">timer</span><span class="metric-value">--</span></span>
1651
+
1652
</span>
1653
`;
1654
@@ -1914,13 +1917,18 @@ function updateProcessGroupHeader(group) {
1917
}
1918
1919
// Update step count in metrics - All GEN steps from all agents per process group
1917
- const stepsMetricEl = metricsEl?.querySelector(".metric-steps .metric-value");
1918
- if (stepsMetricEl) {
1919
- const genSteps = group.querySelectorAll('.process-step[data-type="agent"]');
1920
- stepsMetricEl.textContent = genSteps.length.toString();
1920
+ const stepMetricContainerEl = metricsEl?.querySelector(".metric-steps");
1921
+ const stepsMetricValEl = stepMetricContainerEl?.querySelector(".metric-value");
1922
+ if (stepsMetricValEl) {
1923
+ let genSteps = group.querySelectorAll('.process-step[data-log-type="agent"]').length;
1924
+ genSteps -= 1; // don't count response as step
1925
+ stepsMetricValEl.textContent = genSteps.toString();
1926
+ if (genSteps <= 0) stepMetricContainerEl.classList.add("display-none"); // hide when no steps
1927
+ else stepMetricContainerEl.classList.remove("display-none");
1928
}
1929
1930
// Update time metric
1931
+ const timeMetricContainerEl = metricsEl?.querySelector(".metric-time");
1932
const timeMetricEl = metricsEl?.querySelector(".metric-time .metric-value");
1933
const startTimestamp = group.getAttribute("data-start-timestamp");
1934
if (timeMetricEl && startTimestamp) {
@@ -1928,6 +1936,14 @@ function updateProcessGroupHeader(group) {
1936
const hours = String(date.getHours()).padStart(2, "0");
1937
const minutes = String(date.getMinutes()).padStart(2, "0");
1938
timeMetricEl.textContent = `${hours}:${minutes}`;
1939
+ if (timeMetricContainerEl) {
1940
+ const fullDateTime = date.toLocaleString(undefined, {
1941
+ dateStyle: "medium",
1942
+ timeStyle: "short",
1943
+ });
1944
+ timeMetricContainerEl.title = timeMetricContainerEl.dataset.bsOriginalTitle =
1945
+ fullDateTime;
1946
+ }
1947
}
1948
1949
const firstTimestampMs = parseInt(
@@ -1946,39 +1962,27 @@ function updateProcessGroupHeader(group) {
1962
lastTimestampMs > 0 &&
1963
formatDuration(Math.max(0, lastTimestampMs - firstTimestampMs));
1964
1949
- const durationMetricEl =
1950
- durationText &&
1951
- (() => {
1952
- const container = ensureChild(
1953
- metricsEl,
1954
- ".metric-duration",
1955
- "span",
1956
- "metric-duration",
1957
- );
1958
- container.title = "Duration";
1959
- const icon = ensureChild(
1960
- container,
1961
- ".material-symbols-outlined",
1962
- "span",
1963
- "material-symbols-outlined",
1964
- );
1965
- icon.textContent = "timer";
1966
- return ensureChild(container, ".metric-value", "span", "metric-value");
1967
- })();
1968
- durationMetricEl && (durationMetricEl.textContent = durationText);
1965
+ const durationMetricContainerEl = metricsEl?.querySelector(".metric-duration");
1966
+ const durationMetricValEl = durationMetricContainerEl?.querySelector(".metric-value");
1967
+ if (durationMetricContainerEl && durationMetricValEl && durationText) {
1968
+ durationMetricValEl.textContent = durationText;
1969
+ durationMetricContainerEl.classList.remove("display-none");
1970
+ } else if (durationMetricContainerEl) {
1971
+ durationMetricContainerEl.classList.add("display-none");
1972
+ }
1973
1974
if (notificationsEl) {
1971
- const counts = { warning: 0, info: 0, hint: 0 };
1975
+ const counts = { warning: 0, info: 0 };
1976
steps.forEach((step) => {
1973
- const stepType = step.getAttribute("data-type");
1977
+ const stepType = step.getAttribute("data-log-type");
1978
if (Object.prototype.hasOwnProperty.call(counts, stepType)) {
1979
counts[stepType] += 1;
1980
}
1981
});
1982
1979
- const totalNotifications = counts.warning + counts.info + counts.hint;
1983
+ const totalNotifications = counts.warning + counts.info;
1984
const countEl = notificationsEl.querySelector(".metric-value");
1981
- notificationsEl.classList.remove("status-wrn", "status-inf", "status-hnt");
1985
+ notificationsEl.classList.remove("status-wrn", "status-inf");
1986
1987
if (totalNotifications > 0) {
1988
if (countEl) {
@@ -1988,11 +1992,9 @@ function updateProcessGroupHeader(group) {
1992
notificationsEl.classList.add("status-wrn");
1993
} else if (counts.info > 0) {
1994
notificationsEl.classList.add("status-inf");
1991
- } else {
1992
- notificationsEl.classList.add("status-hnt");
1995
}
1996
notificationsEl.hidden = false;
1995
- notificationsEl.title = `Warnings: ${counts.warning}, Info: ${counts.info}, Hints: ${counts.hint}`;
1997
+ notificationsEl.title = `Warnings: ${counts.warning}, Info: ${counts.info}`;
1998
} else {
1999
notificationsEl.hidden = true;
2000
}