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 }