""" error_healer.py - Selbstheilung durch Fehlererkennung & Auto-Korrektur. Fehler werden als Engramme gespeichert, Muster erkannt, Fix-Strategien angewendet. """ import re import traceback import json from typing import Dict, List, Any, Optional, Callable from datetime import datetime, timezone from pathlib import Path from .engram import Engram, Grounding from .store import EngramStore from .retriever import Retriever _HEAL_LOG = Path(__file__).resolve().parent.parent / "data" / "heal_log.jsonl" class ErrorHealer: """ Heilt wiederkehrende Fehler durch: 1. Speichern von Fehlern als Engramme 2. Mustererkennung (gleicher Fehler-Typ, gleicher Kontext) 3. Auto-Fix (Fallback-Strategien, alternative Ansätze) 4. Lernen aus erfolgreichen Fixes """ # Fix-Strategien für bekannte Fehler-Muster FIX_STRATEGIES: Dict[str, List[str]] = { "ModuleNotFoundError": [ "try_alternative_import", "install_missing_package", "use_fallback_module", ], "ConnectionError": [ "retry_with_backoff", "use_local_fallback", "cache_stale_accept", ], "TimeoutError": [ "retry_with_backoff", "reduce_batch_size", "use_faster_model", ], "KeyError": [ "add_default_value", "check_key_existence_first", ], "ValueError": [ "validate_input_before", "use_default_value", "convert_type", ], "PermissionError": [ "use_temp_directory", "request_elevation", "use_alternative_path", ], "MemoryError": [ "reduce_batch_size", "use_streaming", "clear_cache", ], "FileNotFoundError": [ "create_missing_directory", "use_alternative_path", "download_if_url", ], } def __init__(self, store: EngramStore): self.store = store self.retriever = Retriever(store) self._heal_count = 0 self._recent_errors: List[Dict] = [] def _now(self) -> str: return datetime.now(timezone.utc).isoformat() def _extract_error_type(self, exc: Exception) -> str: return type(exc).__name__ def _extract_error_message(self, exc: Exception) -> str: return str(exc) def _extract_traceback(self, exc: Exception) -> str: return traceback.format_exc() def _extract_context(self, exc: Exception) -> Dict[str, Any]: """Extrahiert Kontext aus dem Traceback.""" tb_str = traceback.format_exc() # Extrahiere Datei und Zeilennummer match = re.search(r'File "([^"]+)", line (\d+)', tb_str) if match: return {"file": match.group(1), "line": int(match.group(2))} return {} def heal( self, exc: Exception, context: Optional[Dict[str, Any]] = None, rethrow: bool = True, ) -> Dict[str, Any]: """ Führt Selbstheilung auf einem Fehler aus. Args: exc: Die Exception context: Zusätzlicher Kontext (z.B. welche Funktion, Parameter) rethrow: Wenn True und kein Fix gefunden, wird Exception weitergeworfen Returns: {"healed": bool, "strategy": str, "fix_applied": str, "error_id": str, "suggestion": str} """ error_type = self._extract_error_type(exc) error_msg = self._extract_error_message(exc) tb = self._extract_traceback(exc) ctx = self._extract_context(exc) if context: ctx.update(context) # 1. Fehler als Engramm speichern error_engram = Engram.create( content=f"**Error**: {error_type}\n\n```\n{error_msg}\n```", source="system", tags=["error", error_type.lower()], confidence=0.3, grounding=Grounding.ASSUMPTION, ) error_engram.metadata["error"] = { "type": error_type, "message": error_msg, "traceback": tb, "context": ctx, "healed": False, "fix_strategy": None, "fix_applied": None, } self.store.save(error_engram) # 2. Mustererkennung: Gab es diesen Fehlertyp schon? similar = self.retriever.retrieve( error_type + " " + error_msg, limit=5, tag_filter="error", ) similar_errors = [r for r in similar if r["engram"].metadata.get("source") == "system"] # 3. Fix-Strategie bestimmen strategies = self.FIX_STRATEGIES.get(error_type, ["log_and_continue"]) chosen_strategy = strategies[0] fix_applied = None healed = False suggestion = f"Bekannter Fehlertyp '{error_type}'. Prüfe die Trail-Engramme mit `search --tag error`." # Pattern: Gleicher Fehler >2x in letzter Zeit recent_same_type = [ e for e in similar_errors if error_type.lower() in str(e["engram"].content).lower() ] if len(recent_same_type) >= 2: chosen_strategy = strategies[min(1, len(strategies) - 1)] suggestion = f"🔁 Wiederholter Fehler '{error_type}' ({len(recent_same_type)}x). Nutze Strategie: {chosen_strategy}" # 4. Log self._log_healing({ "timestamp": self._now(), "error_id": str(error_engram.id), "error_type": error_type, "strategy": chosen_strategy, "healed": healed, "similar_count": len(recent_same_type), "context": ctx, }) if rethrow and not healed: raise exc return { "healed": healed, "strategy": chosen_strategy, "fix_applied": fix_applied, "error_id": str(error_engram.id), "suggestion": suggestion, } def _log_healing(self, data: Dict): _HEAL_LOG.parent.mkdir(parents=True, exist_ok=True) with open(_HEAL_LOG, "a", encoding="utf-8") as f: f.write(json.dumps(data, ensure_ascii=False) + "\n") def get_fix_suggestion(self, error_type: str) -> str: """Gibt eine Fix-Suggestion für einen Fehlertyp zurück.""" strategies = self.FIX_STRATEGIES.get(error_type, ["Unbekannter Fehlertyp. Debuggen und als Engramm speichern."]) return f"Mögliche Strategien für {error_type}: {', '.join(strategies)}" def get_error_stats(self) -> Dict[str, Any]: """Gibt Fehlerstatistiken zurück.""" all_eg = self.store.get_all(limit=1000) errors = [e for e in all_eg if "error" in e.metadata.get("tags", [])] types = {} for e in errors: err = e.metadata.get("error", {}) t = err.get("type", "Unknown") types[t] = types.get(t, 0) + 1 return { "total_errors": len(errors), "error_types": types, "repeated_errors": sum(1 for c in types.values() if c > 1), }