""" FILE: app/core/chunking/chunking_propagation.py DESCRIPTION: Injiziert Sektions-Kanten physisch in den Text (Embedding-Enrichment). Fix v3.3.6: Nutzt robustes Parsing zur Erkennung vorhandener Kanten, um Dopplungen direkt hinter [!edge] Callouts format-agnostisch zu verhindern. """ from typing import List, Dict, Set from .chunking_models import Chunk from .chunking_parser import parse_edges_robust def propagate_section_edges(chunks: List[Chunk]) -> List[Chunk]: """ Sammelt Kanten pro Sektion und schreibt sie hart in den Text und das Window. Verhindert Dopplungen, wenn Kanten bereits via [!edge] Callout vorhanden sind. """ # 1. Sammeln: Alle expliziten Kanten pro Sektions-Pfad aggregieren section_map: Dict[str, Set[str]] = {} # path -> set(kind:target) for ch in chunks: # Root-Level "/" ignorieren (zu global), Fokus auf spezifische Kapitel if not ch.section_path or ch.section_path == "/": continue # Nutzt den robusten Parser aus dem Package # WP-24c v4.2.7: parse_edges_robust gibt jetzt Liste von Dicts zurück edge_infos = parse_edges_robust(ch.text) if edge_infos: if ch.section_path not in section_map: section_map[ch.section_path] = set() for edge_info in edge_infos: section_map[ch.section_path].add(edge_info["edge"]) # 2. Injizieren: Kanten in jeden Chunk der Sektion zurückschreiben (Broadcasting) for ch in chunks: if ch.section_path in section_map: edges_to_add = section_map[ch.section_path] if not edges_to_add: continue # Vorhandene Kanten (Typ:Ziel) in DIESEM Chunk ermitteln, # um Dopplungen (z.B. durch Callouts) zu vermeiden. # WP-24c v4.2.7: parse_edges_robust gibt jetzt Liste von Dicts zurück existing_edge_infos = parse_edges_robust(ch.text) existing_edges = {ei["edge"] for ei in existing_edge_infos} injections = [] # Sortierung für deterministische Ergebnisse for e_str in sorted(list(edges_to_add)): # Wenn die Kante (Typ + Ziel) bereits vorhanden ist (egal welches Format), # überspringen wir die Injektion für diesen Chunk. if e_str in existing_edges: continue kind, target = e_str.split(':', 1) injections.append(f"[[rel:{kind}|{target}]]") if injections: # Physische Anreicherung # Triple-Newline für saubere Trennung im Embedding-Fenster block = "\n\n\n" + " ".join(injections) ch.text += block # Auch ins Window schreiben, da Qdrant hier sucht! if ch.window: ch.window += block else: ch.window = ch.text return chunks