From d25d623b9cc25a60755560df85d9aa740c80cd5f Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 12 Dec 2025 13:26:31 +0100 Subject: [PATCH] =?UTF-8?q?logging=20f=C3=BCr=20import=5Fmarkdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/semantic_analyzer.py | 49 +++++++++++++++++++++++-------- scripts/import_markdown.py | 7 +++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/app/services/semantic_analyzer.py b/app/services/semantic_analyzer.py index 4cd11ae..e6e5476 100644 --- a/app/services/semantic_analyzer.py +++ b/app/services/semantic_analyzer.py @@ -1,11 +1,11 @@ """ app/services/semantic_analyzer.py — Edge Validation & Filtering -Version: 1.1 (Robust JSON Parsing) +Version: 1.2 (Extended Observability & Debugging) """ import json import logging -from typing import List, Optional +from typing import List, Optional, Any from dataclasses import dataclass # Importe @@ -21,6 +21,7 @@ class SemanticAnalyzer: """ Sendet einen Chunk und eine Liste potenzieller Kanten an das LLM. Das LLM filtert heraus, welche Kanten für diesen Chunk relevant sind. + Enthält erweitertes Logging für Debugging. """ if not all_edges: return [] @@ -28,8 +29,8 @@ class SemanticAnalyzer: # 1. Prompt laden prompt_template = self.llm.prompts.get("edge_allocation_template") - # Fallback, falls Prompt nicht in YAML definiert ist (für Tests ohne volle Config) if not prompt_template: + logger.warning("⚠️ Prompt 'edge_allocation_template' fehlt. Nutze Fallback-Prompt.") prompt_template = ( "TASK: Wähle aus den Kandidaten die relevanten Kanten für den Text.\n" "TEXT: {chunk_text}\n" @@ -39,6 +40,9 @@ class SemanticAnalyzer: # 2. Kandidaten-Liste formatieren edges_str = "\n".join([f"- {e}" for e in all_edges]) + + # LOG: Request Info + logger.debug(f"🔍 [SemanticAnalyzer] Request: {len(chunk_text)} chars Text, {len(all_edges)} Candidates.") # 3. Prompt füllen final_prompt = prompt_template.format( @@ -53,11 +57,26 @@ class SemanticAnalyzer: force_json=True ) + # LOG: Raw Response (nur die ersten 200 Zeichen, um Log nicht zu fluten, außer bei Fehler) + logger.debug(f"📥 [SemanticAnalyzer] Raw Response (Preview): {response_json[:200]}...") + # 5. Parsing & Cleaning clean_json = response_json.replace("```json", "").replace("```", "").strip() - if not clean_json: return [] + + if not clean_json: + logger.warning("⚠️ [SemanticAnalyzer] Leere Antwort vom LLM erhalten. Trigger Fallback.") + return [] + + try: + data = json.loads(clean_json) + except json.JSONDecodeError as json_err: + # LOG: Detaillierter Fehlerbericht für den User + logger.error(f"❌ [SemanticAnalyzer] JSON Decode Error.") + logger.error(f" Grund: {json_err}") + logger.error(f" Empfangener String: {clean_json}") + logger.info(" -> Workaround: Fallback auf 'Alle Kanten' (durch Chunker).") + return [] - data = json.loads(clean_json) valid_edges = [] # 6. Robuste Validierung (List vs Dict) @@ -67,14 +86,15 @@ class SemanticAnalyzer: elif isinstance(data, dict): # Abweichende Formate behandeln + logger.info(f"ℹ️ [SemanticAnalyzer] LLM lieferte Dict statt Liste. Versuche Reparatur. Keys: {list(data.keys())}") + for key, val in data.items(): # Fall A: {"edges": ["kind:target"]} - if key.lower() in ["edges", "results", "kanten"] and isinstance(val, list): + if key.lower() in ["edges", "results", "kanten", "matches"] and isinstance(val, list): valid_edges.extend([str(e) for e in val if isinstance(e, str) and ":" in e]) # Fall B: {"kind": "target"} (Das beobachtete Format im Log) elif isinstance(val, str): - # Wir rekonstruieren "kind:target" valid_edges.append(f"{key}:{val}") # Fall C: {"kind": ["target1", "target2"]} @@ -84,13 +104,18 @@ class SemanticAnalyzer: valid_edges.append(f"{key}:{target}") # Safety: Filtere nur Kanten, die halbwegs valide aussehen - return [e for e in valid_edges if ":" in e] + final_result = [e for e in valid_edges if ":" in e] + + # LOG: Ergebnis + if final_result: + logger.info(f"✅ [SemanticAnalyzer] Success. {len(final_result)} Kanten zugewiesen.") + else: + logger.debug(" [SemanticAnalyzer] Keine spezifischen Kanten erkannt (Empty Result).") + + return final_result - except json.JSONDecodeError: - logger.warning("SemanticAnalyzer: LLM lieferte kein valides JSON. Ignoriere Zuweisung.") - return [] except Exception as e: - logger.error(f"SemanticAnalyzer Error: {e}") + logger.error(f"💥 [SemanticAnalyzer] Kritischer Fehler: {e}", exc_info=True) return [] async def close(self): diff --git a/scripts/import_markdown.py b/scripts/import_markdown.py index da86fc0..d5ce195 100644 --- a/scripts/import_markdown.py +++ b/scripts/import_markdown.py @@ -11,6 +11,13 @@ import logging from pathlib import Path from dotenv import load_dotenv +import logging +# Setzt das Level global auf INFO, damit Sie den Fortschritt sehen +logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s') + +# Wenn Sie TIEFE Einblicke wollen, setzen Sie den SemanticAnalyzer spezifisch auf DEBUG: +logging.getLogger("app.services.semantic_analyzer").setLevel(logging.DEBUG) + # Importiere den neuen Async Service # Stellen wir sicher, dass der Pfad stimmt (Pythonpath) import sys