import { useProcessingState } from '$lib/hooks/use-processing-state.svelte';
import { isLoading } from '$lib/stores/chat.svelte';
import { fade } from 'svelte/transition';
- import { Check, Copy, Package, X } from '@lucide/svelte';
+ import {
+ Check,
+ Copy,
+ Package,
+ X,
+ Gauge,
+ Clock,
+ WholeWord,
+ ChartNoAxesColumn
+ } from '@lucide/svelte';
import { Button } from '$lib/components/ui/button';
import { Checkbox } from '$lib/components/ui/checkbox';
import { INPUT_CLASSES } from '$lib/constants/input-classes';
</div>
{/if}
- {#if displayedModel()}
- <span class="mt-6 mb-4 inline-flex items-center gap-1 text-xs text-muted-foreground">
- <Package class="h-3.5 w-3.5" />
+ <div class="info my-6 grid gap-4">
+ {#if displayedModel()}
+ <span class="inline-flex items-center gap-2 text-xs text-muted-foreground">
+ <span class="inline-flex items-center gap-1">
+ <Package class="h-3.5 w-3.5" />
- <span>Model used:</span>
+ <span>Model used:</span>
+ </span>
- <button
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"
- onclick={handleCopyModel}
- >
- {displayedModel()}
+ <button
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"
+ onclick={handleCopyModel}
+ >
+ {displayedModel()}
- <Copy class="ml-1 h-3 w-3 " />
- </button>
- </span>
- {/if}
+ <Copy class="ml-1 h-3 w-3 " />
+ </button>
+ </span>
+ {/if}
+
+ {#if currentConfig.showMessageStats && message.timings && message.timings.predicted_n && message.timings.predicted_ms}
+ {@const tokensPerSecond = (message.timings.predicted_n / message.timings.predicted_ms) * 1000}
+ <span class="inline-flex items-center gap-2 text-xs text-muted-foreground">
+ <span class="inline-flex items-center gap-1">
+ <ChartNoAxesColumn class="h-3.5 w-3.5" />
+
+ <span>Statistics:</span>
+ </span>
+
+ <div class="inline-flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
+ <span
+ class="inline-flex items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"
+ >
+ <Gauge class="h-3 w-3" />
+ {tokensPerSecond.toFixed(2)} tokens/s
+ </span>
+ <span
+ class="inline-flex items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"
+ >
+ <WholeWord class="h-3 w-3" />
+ {message.timings.predicted_n} tokens
+ </span>
+ <span
+ class="inline-flex items-center gap-1 rounded-sm bg-muted-foreground/15 px-1.5 py-0.75"
+ >
+ <Clock class="h-3 w-3" />
+ {(message.timings.predicted_ms / 1000).toFixed(2)}s
+ </span>
+ </div>
+ </span>
+ {/if}
+ </div>
{#if message.timestamp && !isEditing}
<ChatMessageActions
showThoughtInProgress: false,
disableReasoningFormat: false,
keepStatsVisible: false,
+ showMessageStats: true,
askForTitleConfirmation: false,
pasteLongTextToFileLen: 2500,
pdfAsImage: false,
disableReasoningFormat:
'Show raw LLM output without backend parsing and frontend Markdown rendering to inspect streaming across different models.',
keepStatsVisible: 'Keep processing statistics visible after generation finishes.',
+ showMessageStats:
+ 'Display generation statistics (tokens/second, token count, duration) below each assistant message.',
askForTitleConfirmation:
'Ask for confirmation before automatically changing conversation title when editing the first message.',
pdfAsImage: 'Parse PDF as image instead of text (requires vision-capable model).',