feat: Tageskontingent und Notfall-Eskalation (#6)
- Quota (app/quota.py): Anfragen pro Nutzer/Tag (usage-Tabelle), DAILY_REQUEST_LIMIT, pro Nutzer via prefs uebersteuerbar; 429 (REST) bzw. error-Event (WS); Metrik - Notfall (app/safety/emergency.py): heuristische Erkennung (de/en); Log im Store (emergency_events) + optionaler Webhook (best-effort) + X-Emergency/emergency-Event; Metrik emergency_total; Notfaelle umgehen das Kontingent - verdrahtet in chat/speak/transcribe + WS-Turns - Tests: 64 gruen (+6); Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur, .env.example) Hinweis: Notfall-Erkennung ist eine Heuristik (kein Lebensretter); erkannte Texte sind sensibel -> DSGVO beachten. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6422444017
commit
1eb79c1f09
14 changed files with 378 additions and 2 deletions
|
|
@ -29,6 +29,8 @@ from app.dependencies import (
|
|||
)
|
||||
from app.store import SessionOwnershipError
|
||||
from app.audio.vad import EnergyVAD
|
||||
from app.quota import enforce_quota, record_usage, QuotaExceededError
|
||||
from app.safety.emergency import handle_emergency
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
|
@ -75,6 +77,17 @@ async def _run_turn(websocket, store, user, session_id, route, orchestrator, out
|
|||
)
|
||||
llm_context = [{"role": "system", "content": memory_text}] + llm_context
|
||||
|
||||
# Notfall-Erkennung zuerst (immer eskalieren, auch bei Quota-Limit).
|
||||
emergency = handle_emergency(user, text, store)
|
||||
if emergency:
|
||||
await websocket.send_json({"type": "emergency", "category": emergency["category"]})
|
||||
else:
|
||||
try:
|
||||
enforce_quota(user, store)
|
||||
except QuotaExceededError as exc:
|
||||
await websocket.send_json({"type": "error", "status": 429, "detail": str(exc)})
|
||||
return
|
||||
|
||||
await websocket.send_json({"type": "ack", "route": route.as_dict()})
|
||||
|
||||
voice = options.get("voice") or settings.openrouter_tts_voice
|
||||
|
|
@ -119,6 +132,8 @@ async def _run_turn(websocket, store, user, session_id, route, orchestrator, out
|
|||
await websocket.send_json({"type": "error", "status": 502, "detail": str(exc)})
|
||||
return
|
||||
|
||||
record_usage(user, store, len(text) + len(trace.semantic_response or ""))
|
||||
|
||||
if session_id:
|
||||
store.append_message(session_id, user.id, "user", text)
|
||||
store.append_message(session_id, user.id, "assistant", trace.semantic_response)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue