feat: Web-Such-Budget pro Nutzer/Tag
Neue Tabelle web_search_usage + Store-Zaehler (web_search_count / add_web_search_usage, kaskadiert in delete_user). Pro-Nutzer-Limit web_search_daily_limit (Pref > global; 0 = unbegrenzt) in quota.py (web_search_within_budget). ws.py: Gate vor _resolve (Budget aufgebraucht -> Web-Suche fuer den Turn aus) + Zaehlung nach dem Turn (trace.web_searches, gezaehlt im Orchestrator je on_citations). Admin: Feld + UI-Eingabe. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5609741039
commit
88c8c9df7f
8 changed files with 106 additions and 5 deletions
|
|
@ -32,7 +32,8 @@ from app.dependencies import (
|
|||
from app.store import ANONYMOUS_USER_ID, SessionOwnershipError
|
||||
from app.audio.vad import EnergyVAD
|
||||
from app.core.memory_extractor import maybe_schedule_extraction
|
||||
from app.quota import enforce_quota, record_usage, QuotaExceededError
|
||||
from app.quota import (enforce_quota, record_usage, QuotaExceededError,
|
||||
web_search_within_budget, record_web_search)
|
||||
from app.auth import authenticate, _cookie_value, CAPABILITY_COOKIE
|
||||
|
||||
router = APIRouter()
|
||||
|
|
@ -63,9 +64,12 @@ def _authenticate(websocket: WebSocket, token: str | None):
|
|||
return authenticate(websocket.headers, client_host, _capability_token(websocket, token))
|
||||
|
||||
|
||||
async def _resolve(user, session_id, options):
|
||||
async def _resolve(user, session_id, options, store=None):
|
||||
"""Loest Route + Orchestrator + Output-Endpunkt auf (kann RoutingError/Ownership werfen)."""
|
||||
overrides = {key: options.get(key) for key in _OVERRIDE_KEYS}
|
||||
# Web-Such-Tagesbudget aufgebraucht -> Web-Suche fuer diesen Turn aus.
|
||||
if store is not None and not web_search_within_budget(user, store):
|
||||
overrides["web_search_enabled"] = False
|
||||
route = resolve_route(user, session_id, overrides)
|
||||
orchestrator = build_orchestrator(route)
|
||||
output = await resolve_output_endpoint(route)
|
||||
|
|
@ -169,6 +173,7 @@ async def _run_turn(
|
|||
return
|
||||
|
||||
record_usage(user, store, len(text) + len(trace.semantic_response or ""))
|
||||
record_web_search(user, store, trace.web_searches)
|
||||
|
||||
if session_id:
|
||||
store.append_message(session_id, user.id, "user", text)
|
||||
|
|
@ -199,7 +204,7 @@ async def _cancel_active(task, websocket) -> None:
|
|||
|
||||
async def _chat_turn(websocket, store, user, session_id, text, options):
|
||||
try:
|
||||
route, orchestrator, output = await _resolve(user, session_id, options)
|
||||
route, orchestrator, output = await _resolve(user, session_id, options, store)
|
||||
except SessionOwnershipError as exc:
|
||||
await websocket.send_json({"type": "error", "status": 403, "detail": str(exc)})
|
||||
return
|
||||
|
|
@ -211,7 +216,7 @@ async def _chat_turn(websocket, store, user, session_id, text, options):
|
|||
|
||||
async def _voice_turn(websocket, store, user, session_id, audio, fmt, options):
|
||||
try:
|
||||
route, orchestrator, output = await _resolve(user, session_id, options)
|
||||
route, orchestrator, output = await _resolve(user, session_id, options, store)
|
||||
except SessionOwnershipError as exc:
|
||||
await websocket.send_json({"type": "error", "status": 403, "detail": str(exc)})
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue