Dateien
AegisSight-Monitor-Verwaltung/src/models.py
claude-dev 4532086e0e feat(orgs): KI-Verarbeitung organisationsweit einstellbar
Der Monitor löst den KI-Weg als Lage vor Organisation vor Servervorgabe auf
und liest dafür das Org-Setting ai_backend. Geschrieben werden konnte es
bisher nur direkt in der Datenbank, eine Oberfläche gab es nicht. Für einen
Kunden, der die DSGVO-Fassung will, ist das der praktisch wichtigste Weg,
denn der stellt einmal zentral um und nicht Lage für Lage.

Das Feld steht jetzt dort, wo die Pipeline-Sprache schon steht, also als
eigene Spalte in der Lizenztabelle der aktiven Lizenz, und zusätzlich im
Anlege-Dialog für neue Organisationen.

Drei Werte. Leer bedeutet Servervorgabe, cli den heutigen Weg über Anthropic,
bedrock die EU-Fassung über AWS Bedrock in Frankfurt mit Recherche über den
europäischen Suchindex staan.

Der Leerstring ist dabei ein gültiger Wert und keine fehlende Angabe. Der
Router prüft deshalb auf None statt auf Wahrheit, sonst ließe sich eine
gesetzte Vorgabe nie wieder auf die Servervorgabe zurücknehmen.

26 Prüfungen, ohne Datenbank und ohne Netz.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 18:31:06 +00:00

125 Zeilen
3.9 KiB
Python

"""Pydantic Models für das Verwaltungsportal."""
from pydantic import BaseModel, Field
from typing import Optional
class MagicLinkRequest(BaseModel):
email: str = Field(min_length=3, max_length=200)
class MagicLinkResponse(BaseModel):
message: str
class VerifyTokenRequest(BaseModel):
token: str = Field(min_length=10, max_length=200)
class TokenResponse(BaseModel):
access_token: str
token_type: str = "bearer"
username: str
email: str = ""
class LicenseParams(BaseModel):
"""Stellwerte einer Lizenz ohne Org-Bezug.
Wird zweifach genutzt, direkt beim Lizenz-Anlegen (LicenseCreate) und
verschachtelt beim Org-Anlegen (OrgCreate.license). Die Laufzeitfelder
(credits_used, credits_period_start, budget_warning_sent) verwaltet der
Monitor, das Portal setzt nur Stellwerte.
"""
license_type: str = Field(pattern="^(trial|annual|permanent)$")
max_users: int = Field(default=5, ge=1, le=1000)
duration_days: Optional[int] = Field(default=None, ge=1, le=3650)
token_budget_usd: Optional[float] = None
credits_total: Optional[int] = None
cost_per_credit: Optional[float] = None
budget_warning_percent: Optional[int] = Field(default=80, ge=0, le=100) # 0 = Warnung aus
unlimited_budget: bool = False
# 'monthly' = Kontingent wird zum Monatswechsel neu gefuellt (der Monitor
# setzt den Verbrauch traege zurueck), 'total' = gilt fuer die Laufzeit.
# Ungenutzte Credits verfallen zum Periodenende (kein Uebertrag,
# Produktentscheidung 07/2026).
credits_period: str = Field(default="monthly", pattern="^(monthly|total)$")
class OrgCreate(BaseModel):
name: str = Field(min_length=1, max_length=200)
slug: str = Field(min_length=1, max_length=100, pattern="^[a-z0-9-]+$")
output_language: str = Field(default="de", pattern="^(de|en)$")
# KI-Weg der Organisation. Leer heisst, es gilt die Servervorgabe.
ai_backend: str = Field(default="", pattern="^(cli|bedrock|)$")
# Optional direkt eine Lizenz mit Credits-Kontingent anlegen
license: Optional[LicenseParams] = None
class OrgUpdate(BaseModel):
name: Optional[str] = Field(default=None, max_length=200)
is_active: Optional[bool] = None
output_language: Optional[str] = Field(default=None, pattern="^(de|en)$")
ai_backend: Optional[str] = Field(default=None, pattern="^(cli|bedrock|)$")
class OrgResponse(BaseModel):
id: int
name: str
slug: str
is_active: bool
user_count: int = 0
license_status: str = ""
license_type: str = ""
created_at: str
globe_access: bool = False
network_access: bool = False
output_language: str = "de"
ai_backend: str = ""
class LicenseCreate(LicenseParams):
organization_id: int
class LicenseResponse(BaseModel):
id: int
organization_id: int
license_type: str
max_users: int
valid_from: str
valid_until: Optional[str]
status: str
notes: Optional[str]
token_budget_usd: Optional[float] = None
credits_total: Optional[int] = None
credits_used: Optional[float] = None
cost_per_credit: Optional[float] = None
budget_warning_percent: Optional[int] = None
unlimited_budget: bool = False
# Neue Kontingent-Felder, alle optional mit Default, damit Responses auch
# von einer noch nicht migrierten Datenbank validieren.
credits_period: Optional[str] = "monthly"
credits_period_start: Optional[str] = None
created_at: str
globe_access: bool = False
network_access: bool = False
class UserCreate(BaseModel):
email: str = Field(min_length=3, max_length=200)
username: Optional[str] = Field(default=None, max_length=100)
role: str = Field(default="member", pattern="^(org_admin|member)$")
class UserResponse(BaseModel):
id: int
email: str
username: str
organization_id: int
role: str
is_active: bool
last_login_at: Optional[str]
created_at: str
globe_access: bool = False
network_access: bool = False