"""Konfiguration nur über Umgebungsvariablen mit Präfix AIW_ (oder .env).""" from functools import lru_cache from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(env_prefix="AIW_", env_file=".env", extra="ignore") database_url: str = "postgresql+psycopg://aiw:aiw@127.0.0.1:55433/aiw" # Demo-Anmeldung (→ Konzept 5.1): ein gemeinsames Passwort für alle Demo-Nutzer. # Leer = Demo-Anmeldung abgeschaltet. demo_password: str = "" # Cookies nur über HTTPS; lokal ohne TLS auf False setzen cookie_secure: bool = True session_hours: int = 12 # Sperre nach Fehlversuchen (eigene Passwörter) login_max_failures: int = 5 login_lock_minutes: int = 15 # Git-Quellen (→ Konzept 7.3, 7.5): erlaubte Hosts, kommagetrennt. # file:// nur, wenn ausdrücklich erlaubt (lokale Test-Repositories). git_allowed_hosts: str = "" git_allow_local: bool = False git_timeout_seconds: int = 20 # Lesender Zugriff (→ Konzept 7.5): Klon-Cache und Limits git_cache_dir: str = "data/git-cache" git_max_repo_mb: int = 500 git_max_file_kb: int = 1024 # Commits für Änderungsvorschläge (→ Konzept 7.5) git_author_name: str = "AI Workspace" git_author_email: str = "ai-workspace@localhost" git_max_change_files: int = 50 pr_timeout_seconds: float = 20.0 # Offene Pull Requests so oft abfragen (Status, Kommentare) pr_poll_seconds: int = 300 # Datenbank-Verbindungen (MCP über die DB-Dienste, → Konzept 7.4): erlaubte Datenbank-Hosts, kommagetrennt; # localhost nur, wenn ausdrücklich erlaubt. Zusätzlich prüft der DB-Dienst seine eigene Liste. db_allowed_hosts: str = "" db_allow_local: bool = False # Zentrale MCP-Server (→ Konzept 7.3): nur diese Hosts; http nur für localhost und nur wenn erlaubt mcp_allowed_hosts: str = "" mcp_allow_local: bool = False mcp_timeout_seconds: float = 30.0 # Hugin (→ Konzept 7.9): Host unter Administration → Hugin, Integrations-Key je Workspace; # diese Adresse füllt das Formular nur vor. Hosts wie bei MCP (AIW_MCP_ALLOWED_HOSTS). hugin_url: str = "" hugin_timeout_seconds: float = 15.0 # Abgleich im Hintergrund (nur anzeigen und benachrichtigen, nie übernehmen); 0 = aus hugin_check_hours: int = 24 # Größtes Paket (ZIP im Quellformat), das ein Workspace festschreibt hugin_max_package_kb: int = 1024 # Uploads mit DuckDB-Analyse (→ Konzept 7.7) upload_dir: str = "data/uploads" upload_max_mb: int = 200 # Abgeglichene Ordner (app.documents): Größe je Datei, Dateien je Ordner, Zeit für die Auslese, Text je Datei doc_max_mb: int = 25 doc_max_files: int = 2000 doc_extract_timeout_seconds: float = 120.0 doc_max_text_kb: int = 2048 # Speicherkontingent je Workspace (DuckDB-Dateien und Text abgeglichener Ordner); je Workspace anpassbar workspace_storage_mb: int = 500 query_timeout_seconds: float = 20.0 query_max_rows: int = 200 query_memory_mb: int = 512 # Währung der Modellpreise (nur Anzeige) cost_currency: str = "USD" # Benachrichtigungen (E-Mail per SMTP, Webhooks für Teams/Slack); Links zeigen auf public_url public_url: str = "" smtp_host: str = "" smtp_port: int = 587 smtp_user: str = "" smtp_password: str = "" smtp_from: str = "" smtp_starttls: bool = True smtp_timeout_seconds: float = 10.0 # Kommagetrennt; ".example.com" erlaubt alle Unterdomains webhook_allowed_hosts: str = "hooks.slack.com,.webhook.office.com,.logic.azure.com" webhook_allow_local: bool = False # Betrieb: Logs (text | json), Metriken (/metrics nur mit Token), Worker-HTTP für Probes log_level: str = "INFO" log_format: str = "text" metrics_token: str = "" worker_http_host: str = "0.0.0.0" worker_http_port: int = 9101 # Worker gilt als hängend bzw. tot, wenn seine Schleife bzw. sein Herzschlag älter ist worker_stale_seconds: int = 120 # Gelöschte Workspaces bleiben so viele Tage im Papierkorb, danach löscht der Worker sie endgültig workspace_trash_days: int = 14 # Freigaben (→ Konzept 10.5): Ablauffrist in Stunden approval_hours: int = 24 # Tools (→ Konzept 8.3): Ergebnisgröße und Zahl der Tool-Runden je Run tool_max_output_chars: int = 20000 max_tool_rounds: int = 12 # Obergrenze, bis zu der Workspace-Admins die Tool-Runden je Antwort erhöhen dürfen max_tool_rounds_limit: int = 40 # Verlauf je Anfrage höchstens so viele Tokens (geschätzt) bzw. 60 % des Kontextfensters; # Älteres geht als Arbeitsnotiz (Zusammenfassung) mit history_max_tokens: int = 60000 # Worker (→ Konzept 9.3) run_lease_seconds: int = 60 run_max_attempts: int = 3 max_runs_per_team: int = 3 worker_poll_seconds: float = 1.0 # Master-Key für Geheimnisse in der Datenbank (aiw gen-key); kommagetrennt zum Wechseln secret_key: str = "" # Zeitlimit für einen Modellaufruf (Sekunden) model_timeout_seconds: float = 120.0 # Streaming (→ Konzept 4.4): Tokens gebündelt schreiben event_flush_ms: int = 150 @property def allowed_git_hosts(self) -> set[str]: return {h.strip().lower() for h in self.git_allowed_hosts.split(",") if h.strip()} @property def allowed_db_hosts(self) -> set[str]: return {h.strip().lower() for h in self.db_allowed_hosts.split(",") if h.strip()} @property def allowed_webhook_hosts(self) -> set[str]: return {h.strip().lower() for h in self.webhook_allowed_hosts.split(",") if h.strip()} @property def allowed_mcp_hosts(self) -> set[str]: return {h.strip().lower() for h in self.mcp_allowed_hosts.split(",") if h.strip()} @property def psycopg_url(self) -> str: """URL für psycopg direkt (LISTEN), ohne den SQLAlchemy-Treiberzusatz.""" return self.database_url.replace("postgresql+psycopg://", "postgresql://", 1) @lru_cache def get_settings() -> Settings: return Settings()