Admin: KI-Anfragen nach Quelle aufschlüsseln (cloud/local/luna)
- ki_daily_calls: PK auf (user_id, date, source) erweitert + Index; Migration baut Tabelle mit neuer Struktur neu auf, behält Altdaten als 'cloud' - ki.py: return_source=True-Parameter gibt (text, 'cloud'|'local') zurück - training.py: ki_source aus ki.complete() auslesen, in DB speichern - social.py: _ki_complete_tracked() zählt Luna-Anfragen mit source='luna'; alle Content-Endpoints (generate, evaluate, training-tip, breed-of-day, pflege-tipp) nutzen tracking-Variante - admin.py: Stats aufgeteilt in ki_cloud/ki_local/ki_luna je heute+Monat - admin.js: KI-Karte zeigt 9 Zeilen mit ☁️ Claude / 🖥️ LM Studio / 🌙 Luna - SW by-v359, APP_VER 344
This commit is contained in:
parent
74b6c03bb3
commit
8d5c7a19b1
6 changed files with 136 additions and 33 deletions
|
|
@ -25,7 +25,7 @@ logger = logging.getLogger(__name__)
|
|||
KI_MODE = os.getenv("KI_MODE", "local") # off | local | cloud
|
||||
LOCAL_BASE_URL = os.getenv("KI_LOCAL_URL", "http://10.47.11.70:11435/v1")
|
||||
LOCAL_MODEL = os.getenv("KI_LOCAL_MODEL", "gemma-4-31b-it")
|
||||
CLOUD_MODEL = os.getenv("KI_CLOUD_MODEL", "claude-opus-4-6")
|
||||
CLOUD_MODEL = os.getenv("KI_CLOUD_MODEL", "claude-sonnet-4-6")
|
||||
ANTHROPIC_KEY = os.getenv("ANTHROPIC_API_KEY", "")
|
||||
|
||||
# Lazy Imports — nur laden wenn wirklich benötigt
|
||||
|
|
@ -70,6 +70,7 @@ async def complete(
|
|||
user_is_premium: bool = False,
|
||||
json_mode: bool = False,
|
||||
return_model: bool = False,
|
||||
return_source: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
KI-Completion. Wählt automatisch den richtigen Backend.
|
||||
|
|
@ -81,9 +82,10 @@ async def complete(
|
|||
requires_premium: True = nur für Premium-User (nutzt Cloud)
|
||||
user_is_premium: Ob der anfragende User Premium hat
|
||||
json_mode: Antwort als JSON anfordern
|
||||
return_source: Falls True: gibt (text, source) zurück, source = 'cloud'|'local'
|
||||
|
||||
Returns:
|
||||
KI-Antwort als String
|
||||
KI-Antwort als String, oder (str, str) wenn return_source=True
|
||||
|
||||
Raises:
|
||||
KIPremiumRequired: Cloud-Feature ohne Premium
|
||||
|
|
@ -101,20 +103,26 @@ async def complete(
|
|||
# Cloud-Aufruf: nur wenn Premium UND cloud-Modus
|
||||
if requires_premium and user_is_premium and KI_MODE == "cloud":
|
||||
text = await _cloud_complete(prompt, system, max_tokens, json_mode)
|
||||
return (text, CLOUD_MODEL) if return_model else text
|
||||
if return_model:
|
||||
return (text, CLOUD_MODEL)
|
||||
return (text, "cloud") if return_source else text
|
||||
|
||||
# Lokaler Aufruf: Entwicklung + Free-User
|
||||
if KI_MODE in ("local", "cloud"):
|
||||
try:
|
||||
text = await _local_complete(prompt, system, max_tokens, json_mode)
|
||||
return (text, LOCAL_MODEL) if return_model else text
|
||||
if return_model:
|
||||
return (text, LOCAL_MODEL)
|
||||
return (text, "local") if return_source else text
|
||||
except Exception as e:
|
||||
logger.warning(f"Lokales KI-Modell nicht erreichbar: {e}")
|
||||
# Cloud-Fallback: im cloud-Modus immer, sonst nur für Premium-User
|
||||
if ANTHROPIC_KEY and (KI_MODE == "cloud" or (requires_premium and user_is_premium)):
|
||||
logger.info("Fallback auf Cloud-KI.")
|
||||
text = await _cloud_complete(prompt, system, max_tokens, json_mode)
|
||||
return (text, CLOUD_MODEL) if return_model else text
|
||||
if return_model:
|
||||
return (text, CLOUD_MODEL)
|
||||
return (text, "cloud") if return_source else text
|
||||
raise KIUnavailableError(
|
||||
"KI-Modell momentan nicht erreichbar. Bitte später erneut versuchen."
|
||||
) from e
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue