From 3629bc3fb9e9867ab0db22f80781fef75ea26712 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 12 Dec 2025 09:42:57 +0100 Subject: [PATCH] modified: app/services/semantic_analyzer.py --- app/services/semantic_analyzer.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/services/semantic_analyzer.py b/app/services/semantic_analyzer.py index 2d4399e..87dd28b 100644 --- a/app/services/semantic_analyzer.py +++ b/app/services/semantic_analyzer.py @@ -48,7 +48,7 @@ class SemanticAnalyzer: user_prompt = f"Dokument-Typ: {source_type}\n\nTEXT:\n{text}" try: - # WICHTIG: Erzwingt Ollama JSON Mode + # 2. LLM Call (Async) response_json = await self.llm.generate_raw_response( user_prompt, system=system_prompt, @@ -59,8 +59,18 @@ class SemanticAnalyzer: clean_json = response_json.replace("```json", "").replace("```", "").strip() data = json.loads(clean_json) + # 3a. Typsicherheit des äußeren Arrays + if not isinstance(data, list): + logger.error("SemanticAnalyzer: JSON root ist kein Array. Fehlerhafte LLM-Antwort.") + raise ValueError("Root element is not a list.") + results = [] for item in data: + # FIX: Typsicherheit auf Item-Ebene + if not isinstance(item, dict): + logger.warning(f"SemanticAnalyzer: Ungültiges Chunk-Element ignoriert: {item}") + continue + content = item.get("content", "").strip() if not content: continue @@ -68,7 +78,7 @@ class SemanticAnalyzer: refined_edges = [] for rel in raw_rels: - # FIX: Sicherstellen, dass rel ein Dictionary ist, um 'str' object has no attribute 'get' zu verhindern + # Typsicherheit auf Relation-Ebene if not isinstance(rel, dict): logger.warning(f"SemanticAnalyzer: Ignoriere ungültige Relation: {rel}") continue @@ -77,7 +87,7 @@ class SemanticAnalyzer: raw_type = rel.get("type", "related_to") if target: - # 1. Annahme: Hole den Typ der ZIEL-Entität aus dem Index (für Matrix-Logik) + # 1. Typ-Auflösung (für Matrix) target_entity_type = self._get_target_type_from_title(target) # 2. Matrix-Logik anwenden: @@ -87,7 +97,6 @@ class SemanticAnalyzer: if final_kind not in ["related_to", "references"] and target_entity_type != "concept": edge_str = f"{final_kind}:{target}" else: - # Wenn Matrix oder LLM generisch war, nutzen wir den generischen Output des LLM. edge_str = f"{raw_type}:{target}" refined_edges.append(edge_str) @@ -100,7 +109,7 @@ class SemanticAnalyzer: logger.error("SemanticAnalyzer: LLM lieferte KEIN valides JSON. Fallback auf Raw Text.") return [SemanticChunkResult(content=text, suggested_edges=[])] except Exception as e: - # Wichtig: Fehler im Loggen, damit wir wissen, warum es crashte (z.B. Timeout/Ressource) + # Wichtig: Wir fangen alle anderen Fehler, um den Prozess nicht abzubrechen. logger.error(f"SemanticAnalyzer Unbehandelter Fehler: {e}") return [SemanticChunkResult(content=text, suggested_edges=[])] @@ -119,7 +128,6 @@ class SemanticAnalyzer: if "leitbild-rituale-system" in title_lower: return "concept" - # Fallback return "concept" async def close(self):