Feature: Tierschutz-Check, KI-Züchter-Features, Export, SEO-Update
Tierschutz-System (immer aktiv, nicht abschaltbar): - welfare_check.py: regelbasierte Prüfung IK, Alter, Deckpause, Wurfanzahl, Genetik - Grün/Gelb/Rot-Modal bei Wurf anlegen + Probeverpaarung - Bei kritischem Befund + "Trotzdem fortfahren" → automatische Admin-Mail - Tierschutz-Check nie durch Nutzer deaktivierbar KI-Züchter-Features (pro User an/abschaltbar außer Tierschutz): - routes/zucht_ki.py: 5 Endpunkte — Wurfankündigung, Genetik-Erklärung, Paarungsanalyse, Hund-Beschreibung, Jahresbericht - Toggles in Einstellungen (ki_zucht_* Felder) - KI-Buttons in litters.js + zuchthunde.js KI-Routing: Privilegierte Rollen (Admin, Züchter, Moderator, Manager) nutzen Claude Sonnet primär, lokales LLM als Fallback Datenexport: routes/breeder_export.py — ZIP mit HTML-Dossier + ODS (odfpy hinzugefügt in requirements.txt) Admin-Profil: POST /admin/breeder/create-profile für Schnellprofil ohne Antragsprozess; Admin-Rolle bleibt erhalten Wurfformular: Dropdown aus Zuchtkartei für Vater/Mutter mit Auto-Fill; litters.vater_id + mutter_id als FK auf zucht_hunde Probeverpaarung: heart-fill Icon + Welfare-Block im Ergebnis Landing Page: Züchter-Section + Feature-Gruppe, Meta-Tags, JSON-LD, keywords, softwareVersion 2.1 SEO: llms.txt vollständig überarbeitet, robots.txt Züchter-Pfade, sitemap.xml um Wurfbörse + Züchter-Profile erweitert SW by-v474, APP_VER 451
This commit is contained in:
parent
91340be5a3
commit
c8ae514c01
20 changed files with 2129 additions and 200 deletions
|
|
@ -82,6 +82,28 @@ def _track_usage(user_id: int | None, source: str) -> None:
|
|||
logger.warning(f"KI-Tracking fehlgeschlagen: {exc}")
|
||||
|
||||
|
||||
def _is_cloud_priority_user(user_id: int | None) -> bool:
|
||||
"""Privilegierte Rollen (Admin, Moderator, Züchter, Manager) nutzen Cloud-KI primär."""
|
||||
if not user_id or not ANTHROPIC_KEY:
|
||||
return False
|
||||
try:
|
||||
from database import db
|
||||
with db() as conn:
|
||||
user = conn.execute(
|
||||
"SELECT rolle, is_moderator, is_social_media FROM users WHERE id=?",
|
||||
(user_id,)
|
||||
).fetchone()
|
||||
if not user:
|
||||
return False
|
||||
return bool(
|
||||
user["rolle"] in ("admin", "breeder", "moderator")
|
||||
or user["is_moderator"]
|
||||
or user["is_social_media"]
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _check_weekly_cloud_limit(user_id: int | None) -> None:
|
||||
"""Wirft KIPremiumRequired wenn user_id das wöchentliche Cloud-Limit erreicht hat."""
|
||||
if user_id is None or CLOUD_WEEKLY_LIMIT <= 0:
|
||||
|
|
@ -92,9 +114,9 @@ def _check_weekly_cloud_limit(user_id: int | None) -> None:
|
|||
user = conn.execute(
|
||||
"SELECT rolle, is_moderator FROM users WHERE id=?", (user_id,)
|
||||
).fetchone()
|
||||
# Admins, Moderatoren und Media Manager haben kein Limit
|
||||
# Admins, Moderatoren, Züchter und Media Manager haben kein Limit
|
||||
if user and (
|
||||
user["rolle"] in ("admin", "moderator", "media_manager")
|
||||
user["rolle"] in ("admin", "breeder", "moderator", "media_manager")
|
||||
or user["is_moderator"]
|
||||
):
|
||||
return
|
||||
|
|
@ -137,8 +159,28 @@ async def complete(
|
|||
if requires_premium and not user_is_premium:
|
||||
raise KIPremiumRequired("Dieses Feature ist Teil von Ban Yaro Premium.")
|
||||
|
||||
# Immer lokal zuerst — Cloud ist Fallback wenn lokal nicht erreichbar
|
||||
if KI_MODE in ("local", "cloud"):
|
||||
# Privilegierte Rollen (Admin, Moderator, Züchter, Manager) → Cloud zuerst
|
||||
if _is_cloud_priority_user(user_id):
|
||||
try:
|
||||
_check_weekly_cloud_limit(user_id)
|
||||
text = await _cloud_complete(prompt, system, max_tokens, json_mode)
|
||||
_track_usage(user_id, "cloud")
|
||||
if return_model:
|
||||
return (text, CLOUD_MODEL)
|
||||
return (text, "cloud") if return_source else text
|
||||
except KIPremiumRequired:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.warning(f"Cloud-KI nicht erreichbar für privilegierten User, Fallback lokal: {e}")
|
||||
# Fallback auf lokales Modell
|
||||
text = await _local_complete(prompt, system, max_tokens, json_mode)
|
||||
_track_usage(user_id, "local")
|
||||
if return_model:
|
||||
return (text, LOCAL_MODEL)
|
||||
return (text, "local") if return_source else text
|
||||
|
||||
# Standard-User → lokal zuerst, Cloud als Fallback
|
||||
try:
|
||||
text = await _local_complete(prompt, system, max_tokens, json_mode)
|
||||
_track_usage(user_id, "local")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue