]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/blob
34362e026fbdd044c854202e2fe3fd4e90f85a9a
[pkg/ggml/sources/llama.cpp] /
1 <script lang="ts">
2 import { Clock, Gauge, WholeWord, BookOpenText, Sparkles, Wrench, Layers } from '@lucide/svelte';
3 import { ChatMessageStatisticsBadge } from '$lib/components/app';
4 import * as Tooltip from '$lib/components/ui/tooltip';
5 import { ChatMessageStatsView } from '$lib/enums';
6 import type { ChatMessageAgenticTimings } from '$lib/types/chat';
7 import { formatPerformanceTime } from '$lib/utils';
8 import { MS_PER_SECOND, DEFAULT_PERFORMANCE_TIME } from '$lib/constants';
9
10 interface Props {
11 predictedTokens?: number;
12 predictedMs?: number;
13 promptTokens?: number;
14 promptMs?: number;
15 isLive?: boolean;
16 isProcessingPrompt?: boolean;
17 initialView?: ChatMessageStatsView;
18 agenticTimings?: ChatMessageAgenticTimings;
19 onActiveViewChange?: (view: ChatMessageStatsView) => void;
20 hideSummary?: boolean;
21 }
22
23 let {
24 predictedTokens,
25 predictedMs,
26 promptTokens,
27 promptMs,
28 isLive = false,
29 isProcessingPrompt = false,
30 initialView = ChatMessageStatsView.GENERATION,
31 agenticTimings,
32 onActiveViewChange,
33 hideSummary = false
34 }: Props = $props();
35
36 let activeView: ChatMessageStatsView = $derived(initialView);
37 let hasAutoSwitchedToGeneration = $state(false);
38
39 $effect(() => {
40 onActiveViewChange?.(activeView);
41 });
42
43 // In live mode: auto-switch to GENERATION tab when prompt processing completes
44 $effect(() => {
45 if (isLive) {
46 // Auto-switch to generation tab only when prompt processing is done (once)
47 if (
48 !hasAutoSwitchedToGeneration &&
49 !isProcessingPrompt &&
50 predictedTokens &&
51 predictedTokens > 0
52 ) {
53 activeView = ChatMessageStatsView.GENERATION;
54 hasAutoSwitchedToGeneration = true;
55 } else if (!hasAutoSwitchedToGeneration) {
56 // Stay on READING while prompt is still being processed
57 activeView = ChatMessageStatsView.READING;
58 }
59 }
60 });
61
62 let hasGenerationStats = $derived(
63 predictedTokens !== undefined &&
64 predictedTokens > 0 &&
65 predictedMs !== undefined &&
66 predictedMs > 0
67 );
68
69 let tokensPerSecond = $derived(
70 hasGenerationStats ? (predictedTokens! / predictedMs!) * MS_PER_SECOND : 0
71 );
72 let formattedTime = $derived(
73 predictedMs !== undefined ? formatPerformanceTime(predictedMs) : DEFAULT_PERFORMANCE_TIME
74 );
75
76 let promptTokensPerSecond = $derived(
77 promptTokens !== undefined && promptMs !== undefined && promptMs > 0
78 ? (promptTokens / promptMs) * MS_PER_SECOND
79 : undefined
80 );
81
82 let formattedPromptTime = $derived(
83 promptMs !== undefined ? formatPerformanceTime(promptMs) : undefined
84 );
85
86 let hasPromptStats = $derived(
87 promptTokens !== undefined &&
88 promptMs !== undefined &&
89 promptTokensPerSecond !== undefined &&
90 formattedPromptTime !== undefined
91 );
92
93 // In live mode, generation tab is disabled until we have generation stats
94 let isGenerationDisabled = $derived(isLive && !hasGenerationStats);
95
96 let hasAgenticStats = $derived(agenticTimings !== undefined && agenticTimings.toolCallsCount > 0);
97
98 let agenticToolsPerSecond = $derived(
99 hasAgenticStats && agenticTimings!.toolsMs > 0
100 ? (agenticTimings!.toolCallsCount / agenticTimings!.toolsMs) * MS_PER_SECOND
101 : 0
102 );
103
104 let formattedAgenticToolsTime = $derived(
105 hasAgenticStats ? formatPerformanceTime(agenticTimings!.toolsMs) : DEFAULT_PERFORMANCE_TIME
106 );
107
108 let agenticTotalTimeMs = $derived(
109 hasAgenticStats
110 ? agenticTimings!.toolsMs + agenticTimings!.llm.predicted_ms + agenticTimings!.llm.prompt_ms
111 : 0
112 );
113
114 let formattedAgenticTotalTime = $derived(formatPerformanceTime(agenticTotalTimeMs));
115 </script>
116
117 <div class="inline-flex items-center text-xs text-muted-foreground">
118 <div class="inline-flex items-center rounded-sm bg-muted-foreground/15 p-0.5">
119 {#if hasPromptStats || isLive}
120 <Tooltip.Root>
121 <Tooltip.Trigger>
122 <button
123 type="button"
124 class="inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors {activeView ===
125 ChatMessageStatsView.READING
126 ? 'bg-background text-foreground shadow-sm'
127 : 'hover:text-foreground'}"
128 onclick={() => (activeView = ChatMessageStatsView.READING)}
129 >
130 <BookOpenText class="h-3 w-3" />
131
132 <span class="sr-only">Reading</span>
133 </button>
134 </Tooltip.Trigger>
135
136 <Tooltip.Content>
137 <p>Reading (prompt processing)</p>
138 </Tooltip.Content>
139 </Tooltip.Root>
140 {/if}
141 <Tooltip.Root>
142 <Tooltip.Trigger>
143 <button
144 type="button"
145 class="inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors {activeView ===
146 ChatMessageStatsView.GENERATION
147 ? 'bg-background text-foreground shadow-sm'
148 : isGenerationDisabled
149 ? 'cursor-not-allowed opacity-40'
150 : 'hover:text-foreground'}"
151 onclick={() => !isGenerationDisabled && (activeView = ChatMessageStatsView.GENERATION)}
152 disabled={isGenerationDisabled}
153 >
154 <Sparkles class="h-3 w-3" />
155
156 <span class="sr-only">Generation</span>
157 </button>
158 </Tooltip.Trigger>
159
160 <Tooltip.Content>
161 <p>
162 {isGenerationDisabled
163 ? 'Generation (waiting for tokens...)'
164 : 'Generation (token output)'}
165 </p>
166 </Tooltip.Content>
167 </Tooltip.Root>
168
169 {#if hasAgenticStats}
170 <Tooltip.Root>
171 <Tooltip.Trigger>
172 <button
173 type="button"
174 class="inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors {activeView ===
175 ChatMessageStatsView.TOOLS
176 ? 'bg-background text-foreground shadow-sm'
177 : 'hover:text-foreground'}"
178 onclick={() => (activeView = ChatMessageStatsView.TOOLS)}
179 >
180 <Wrench class="h-3 w-3" />
181
182 <span class="sr-only">Tools</span>
183 </button>
184 </Tooltip.Trigger>
185
186 <Tooltip.Content>
187 <p>Tool calls</p>
188 </Tooltip.Content>
189 </Tooltip.Root>
190
191 {#if !hideSummary}
192 <Tooltip.Root>
193 <Tooltip.Trigger>
194 <button
195 type="button"
196 class="inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors {activeView ===
197 ChatMessageStatsView.SUMMARY
198 ? 'bg-background text-foreground shadow-sm'
199 : 'hover:text-foreground'}"
200 onclick={() => (activeView = ChatMessageStatsView.SUMMARY)}
201 >
202 <Layers class="h-3 w-3" />
203
204 <span class="sr-only">Summary</span>
205 </button>
206 </Tooltip.Trigger>
207
208 <Tooltip.Content>
209 <p>Agentic summary</p>
210 </Tooltip.Content>
211 </Tooltip.Root>
212 {/if}
213 {/if}
214 </div>
215
216 <div class="flex items-center gap-1 px-2">
217 {#if activeView === ChatMessageStatsView.GENERATION && hasGenerationStats}
218 <ChatMessageStatisticsBadge
219 class="bg-transparent"
220 icon={WholeWord}
221 value="{predictedTokens?.toLocaleString()} tokens"
222 tooltipLabel="Generated tokens"
223 />
224
225 <ChatMessageStatisticsBadge
226 class="bg-transparent"
227 icon={Clock}
228 value={formattedTime}
229 tooltipLabel="Generation time"
230 />
231
232 <ChatMessageStatisticsBadge
233 class="bg-transparent"
234 icon={Gauge}
235 value="{tokensPerSecond.toFixed(2)} t/s"
236 tooltipLabel="Generation speed"
237 />
238 {:else if activeView === ChatMessageStatsView.TOOLS && hasAgenticStats}
239 <ChatMessageStatisticsBadge
240 class="bg-transparent"
241 icon={Wrench}
242 value="{agenticTimings!.toolCallsCount} calls"
243 tooltipLabel="Tool calls executed"
244 />
245
246 <ChatMessageStatisticsBadge
247 class="bg-transparent"
248 icon={Clock}
249 value={formattedAgenticToolsTime}
250 tooltipLabel="Tool execution time"
251 />
252
253 <ChatMessageStatisticsBadge
254 class="bg-transparent"
255 icon={Gauge}
256 value="{agenticToolsPerSecond.toFixed(2)} calls/s"
257 tooltipLabel="Tool execution rate"
258 />
259 {:else if activeView === ChatMessageStatsView.SUMMARY && hasAgenticStats}
260 <ChatMessageStatisticsBadge
261 class="bg-transparent"
262 icon={Layers}
263 value="{agenticTimings!.turns} turns"
264 tooltipLabel="Agentic turns (LLM calls)"
265 />
266
267 <ChatMessageStatisticsBadge
268 class="bg-transparent"
269 icon={WholeWord}
270 value="{agenticTimings!.llm.predicted_n.toLocaleString()} tokens"
271 tooltipLabel="Total tokens generated"
272 />
273
274 <ChatMessageStatisticsBadge
275 class="bg-transparent"
276 icon={Clock}
277 value={formattedAgenticTotalTime}
278 tooltipLabel="Total time (LLM + tools)"
279 />
280 {:else if hasPromptStats}
281 <ChatMessageStatisticsBadge
282 class="bg-transparent"
283 icon={WholeWord}
284 value="{promptTokens} tokens"
285 tooltipLabel="Prompt tokens"
286 />
287
288 <ChatMessageStatisticsBadge
289 class="bg-transparent"
290 icon={Clock}
291 value={formattedPromptTime ?? '0s'}
292 tooltipLabel="Prompt processing time"
293 />
294
295 <ChatMessageStatisticsBadge
296 class="bg-transparent"
297 icon={Gauge}
298 value="{promptTokensPerSecond!.toFixed(2)} tokens/s"
299 tooltipLabel="Prompt processing speed"
300 />
301 {/if}
302 </div>
303 </div>