logging für import_markdown

This commit is contained in:
Lars 2025-12-12 13:26:31 +01:00
parent 1e3fcc1633
commit d25d623b9c
2 changed files with 44 additions and 12 deletions

View File

@ -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"
@ -40,6 +41,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(
chunk_text=chunk_text[:3000],
@ -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 []
data = json.loads(clean_json)
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 []
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):

View File

@ -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