""" FILE: app/config.py DESCRIPTION: Zentrale Pydantic-Konfiguration. WP-20: Hybrid-Cloud Modus Support (OpenRouter/Gemini/Ollama). FIX: Einführung von Parametern zur intelligenten Rate-Limit Steuerung (429 Handling). VERSION: 0.6.7 STATUS: Active DEPENDENCIES: os, functools, pathlib, python-dotenv """ from __future__ import annotations import os from functools import lru_cache from pathlib import Path from dotenv import load_dotenv # WP-20: Lade Umgebungsvariablen aus der .env Datei # override=True garantiert, dass Änderungen in der .env immer Vorrang haben. # WP-24c v4.5.10: Expliziter Pfad für .env-Datei, um Probleme mit Arbeitsverzeichnis zu vermeiden # Suche .env im Projekt-Root (3 Ebenen über app/config.py: app/config.py -> app/ -> root/) _project_root = Path(__file__).parent.parent.parent _env_file = _project_root / ".env" _env_loaded = False # Versuche zuerst expliziten Pfad if _env_file.exists(): _env_loaded = load_dotenv(_env_file, override=True) if _env_loaded: # Optional: Logging (nur wenn logging bereits initialisiert ist) try: import logging _logger = logging.getLogger(__name__) _logger.debug(f"✅ .env geladen von: {_env_file}") except: pass # Logging noch nicht initialisiert # Fallback: Automatische Suche (für Dev/Test oder wenn .env an anderer Stelle liegt) if not _env_loaded: _env_loaded = load_dotenv(override=True) if _env_loaded: try: import logging _logger = logging.getLogger(__name__) _logger.debug(f"✅ .env geladen via automatische Suche (cwd: {Path.cwd()})") except: pass class Settings: # --- Qdrant Datenbank --- QDRANT_URL: str = os.getenv("QDRANT_URL", "http://127.0.0.1:6333") QDRANT_API_KEY: str | None = os.getenv("QDRANT_API_KEY") # WP-24c v4.5.10: Harmonisierung - Unterstützt beide Umgebungsvariablen für Abwärtskompatibilität # COLLECTION_PREFIX hat Priorität, MINDNET_PREFIX als Fallback # WP-24c v4.5.10-FIX: Default auf "mindnet" (Prod) statt "mindnet_dev" (Dev) # Dev muss explizit COLLECTION_PREFIX=mindnet_dev in .env setzen COLLECTION_PREFIX: str = os.getenv("COLLECTION_PREFIX") or os.getenv("MINDNET_PREFIX") or "mindnet" # WP-22: Vektor-Dimension für das Embedding-Modell (nomic) VECTOR_SIZE: int = int(os.getenv("VECTOR_DIM", "768")) DISTANCE: str = os.getenv("MINDNET_DISTANCE", "Cosine") # --- Lokale Embeddings (Ollama & Sentence-Transformers) --- EMBEDDING_MODEL: str = os.getenv("MINDNET_EMBEDDING_MODEL", "nomic-embed-text") MODEL_NAME: str = os.getenv("MINDNET_MODEL", "sentence-transformers/all-MiniLM-L6-v2") # --- WP-20 Hybrid LLM Provider --- # Erlaubt: "ollama" | "gemini" | "openrouter" MINDNET_LLM_PROVIDER: str = os.getenv("MINDNET_LLM_PROVIDER", "openrouter").lower() # Standardwert 10000, falls nichts in der .env steht MAX_OLLAMA_CHARS: int = int(os.getenv("MAX_OLLAMA_CHARS", 10000)) # Google AI Studio (2025er Lite-Modell für höhere Kapazität) GOOGLE_API_KEY: str | None = os.getenv("GOOGLE_API_KEY") GEMINI_MODEL: str = os.getenv("MINDNET_GEMINI_MODEL", "gemini-2.5-flash-lite") # OpenRouter Integration (Verfügbares Free-Modell 2025) OPENROUTER_API_KEY: str | None = os.getenv("OPENROUTER_API_KEY") OPENROUTER_MODEL: str = os.getenv("OPENROUTER_MODEL", "mistralai/mistral-7b-instruct:free") LLM_FALLBACK_ENABLED: bool = os.getenv("MINDNET_LLM_FALLBACK", "true").lower() == "true" # --- NEU: Intelligente Rate-Limit Steuerung --- # Dauer der Wartezeit in Sekunden, wenn ein HTTP 429 (Rate Limit) auftritt LLM_RATE_LIMIT_WAIT: float = float(os.getenv("MINDNET_LLM_RATE_LIMIT_WAIT", "60.0")) # Anzahl der Cloud-Retries bei 429, bevor Ollama-Fallback greift LLM_RATE_LIMIT_RETRIES: int = int(os.getenv("MINDNET_LLM_RATE_LIMIT_RETRIES", "3")) # --- WP-05 Lokales LLM (Ollama) --- OLLAMA_URL: str = os.getenv("MINDNET_OLLAMA_URL", "http://127.0.0.1:11434") LLM_MODEL: str = os.getenv("MINDNET_LLM_MODEL", "phi3:mini") PROMPTS_PATH: str = os.getenv("MINDNET_PROMPTS_PATH", "config/prompts.yaml") # --- WP-06 / WP-14 Performance & Last-Steuerung --- LLM_TIMEOUT: float = float(os.getenv("MINDNET_LLM_TIMEOUT", "300.0")) DECISION_CONFIG_PATH: str = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml") BACKGROUND_LIMIT: int = int(os.getenv("MINDNET_LLM_BACKGROUND_LIMIT", "2")) # --- System-Pfade & Ingestion-Logik --- DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true" MINDNET_VAULT_ROOT: str = os.getenv("MINDNET_VAULT_ROOT", "./vault_master") MINDNET_TYPES_FILE: str = os.getenv("MINDNET_TYPES_FILE", "config/types.yaml") MINDNET_VOCAB_PATH: str = os.getenv("MINDNET_VOCAB_PATH", "/mindnet/vault/mindnet/_system/dictionary/edge_vocabulary.md") CHANGE_DETECTION_MODE: str = os.getenv("MINDNET_CHANGE_DETECTION_MODE", "full") # --- WP-04 Retriever Gewichte --- RETRIEVER_W_SEM: float = float(os.getenv("MINDNET_WP04_W_SEM", "0.70")) RETRIEVER_W_EDGE: float = float(os.getenv("MINDNET_WP04_W_EDGE", "0.25")) RETRIEVER_W_CENT: float = float(os.getenv("MINDNET_WP04_W_CENT", "0.05")) RETRIEVER_TOP_K: int = int(os.getenv("MINDNET_WP04_TOP_K", "10")) RETRIEVER_EXPAND_DEPTH: int = int(os.getenv("MINDNET_WP04_EXPAND_DEPTH", "1")) RETRIEVER_EDGE_TYPES: str = os.getenv("MINDNET_WP04_EDGE_TYPES", "references,belongs_to,prev,next") @lru_cache def get_settings() -> Settings: """Gibt die zentralen Einstellungen als Singleton zurück.""" return Settings()