Refactor: Pricing/Draft-Pfad als Single Source of Truth (Phase 1)
- Neuer services/pricing.py: PRICING-Dict + compute_savings() aus dem system-Router extrahiert; Router ist jetzt dünn (nur role_map + Aufruf). - /system/token-stats liefert zusätzlich das pricing-Dict → Frontend zeigt die Tarife daraus an statt sie im Text zu hartkodieren. - SPEC_DRAFT_MODEL_PATH in config.py (MC_SPEC_DRAFT_MODEL); llamaswap.py und migrate_config.py referenzieren die Konstante statt des doppelten Literals. - Ersparnis-Berechnung verhaltensneutral verifiziert (35,09 $ / 32,28 €). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+11
-54
@@ -5,6 +5,7 @@ Lokal (Windows) schlagen die Shell-Befehle harmlos fehl und werden als Fehler
|
||||
zurückgegeben statt zu crashen.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
@@ -15,8 +16,12 @@ from config import GATEWAY_URL, HERMES_API_URL, HERMES_WEBUI_URL, LLAMA_SWAP_URL
|
||||
from services import backup as backup_svc
|
||||
from services.agent import agent_status
|
||||
from services.gateway import gateway_reachable
|
||||
from services.llamaswap import engine_reachable
|
||||
from services.llamaswap import engine_reachable, list_models
|
||||
from services.pricing import compute_savings
|
||||
from services.system import system_status
|
||||
from services.token_stats import get_stats
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -90,64 +95,16 @@ def self_update() -> dict:
|
||||
return {"pull": pull, "reset": reset, "restart": restart_res}
|
||||
|
||||
|
||||
from services.token_stats import get_stats
|
||||
from services.llamaswap import list_models
|
||||
|
||||
@router.get("/system/token-stats")
|
||||
def token_stats() -> dict:
|
||||
stats = get_stats()
|
||||
p = stats.get("prompt_tokens", 0)
|
||||
c = stats.get("completion_tokens", 0)
|
||||
total = p + c
|
||||
|
||||
# Map model IDs and aliases to their respective roles for pricing resolution
|
||||
role_map = {}
|
||||
"""Token-Verbrauch + Cloud-Ersparnis. Logik im pricing-Service (SSoT)."""
|
||||
# Rolle je Modell/Alias (lowercase) für die Tarif-Auflösung auflösen.
|
||||
role_map: dict[str, str | None] = {}
|
||||
try:
|
||||
for m in list_models():
|
||||
role_map[m["name"].lower()] = m.get("role")
|
||||
for alias in m.get("aliases", []):
|
||||
role_map[alias.lower()] = m.get("role")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Dynamic pricing tiers based on model class in June 2026
|
||||
PRICING = {
|
||||
"heavy": (15.0, 75.0),
|
||||
"coder": (3.0, 15.0),
|
||||
"hermes": (1.0, 5.0),
|
||||
"fast": (0.15, 0.60),
|
||||
"scout": (0.15, 0.60),
|
||||
"vision": (0.15, 0.60),
|
||||
"reasoning": (0.15, 0.60),
|
||||
}
|
||||
|
||||
modeled_p = 0
|
||||
modeled_c = 0
|
||||
saved_usd = 0.0
|
||||
|
||||
models_data = stats.get("models") or {}
|
||||
for m_name, m_tokens in models_data.items():
|
||||
mp = m_tokens.get("prompt", 0)
|
||||
mc = m_tokens.get("completion", 0)
|
||||
modeled_p += mp
|
||||
modeled_c += mc
|
||||
|
||||
role = role_map.get(m_name, m_name)
|
||||
rate_in, rate_out = PRICING.get(role, (0.15, 0.60))
|
||||
saved_usd += (mp * rate_in + mc * rate_out) / 1_000_000.0
|
||||
|
||||
# Baseline/legacy tokens calculated at premium rates ($15.00 / $75.00)
|
||||
# to preserve historical savings value prior to model-specific logging
|
||||
baseline_p = max(0, p - modeled_p)
|
||||
baseline_c = max(0, c - modeled_c)
|
||||
saved_usd += (baseline_p * 15.0 + baseline_c * 75.0) / 1_000_000.0
|
||||
|
||||
saved_eur = saved_usd * 0.92 # 1 USD = 0.92 EUR
|
||||
|
||||
return {
|
||||
"prompt_tokens": p,
|
||||
"completion_tokens": c,
|
||||
"total_tokens": total,
|
||||
"saved_usd": round(saved_usd, 2),
|
||||
"saved_eur": round(saved_eur, 2)
|
||||
}
|
||||
log.warning("token_stats: list_models fehlgeschlagen, Tarife per Name", exc_info=True)
|
||||
return compute_savings(get_stats(), role_map)
|
||||
|
||||
Reference in New Issue
Block a user