diff --git a/src/routes/model/$modelId.tsx b/src/routes/model/$modelId.tsx index d2b58c4..02cc4f6 100644 --- a/src/routes/model/$modelId.tsx +++ b/src/routes/model/$modelId.tsx @@ -7,6 +7,7 @@ import { Gauge, Info, Layers, + Wrench, } from "lucide-react"; import { useCallback, useEffect, useMemo } from "react"; import CategoryTabs from "#/components/CategoryTabs"; @@ -130,15 +131,26 @@ interface StatCard { } function ModelStats({ model }: { model: ModelResult }) { - const items: StatCard[] = []; - + const totalItems: StatCard[] = []; + const avgItems: StatCard[] = []; + + // Compute average tool calls + const totalToolCalls = + model.questionDetails?.reduce( + (sum, q) => sum + (q.toolCallCount ?? 0), + 0, + ) ?? 0; + const avgToolCalls = + model.totalQuestions > 0 ? totalToolCalls / model.totalQuestions : 0; + + // Total stats if (model.totalTokens > 0) { const parts: string[] = []; if (model.totalPromptTokens > 0) parts.push(`Input: ${model.totalPromptTokens.toLocaleString()}`); if (model.totalCompletionTokens > 0) parts.push(`Output: ${model.totalCompletionTokens.toLocaleString()}`); - items.push({ + totalItems.push({ icon: Layers, label: "Total tokens", value: model.totalTokens.toLocaleString(), @@ -146,53 +158,127 @@ function ModelStats({ model }: { model: ModelResult }) { }); } if (model.totalDurationMs > 0) { - items.push({ + totalItems.push({ icon: Clock, label: "Total duration", value: formatDuration(model.totalDurationMs), }); } + if (model.totalCost > 0) { + totalItems.push({ + icon: Coins, + label: "Total cost", + value: `$${model.totalCost.toFixed(4)}`, + }); + } + if (totalToolCalls > 0) { + totalItems.push({ + icon: Wrench, + label: "Total tool calls", + value: totalToolCalls.toLocaleString(), + }); + } + + // Average stats per question + if (model.totalTokens > 0 && model.totalQuestions > 0) { + const avgTokens = Math.round(model.totalTokens / model.totalQuestions); + const avgPrompt = Math.round( + model.totalPromptTokens / model.totalQuestions, + ); + const avgCompletion = Math.round( + model.totalCompletionTokens / model.totalQuestions, + ); + avgItems.push({ + icon: Layers, + label: "Avg tokens/question", + value: avgTokens.toLocaleString(), + tooltip: `Input: ${avgPrompt.toLocaleString()}\nOutput: ${avgCompletion.toLocaleString()}`, + }); + } + if (model.totalDurationMs > 0 && model.totalQuestions > 0) { + const avgDurationMs = Math.round( + model.totalDurationMs / model.totalQuestions, + ); + avgItems.push({ + icon: Clock, + label: "Avg duration/question", + value: formatDuration(avgDurationMs), + }); + } if (model.averageTokensPerSecond > 0) { - items.push({ + avgItems.push({ icon: Gauge, label: "Avg speed", value: `${model.averageTokensPerSecond.toFixed(1)} tok/s`, }); } - if (model.totalCost > 0) { - items.push({ + if (model.totalCost > 0 && model.totalQuestions > 0) { + const avgCost = model.totalCost / model.totalQuestions; + avgItems.push({ icon: Coins, - label: "Total cost", - value: `$ ${model.totalCost.toFixed(4)}`, + label: "Avg cost/question", + value: avgCost < 0.0001 ? "<$0.0001" : `$${avgCost.toFixed(4)}`, + }); + } + if (avgToolCalls > 0) { + avgItems.push({ + icon: Wrench, + label: "Avg tool calls/question", + value: avgToolCalls.toFixed(1), }); } - if (items.length === 0) return null; + if (totalItems.length === 0 && avgItems.length === 0) return null; + + const renderCard = (item: StatCard) => ( +