init
This commit is contained in:
72
backend/config.py
Normal file
72
backend/config.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""Konfiguration: Provider-Stacks, Rollen-Auflösung, Engine-Konstanten.
|
||||
|
||||
Rollen-Modelle stehen AUSSCHLIESSLICH in der .env (ROLLE_<NAME>=provider:modell) —
|
||||
nirgendwo im Code ein Default oder Fallback. Unkonfigurierte Rolle = harter Stopp.
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
TEMPLATES_DIR = Path(__file__).resolve().parent / "templates"
|
||||
STORAGE_DIR = PROJECT_ROOT / "storage"
|
||||
DB_PATH = STORAGE_DIR / "planer.db"
|
||||
LAEUFE_DIR = STORAGE_DIR / "laeufe"
|
||||
OPENCODE_CONFIG = PROJECT_ROOT / "dev-ops" / "opencode-slim.json"
|
||||
PROJEKTE_DATEI = PROJECT_ROOT / "projekte.txt"
|
||||
|
||||
|
||||
def _load_env(path: Path) -> None:
|
||||
"""Mini .env-Loader (keine Dependency): KEY=VALUE-Zeilen. Die DATEI gewinnt über
|
||||
vererbte Env (Creator-Lehre: vererbte Werte pinnen sonst still veraltete Stände)."""
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, _, value = line.partition("=")
|
||||
key, value = key.strip(), value.strip().strip('"').strip("'")
|
||||
if key:
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
_load_env(PROJECT_ROOT / ".env")
|
||||
|
||||
# --- Provider-Stacks: nur Aufrufweg + Auth, KEINE Modelle -------------------------
|
||||
PROVIDERS = {
|
||||
"claude": {"cli": "claude", "env_key": None}, # Auth via OAuth/~/.claude (Abo)
|
||||
"minimax": {"cli": "opencode", "env_key": "MINIMAX_API_KEY"},
|
||||
}
|
||||
|
||||
# LLM-Rollen des Scan-Boards. Jede braucht ROLLE_<NAME> in der .env.
|
||||
ROLLEN = ("schneiden", "nachfass", "sichten", "judge")
|
||||
|
||||
|
||||
def resolve_role(role: str) -> tuple[str, str]:
|
||||
"""→ (provider, modell) aus ROLLE_<NAME>=provider:modell. Fehlt die Konfiguration
|
||||
oder ist sie unbrauchbar: RuntimeError — NIE ein stiller Default (Leitprinzip 2)."""
|
||||
env_name = f"ROLLE_{role.upper()}"
|
||||
wert = os.getenv(env_name, "").strip()
|
||||
if not wert:
|
||||
raise RuntimeError(
|
||||
f"Rolle '{role}' hat kein Modell konfiguriert — {env_name}=provider:modell in .env setzen")
|
||||
provider, _, modell = wert.partition(":")
|
||||
if provider not in PROVIDERS:
|
||||
raise RuntimeError(
|
||||
f"{env_name}: unbekannter Provider '{provider}' (bekannt: {', '.join(PROVIDERS)})")
|
||||
if not modell:
|
||||
raise RuntimeError(f"{env_name}: Modell fehlt (Format provider:modell)")
|
||||
return provider, modell
|
||||
|
||||
|
||||
# --- Engine-Konstanten (Kanban + Agenten) -----------------------------------------
|
||||
KANBAN_BATCH = int(os.getenv("KANBAN_BATCH", "4")) # Karten je Paket
|
||||
MAX_CARD_RETRIES = int(os.getenv("MAX_CARD_RETRIES", "3")) # dann dead-letter
|
||||
RETRY_BACKOFF = float(os.getenv("RETRY_BACKOFF", "30")) # Basis-Sekunden, exponentiell
|
||||
MAX_CONCURRENT_AGENTS = int(os.getenv("MAX_CONCURRENT_AGENTS", "6"))
|
||||
MAX_CONCURRENT_AGENTS_PER_TOPIC = int(os.getenv("MAX_CONCURRENT_AGENTS_PER_TOPIC", "6"))
|
||||
MAX_CONCURRENT_API_AGENTS = int(os.getenv("MAX_CONCURRENT_API_AGENTS", "12"))
|
||||
RAM_MIN_FREE_PCT = int(os.getenv("RAM_MIN_FREE_PCT", "20")) # 0 = RAM-Gate aus
|
||||
AGENT_TIMEOUT = int(os.getenv("AGENT_TIMEOUT", "360")) # Sekunden je LLM-Call
|
||||
Reference in New Issue
Block a user