diff --git a/app/core/retriever_config.py b/app/core/retriever_config.py new file mode 100644 index 0000000..fcf7bc7 --- /dev/null +++ b/app/core/retriever_config.py @@ -0,0 +1,116 @@ +"""app/core/retriever_config.py +--------------------------------- +Zentrale Konfiguration für den mindnet-Retriever (WP-04). + +Zweck: + - Lädt config/retriever.yaml (falls vorhanden) oder nutzt sinnvolle Defaults. + - Bietet einen gecachten Zugriff auf die Retriever-Config für + andere Module (z. B. graph_adapter, retriever). + +Hinweis zur Weiterentwicklung (Selbstjustierung): + - Die hier definierten Parameter sind so gewählt, dass sie später + durch ein Feedback-/Learning-to-Rank-Modell überschrieben werden + können, ohne die restliche Architektur anzupassen. +""" + +from __future__ import annotations + +import os +from dataclasses import dataclass +from functools import lru_cache +from pathlib import Path +from typing import Dict + +try: + import yaml # type: ignore +except Exception: # pragma: no cover - Fallback, falls PyYAML nicht installiert ist. + yaml = None # type: ignore + +@dataclass(frozen=True) +class RetrieverConfig: + semantic_scale: float + edge_scale: float + centrality_scale: float + edge_weights: Dict[str, float] + +@lru_cache +def get_retriever_config() -> RetrieverConfig: + """Lädt die Retriever-Konfiguration (YAML + Defaults). + + Reihenfolge: + 1. Defaults (sinnvoll gewählte Startwerte). + 2. Optional: config/retriever.yaml bzw. Pfad aus ENV + MINDNET_RETRIEVER_CONFIG überschreibt die Defaults. + + Die Funktion ist bewusst gecached, da sich die Konfiguration zur + Laufzeit üblicherweise nicht ändert. Für dynamisches Nachladen + könnte der Cache explizit geleert werden. + """ + + # 1) Defaults – bewusst konservativ gewählt. + semantic_scale = 1.0 + edge_scale = 1.0 + centrality_scale = 1.0 + + edge_weights: Dict[str, float] = { + # Wissens-Kanten + "depends_on": 1.0, + "related_to": 0.7, + "similar_to": 0.7, + "references": 0.5, + # Struktur-Kanten + "belongs_to": 0.2, + "next": 0.1, + "prev": 0.1, + # Sonstige / technische Kanten + "backlink": 0.2, + "references_at": 0.2, + } + + # 2) Optional: YAML-Konfiguration laden + cfg_path_env = os.getenv("MINDNET_RETRIEVER_CONFIG") + if cfg_path_env: + cfg_path = Path(cfg_path_env) + else: + # Project-Root = zwei Ebenen über app/core/ + cfg_path = Path(__file__).resolve().parents[2] / "config" / "retriever.yaml" + + if yaml is not None and cfg_path.exists(): + try: + data = yaml.safe_load(cfg_path.read_text(encoding="utf-8")) or {} + except Exception: + data = {} + + retr = data.get("retriever") or {} + + # Skalenwerte überschreiben, falls angegeben + try: + semantic_scale = float(retr.get("semantic_scale", semantic_scale)) + except (TypeError, ValueError): + pass + + try: + edge_scale = float(retr.get("edge_scale", edge_scale)) + except (TypeError, ValueError): + pass + + try: + centrality_scale = float(retr.get("centrality_scale", centrality_scale)) + except (TypeError, ValueError): + pass + + # Edge-Gewichte je Kanten-Typ + ew_cfg = retr.get("edge_weights") or {} + if isinstance(ew_cfg, dict): + for k, v in ew_cfg.items(): + try: + edge_weights[str(k)] = float(v) + except (TypeError, ValueError): + continue + + return RetrieverConfig( + semantic_scale=semantic_scale, + edge_scale=edge_scale, + centrality_scale=centrality_scale, + edge_weights=edge_weights, + )