From c77297af72c016f52c934887003fc5795e5b773b Mon Sep 17 00:00:00 2001 From: seb Date: Tue, 20 Oct 2026 22:17:27 +0200 Subject: [PATCH] u --- src/components/chat/ChatFlowList.jsx | 106 +++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/src/components/chat/ChatFlowList.jsx b/src/components/chat/ChatFlowList.jsx index 69589cf..089931d 100644 --- a/src/components/chat/ChatFlowList.jsx +++ b/src/components/chat/ChatFlowList.jsx @@ -73,7 +73,7 @@ function parseMessagePayload(raw) { return { content: raw }; } -function formatUsageText(usage) { +function formatUsageText(usage, prefix = '') { if (!usage) return ''; const input = Number(usage.input ?? usage.prompt_tokens ?? 0); const output = Number(usage.output ?? usage.completion_tokens ?? 0); @@ -81,7 +81,40 @@ function formatUsageText(usage) { const cost = typeof usage.cost === 'number' ? ` · $${usage.cost.toFixed(6)}` : ''; const fmt = (n) => n.toLocaleString('de-DE'); const cachedPart = cached > 0 ? ` / ${fmt(cached)} Cache` : ''; - return `${fmt(input)} Prompt${cachedPart} · ${fmt(output)} Antwort${cost}`; + const pfx = prefix ? `${prefix} ` : ''; + return `${pfx}${fmt(input)} Prompt${cachedPart} · ${fmt(output)} Antwort${cost}`; +} + +export function aggregateUsage(turns = []) { + if (!Array.isArray(turns) || turns.length === 0) return null; + let hasUsage = false; + let totalInput = 0; + let totalOutput = 0; + let totalCached = 0; + let totalCost = 0; + let hasCost = false; + + for (const turn of turns) { + const u = turn?.usage; + if (!u) continue; + hasUsage = true; + totalInput += Number(u.input ?? u.prompt_tokens ?? 0); + totalOutput += Number(u.output ?? u.completion_tokens ?? 0); + totalCached += Number(u.cached ?? 0); + if (typeof u.cost === 'number' && !Number.isNaN(u.cost)) { + totalCost += u.cost; + hasCost = true; + } + } + + if (!hasUsage) return null; + + return { + input: totalInput, + output: totalOutput, + cached: totalCached, + cost: hasCost ? totalCost : undefined, + }; } function formatToolTooltip(content, result, parameters, prefix, error) { @@ -455,12 +488,12 @@ function tryExtractObject(val) { const trimmed = val.trim(); try { return JSON.parse(trimmed); - } catch {} + } catch { } const match = trimmed.match(/\{[\s\S]*\}/); if (match) { try { return JSON.parse(match[0]); - } catch {} + } catch { } } } return null; @@ -652,11 +685,11 @@ function WorkIconView({ tool }) { return iconElement; } -function UsageChipView({ usage }) { - const text = formatUsageText(usage); +function UsageChipView({ usage, prefix = '', isSummary = false, title = '' }) { + const text = formatUsageText(usage, prefix); if (!text) return null; - return ( + const chip = ( ); + + if (title) { + return ( + + {chip} + + ); + } + + return chip; } export function TurnRow({ turn, isLive = false }) { @@ -960,11 +1006,22 @@ export function normalizeChatMessages(rawMessages = []) { function MessageRow({ msg, showTime = true }) { // If the message has structured turns, render each turn if (msg.turns && Array.isArray(msg.turns) && msg.turns.length > 0) { + const summaryUsage = aggregateUsage(msg.turns); + const turnCount = msg.turns.length; + return ( {msg.turns.map((t, idx) => ( ))} + {summaryUsage ? ( + + + + ) : null} ); } @@ -1198,11 +1255,32 @@ export default class ChatFlowList extends PureComponent { })} {/* Live streaming turns */} - {activeTurns && activeTurns.length > 0 - ? activeTurns.map((turn, idx) => ( - - )) - : null} + {activeTurns && activeTurns.length > 0 ? ( + <> + {activeTurns.map((turn, idx) => ( + + ))} + {(() => { + const activeSummary = aggregateUsage(activeTurns); + if (!activeSummary) return null; + const count = activeTurns.length; + const prefix = count > 1 ? `Summe (${count} Turns):` : 'Summe:'; + const tooltipTitle = count > 1 + ? `Zusammenfassung von ${count} LLM-Turns` + : 'Zusammenfassung des LLM-Turns'; + return ( + + + + ); + })()} + + ) : null}