feat(costs): Stufe 2 — geschätzte Quellen (lokaler Strom, Cartesia, Notruf)

Lokales LLM: Call-Dauer gemessen -> Stromkosten je Job ((max-idle)*load*n/
3.6e6*Preis(EUR/kWh)*usd_per_eur), Kategorie llm_local. Cartesia-TTS: Zeichen x
cartesia_usd_per_char (tts). Notruf: pro Webhook-Alarm 0,20€*usd_per_eur
(alert), nur Doku, nie Sperre. Helfer in costs.py; neue Settings (GPU-Watt/Last/
Strompreis/usd_per_eur/cartesia/alert). Statistik: "exakt vs. geschätzt"
(Tooltip + Summenzeile, Kategorien tts/alert/llm_local). Tests: 318.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-30 13:27:50 +02:00
commit d45e2c9ae3
8 changed files with 85 additions and 5 deletions

View file

@ -1851,6 +1851,7 @@ async function loadMetrics() {
const totalReq = data.reduce((s, u) => s + (u.total_requests || 0), 0);
const totalWS = data.reduce((s, u) => s + (u.total_web_searches || 0), 0);
const totalCost = data.reduce((s, u) => s + (u.month_cost_usd || 0), 0);
const totalEstimated = data.reduce((s, u) => s + (u.month_cost_estimated_usd || 0), 0);
const fmtUsd = (n) => "$" + (Number(n) || 0).toFixed(4);
const maxReq = Math.max(...data.map((u) => u.total_requests || 0), 1);
@ -1886,7 +1887,7 @@ async function loadMetrics() {
<td class="px-4 py-2.5 font-medium">${escHtml(u.display_name)}</td>
<td class="px-4 py-2.5 text-right font-mono text-xs">${u.total_requests ?? 0}</td>
<td class="px-4 py-2.5 text-right font-mono text-xs">${u.total_web_searches ?? 0}</td>
<td class="px-4 py-2.5 text-right font-mono text-xs">${fmtUsd(u.month_cost_usd)}</td>
<td class="px-4 py-2.5 text-right font-mono text-xs" title="davon geschätzt (TTS/Notruf/Strom): ${fmtUsd(u.month_cost_estimated_usd)}">${fmtUsd(u.month_cost_usd)}</td>
<td class="px-4 py-2.5 text-right font-mono text-xs">${u.month_requests ? fmtUsd((u.month_cost_usd || 0) / u.month_requests) : "—"}</td>
<td class="px-4 py-2.5 text-right font-mono text-xs">${u.total_units ?? 0}</td>
<td class="px-4 py-2.5 text-right text-xs text-slate-400">${u.last_active || "—"}</td>
@ -1906,6 +1907,8 @@ async function loadMetrics() {
</table>
</div>
<p class="text-xs text-slate-400 mb-4">Kosten = laufender Monat (USD). Exakt via OpenRouter (abgleichbar): <span class="font-mono">${fmtUsd(totalCost - totalEstimated)}</span> · geschätzt (TTS/Notruf/Strom): <span class="font-mono">${fmtUsd(totalEstimated)}</span>.</p>
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4">
<h3 class="font-semibold text-sm mb-4">Anfragen je Nutzer</h3>
<div class="space-y-2.5">${bars}</div>