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>
125 Zeilen
3.9 KiB
Python
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
|