diff --git a/src/components/chat/ChatFlowList.jsx b/src/components/chat/ChatFlowList.jsx index 089931d..ebfc607 100644 --- a/src/components/chat/ChatFlowList.jsx +++ b/src/components/chat/ChatFlowList.jsx @@ -27,6 +27,7 @@ import CalculateIcon from '@mui/icons-material/Calculate'; import QrCodeScannerIcon from '@mui/icons-material/QrCodeScanner'; import DirectionsIcon from '@mui/icons-material/Directions'; import EditNoteIcon from '@mui/icons-material/EditNote'; +import PaidRoundedIcon from '@mui/icons-material/PaidRounded'; import Button from '@mui/material/Button'; import { enqueueSnackbar } from 'notistack'; import { deliveryEditorAutomat, setLastDeliveryPath } from '../../automats'; @@ -73,7 +74,7 @@ function parseMessagePayload(raw) { return { content: raw }; } -function formatUsageText(usage, prefix = '') { +function formatUsageText(usage) { if (!usage) return ''; const input = Number(usage.input ?? usage.prompt_tokens ?? 0); const output = Number(usage.output ?? usage.completion_tokens ?? 0); @@ -81,8 +82,28 @@ function formatUsageText(usage, prefix = '') { const cost = typeof usage.cost === 'number' ? ` · $${usage.cost.toFixed(6)}` : ''; const fmt = (n) => n.toLocaleString('de-DE'); const cachedPart = cached > 0 ? ` / ${fmt(cached)} Cache` : ''; - const pfx = prefix ? `${prefix} ` : ''; - return `${pfx}${fmt(input)} Prompt${cachedPart} · ${fmt(output)} Antwort${cost}`; + return `${fmt(input)} Prompt${cachedPart} · ${fmt(output)} Antwort${cost}`; +} + +function formatUsageTooltip(usage) { + if (!usage) return ''; + const input = Number(usage.input ?? usage.prompt_tokens ?? 0); + const output = Number(usage.output ?? usage.completion_tokens ?? 0); + const cached = Number(usage.cached ?? 0); + const cost = typeof usage.cost === 'number' ? `$${usage.cost.toFixed(6)}` : null; + const fmt = (n) => n.toLocaleString('de-DE'); + + const lines = [ + `Prompt: ${fmt(input)} Tokens`, + ]; + if (cached > 0) { + lines.push(`Cache: ${fmt(cached)} Tokens`); + } + lines.push(`Antwort: ${fmt(output)} Tokens`); + if (cost) { + lines.push(`Kosten: ${cost}`); + } + return lines.join('\n'); } export function aggregateUsage(turns = []) { @@ -685,11 +706,11 @@ function WorkIconView({ tool }) { return iconElement; } -function UsageChipView({ usage, prefix = '', isSummary = false, title = '' }) { - const text = formatUsageText(usage, prefix); +function UsageChipView({ usage, isSummary = false }) { + const text = formatUsageText(usage); if (!text) return null; - const chip = ( + return ( ); +} - if (title) { - return ( - - {chip} - - ); - } +function UsageIconView({ usage }) { + if (!usage) return null; + const tooltipText = formatUsageTooltip(usage); + if (!tooltipText) return null; - return chip; + return ( + + {tooltipText} + + } + > + + + + + ); } export function TurnRow({ turn, isLive = false }) { @@ -732,8 +786,9 @@ export function TurnRow({ turn, isLive = false }) { ); const hasTools = Boolean(turn.tools && turn.tools.length > 0); const hasContent = Boolean(turn.content); - const showHandshake = !hasReasonings && !hasTools && !hasContent; - const showStatusLine = showHandshake || hasReasonings || hasTools; + const hasUsage = Boolean(turn.usage); + const showHandshake = !hasReasonings && !hasTools && !hasContent && !hasUsage; + const showStatusLine = showHandshake || hasReasonings || hasTools || hasUsage; return ( ( ))} + + {hasUsage ? ( + + ) : null} ) : null} @@ -794,13 +853,6 @@ export function TurnRow({ turn, isLive = false }) { ) : null} - - {/* Pricing / usage is always on a new line */} - {turn.usage ? ( - - - - ) : null} ); } @@ -1263,18 +1315,11 @@ export default class ChatFlowList extends PureComponent { {(() => { 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 ( );