Show per-member token usage badges on panel bubbles

memberUsage was already populated for CLI members (from the result event)
and Anthropic-API members (via the tool loop), but it was never written to
the panel_member_done SSE event or the persisted content JSON, so the
frontend had no token data to render.

Add the token fields (input/output/cacheRead/cacheCreation) to both the
SSE event and the persisted JSON, and render them via a shared
buildPanelMemberMeta helper used by both the live stream and history, so
the badge survives F5. OpenAI members (no per-turn tokens), error members,
and pre-existing history without token fields fall through to no badge.

The badge is display-only; aggregation into the worker/chain total via
seedUsage is unchanged, so there is no double counting and no DB migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.8 committed 2026-06-19 17:47:49 +02:00
1 parent 17ba4840dd
commit 8e5a961e94
4 files changed
+50 -2

No files matched your search

+20
View File
@@ -324,6 +324,24 @@ function finishPanelMemberTool(data) {
scrollDown();
}
// Baut ein kompaktes Token-Badge für eine Panel-Member-Bubble (live + History, gleiche Quelle).
// Gibt ein DOM-Element zurück oder null, wenn keine Token-Daten vorliegen: OpenAI-Member
// (liefert keine per-turn Tokens), Fehler-Member oder Alt-History ohne Token-Felder → kein Badge.
function buildPanelMemberMeta(d) {
if (!d) return null;
var inT = d.inputTokens || 0, outT = d.outputTokens || 0;
var cacheR = d.cacheReadTokens || 0, cacheC = d.cacheCreationTokens || 0;
if (!inT && !outT && !cacheR && !cacheC) return null;
var badges = ['Tokens: ' + inT + ' / ' + outT];
if (cacheR > 0 || cacheC > 0) badges.push('Cache: ' + cacheR + ' / ' + cacheC);
var meta = document.createElement('div');
meta.className = 'panel-member-meta';
meta.innerHTML = badges.map(function (b) {
return '<span class="chain-meta-badge">' + esc(b) + '</span>';
}).join('');
return meta;
}
function finalizePanelMember(data) {
var bubble = _panelGetBubble(data.index);
if (!bubble) return;
@@ -348,6 +366,8 @@ function finalizePanelMember(data) {
}
bubble._segText.innerHTML = renderMarkdown(text);
}
var meta = buildPanelMemberMeta(data);
if (meta) bubble.appendChild(meta);
delete _panelBubbles[_panelKey(data.index, _panelActiveLayer[data.index])];
scrollDown();
}