Feat: Add Token Stats and Cost Savings dashboard card and backend tracking

This commit is contained in:
Hitonabi
2026-06-26 13:48:32 +02:00
parent 311f4d7b68
commit 2e4cddc840
9 changed files with 546 additions and 391 deletions
+62 -3
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react"
import { Bot, Cpu, ExternalLink, Brain, Layers, Plus, ShieldAlert, X, Power, Shield, Download, RefreshCw, Check } from "lucide-react"
import { Bot, Cpu, ExternalLink, Brain, Layers, Plus, ShieldAlert, X, Power, Shield, Download, RefreshCw, Check, Coins } from "lucide-react"
import { api, type SystemStatus, type AgentStatus, type ModelInfo, type Memory, type UpdatesResp, type Job } from "@/lib/api"
import { cn, resolveExternalUrl } from "@/lib/utils"
import { CustomDialog } from "@/components/CustomDialog"
@@ -43,6 +43,13 @@ export function DashboardView() {
const [memories, setMemories] = useState<Memory[]>([])
const [updates, setUpdates] = useState<UpdatesResp | null>(null)
const [jobs, setJobs] = useState<Job[]>([])
const [tokenStats, setTokenStats] = useState<{
prompt_tokens: number
completion_tokens: number
total_tokens: number
saved_usd: number
saved_eur: number
} | null>(null)
// Sudo & Action states
const [msg, setMsg] = useState("")
@@ -123,6 +130,7 @@ export function DashboardView() {
api<Memory[]>("/api/memory?category=").then((d) => setMemories(d.slice(0, 3))).catch(() => {})
api<UpdatesResp>("/api/maintenance/updates").then(setUpdates).catch(() => {})
api<{ jobs: Job[] }>("/api/jobs").then((d) => setJobs(d.jobs || [])).catch(() => {})
api<any>("/api/system/token-stats").then(setTokenStats).catch(() => {})
}
useEffect(() => {
@@ -495,8 +503,8 @@ export function DashboardView() {
</div>
</div>
{/* Bottom Grid: Agent, Roles & Memory (3 Columns) */}
<div className="grid gap-6 md:grid-cols-3">
{/* Bottom Grid: Agent, Roles, Memory & Token Stats (4 Columns on Desktop) */}
<div className="grid gap-6 md:grid-cols-2 xl:grid-cols-4">
{/* Card 3: Hermes Agent Status */}
<div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-5 shadow-lg shadow-black/15 flex flex-col justify-between">
<div>
@@ -700,6 +708,57 @@ export function DashboardView() {
Steht allen Clients per MCP zur Verfügung.
</div>
</div>
{/* Card 6: Effizienz & Ersparnis (Token-Stats & Ersparnis) */}
<div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-5 shadow-lg shadow-black/15 flex flex-col justify-between">
<div>
<div className="flex items-center gap-2 mb-4">
<Coins className="h-4.5 w-4.5 text-primary animate-pulse" />
<h2 className="text-sm font-semibold tracking-wide uppercase text-foreground">Effizienz &amp; Ersparnis</h2>
</div>
{tokenStats ? (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-2.5">
<div className="p-3 bg-background/20 rounded-xl border border-border/40 text-left">
<div className="text-[8px] uppercase tracking-wider font-semibold text-muted-foreground/60">Geld gespart</div>
<div className="text-base font-bold text-emerald-400 mt-0.5 tracking-tight font-space">
{tokenStats.saved_eur.toLocaleString("de-DE", { minimumFractionDigits: 2 })}
</div>
<div className="text-[8px] text-muted-foreground/80 mt-0.5 font-mono">
({tokenStats.saved_usd.toLocaleString("en-US", { minimumFractionDigits: 2 })} $)
</div>
</div>
<div className="p-3 bg-background/20 rounded-xl border border-border/40 text-left">
<div className="text-[8px] uppercase tracking-wider font-semibold text-muted-foreground/60">Gesamt-Tokens</div>
<div className="text-base font-bold text-primary mt-0.5 tracking-tight font-space">
{tokenStats.total_tokens.toLocaleString("de-DE")}
</div>
<div className="text-[8px] text-muted-foreground/80 mt-0.5 font-mono">
(Lokale Inferenz)
</div>
</div>
</div>
<div className="space-y-2 border-t border-border/20 pt-3 text-[10px] text-muted-foreground">
<div className="flex justify-between items-center font-mono">
<span>Input (Prompts):</span>
<span className="font-semibold text-foreground">{tokenStats.prompt_tokens.toLocaleString("de-DE")} tkn</span>
</div>
<div className="flex justify-between items-center font-mono">
<span>Output (Antworten):</span>
<span className="font-semibold text-foreground">{tokenStats.completion_tokens.toLocaleString("de-DE")} tkn</span>
</div>
</div>
</div>
) : (
<div className="h-28 flex items-center justify-center text-xs text-muted-foreground">Lade Statistiken</div>
)}
</div>
<div className="mt-4 text-[9px] text-muted-foreground/70 border-t border-border/30 pt-2.5 leading-normal">
Berechnet im Vergleich zu Cloud-APIs (Ø 3,00 $ / 15,00 $ pro 1M tkn).
</div>
</div>
</div>
{agent && showBrainSelect && (