feat(costs): Pro-Nutzer-Kostenerfassung Stufe 1 (OpenRouter-Ist-Kosten)
CostMeter via ContextVars (app/core/costs.py, multitool-ready). Alle
OpenRouter-Calls (LLM, Sonar, Koreferenz, Fallback) senden usage:{include:true}
und buchen die Ist-Kosten je Kategorie (llm/web_search) — inkl. finalem
Streaming-usage-Chunk. cost_usage-Tabelle (user/day/category) + Store-Methoden;
ws.py/chat.py start_meter pro Turn + persist_costs. Statistik: Spalten
"Kosten (Monat)" + "Ø/Anfrage" + Gesamt. Live: 1 Such-Turn ~ $0,0052
(web_search dominiert, LLM ~ $0,00007). Tests: 318.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
21a5250282
commit
d87f0ce424
9 changed files with 171 additions and 10 deletions
|
|
@ -1827,6 +1827,8 @@ 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 fmtUsd = (n) => "$" + (Number(n) || 0).toFixed(4);
|
||||
const maxReq = Math.max(...data.map((u) => u.total_requests || 0), 1);
|
||||
|
||||
const bars = data.map((u) => {
|
||||
|
|
@ -1849,6 +1851,8 @@ async function loadMetrics() {
|
|||
<th class="text-left px-4 py-2.5">Nutzer</th>
|
||||
<th class="text-right px-4 py-2.5">Anfragen gesamt</th>
|
||||
<th class="text-right px-4 py-2.5">mit Websuche</th>
|
||||
<th class="text-right px-4 py-2.5">Kosten (Monat)</th>
|
||||
<th class="text-right px-4 py-2.5">Ø/Anfrage</th>
|
||||
<th class="text-right px-4 py-2.5">Einheiten</th>
|
||||
<th class="text-right px-4 py-2.5">Letzte Aktivität</th>
|
||||
</tr>
|
||||
|
|
@ -1859,6 +1863,8 @@ 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">${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>
|
||||
</tr>`).join("")}
|
||||
|
|
@ -1868,6 +1874,8 @@ async function loadMetrics() {
|
|||
<td class="px-4 py-2">Gesamt</td>
|
||||
<td class="px-4 py-2 text-right font-mono">${totalReq}</td>
|
||||
<td class="px-4 py-2 text-right font-mono">${totalWS}</td>
|
||||
<td class="px-4 py-2 text-right font-mono">${fmtUsd(totalCost)}</td>
|
||||
<td class="px-4 py-2"></td>
|
||||
<td class="px-4 py-2"></td>
|
||||
<td class="px-4 py-2"></td>
|
||||
</tr>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue