bifroest/app/core/config.py
2026-10-27 08:34:17 +01:00

150 lines
6 KiB
Python

"""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()