mindnet/app/core/ingestion/ingestion_validation.py

89 lines
3.7 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
FILE: app/core/ingestion/ingestion_validation.py
DESCRIPTION: WP-15b semantische Validierung von Kanten gegen den LocalBatchCache.
WP-25b: Umstellung auf Lazy-Prompt-Orchestration (prompt_key + variables).
VERSION: 2.14.0 (WP-25b: Lazy Prompt Integration)
STATUS: Active
FIX:
- WP-25b: Entfernung manueller Prompt-Formatierung zur Unterstützung modell-spezifischer Prompts.
- WP-25b: Umstellung auf generate_raw_response mit prompt_key="edge_validation".
- WP-25a: Voller Erhalt der MoE-Profilsteuerung und Fallback-Kaskade via LLMService.
"""
import logging
from typing import Dict, Any, Optional
from app.core.parser import NoteContext
# ENTSCHEIDENDER FIX: Import der neutralen Bereinigungs-Logik zur Vermeidung von Circular Imports
from app.core.registry import clean_llm_text
logger = logging.getLogger(__name__)
async def validate_edge_candidate(
chunk_text: str,
edge: Dict,
batch_cache: Dict[str, NoteContext],
llm_service: Any,
provider: Optional[str] = None,
profile_name: str = "ingest_validator"
) -> bool:
"""
WP-15b/25b: Validiert einen Kandidaten semantisch gegen das Ziel im Cache.
Nutzt Lazy-Prompt-Loading zur Unterstützung modell-spezifischer Validierungs-Templates.
"""
target_id = edge.get("to")
target_ctx = batch_cache.get(target_id)
# Robust Lookup Fix (v2.12.2): Support für Anker
if not target_ctx and "#" in target_id:
base_id = target_id.split("#")[0]
target_ctx = batch_cache.get(base_id)
# Sicherheits-Fallback (Hard-Link Integrity)
# Explizite Wikilinks oder Callouts werden nicht durch das LLM verifiziert.
if not target_ctx:
logger.info(f" [VALIDATION SKIP] No context for '{target_id}' - allowing link.")
return True
try:
logger.info(f"⚖️ [VALIDATING] Relation '{edge.get('kind')}' -> '{target_id}' (Profile: {profile_name})...")
# WP-25b: Lazy-Prompt Aufruf.
# Wir übergeben keine formatierte Nachricht mehr, sondern Key und Daten-Dict.
# Das manuelle 'template = llm_service.get_prompt(...)' entfällt hier.
raw_response = await llm_service.generate_raw_response(
prompt_key="edge_validation",
variables={
"chunk_text": chunk_text[:1500],
"target_title": target_ctx.title,
"target_summary": target_ctx.summary,
"edge_kind": edge.get("kind", "related_to")
},
priority="background",
profile_name=profile_name
)
# WP-14 Fix: Bereinigung zur Sicherstellung der Interpretierbarkeit
response = clean_llm_text(raw_response)
# Semantische Prüfung des Ergebnisses
is_valid = "YES" in response.upper()
if is_valid:
logger.info(f"✅ [VALIDATED] Relation to '{target_id}' confirmed.")
else:
logger.info(f"🚫 [REJECTED] Relation to '{target_id}' irrelevant for this chunk.")
return is_valid
except Exception as e:
error_str = str(e).lower()
error_type = type(e).__name__
# WP-25b FIX: Differenzierung zwischen transienten und permanenten Fehlern
# Transiente Fehler (Timeout, Network) → erlauben (Datenverlust vermeiden)
if any(x in error_str for x in ["timeout", "connection", "network", "unreachable", "refused"]):
logger.warning(f"⚠️ Transient error for {target_id} using {profile_name}: {error_type} - {e}. Allowing edge.")
return True
# Permanente Fehler (Config, Validation, Invalid Response) → ablehnen (Graph-Qualität)
logger.error(f"❌ Permanent validation error for {target_id} using {profile_name}: {error_type} - {e}")
return False