From 37ec8b614eda2d5fafec92a8f7e04281b57e85b2 Mon Sep 17 00:00:00 2001 From: Lars Date: Sat, 27 Dec 2025 19:12:14 +0100 Subject: [PATCH] bug fix --- app/core/ingestion/ingestion_utils.py | 49 ++++++--------------------- app/core/registry.py | 43 +++++++++++++++++++++++ app/services/llm_service.py | 12 +++---- 3 files changed, 59 insertions(+), 45 deletions(-) create mode 100644 app/core/registry.py diff --git a/app/core/ingestion/ingestion_utils.py b/app/core/ingestion/ingestion_utils.py index 74cb1e6..f4b9324 100644 --- a/app/core/ingestion/ingestion_utils.py +++ b/app/core/ingestion/ingestion_utils.py @@ -1,46 +1,25 @@ """ FILE: app/core/ingestion/ingestion_utils.py DESCRIPTION: Hilfswerkzeuge für JSON-Recovery, Typ-Registry und Konfigurations-Lookups. - AUDIT v2.13.8: Zentralisierung der Text-Bereinigung für LLM-Antworten. + AUDIT v2.13.9: Behebung des Circular Imports durch Nutzung der app.core.registry. """ -import os import json import re -import yaml -from typing import Any, Optional, Dict, List +from typing import Any, Optional, Dict -def clean_llm_text(text: str, registry: Optional[dict] = None) -> str: - """ - Entfernt LLM-Steuerzeichen und Artefakte aus einem Text. - Nutzt die cleanup_patterns aus der Registry oder Standardwerte. - """ - if not text or not isinstance(text, str): - return "" - - # Fallback-Patterns, falls die Registry nicht greift - default_patterns = ["", "", "[OUT]", "[/OUT]"] - - # Falls keine Registry übergeben wurde, versuchen wir sie zu laden - reg = registry or load_type_registry() - - # Lade Patterns aus llm_settings (WP-14 Erweiterung) - patterns: List[str] = reg.get("llm_settings", {}).get("cleanup_patterns", default_patterns) - - clean = text - for p in patterns: - clean = clean.replace(p, "") - - return clean.strip() +# ENTSCHEIDENDER FIX: Import der Basis-Logik aus dem neutralen Registry-Modul. +# Dies bricht den Zirkelbezug auf, da dieses Modul keine Services mehr importiert. +from app.core.registry import load_type_registry, clean_llm_text def extract_json_from_response(text: str, registry: Optional[dict] = None) -> Any: """ Extrahiert JSON-Daten und bereinigt LLM-Steuerzeichen. - WP-14: Nutzt nun die zentrale clean_llm_text Funktion. + WP-14: Nutzt nun die zentrale clean_llm_text Funktion aus app.core.registry. """ if not text: return [] - # 1. Text zentral bereinigen + # 1. Text zentral bereinigen via neutralem Modul clean = clean_llm_text(text, registry) # 2. Markdown-Code-Blöcke extrahieren @@ -65,16 +44,6 @@ def extract_json_from_response(text: str, registry: Optional[dict] = None) -> An except: pass return [] -def load_type_registry(custom_path: Optional[str] = None) -> dict: - """Lädt die types.yaml zur Steuerung der typ-spezifischen Ingestion.""" - from app.config import get_settings - settings = get_settings() - path = custom_path or settings.MINDNET_TYPES_FILE - if not os.path.exists(path): return {} - try: - with open(path, "r", encoding="utf-8") as f: return yaml.safe_load(f) or {} - except Exception: return {} - def resolve_note_type(registry: dict, requested: Optional[str]) -> str: """ Bestimmt den finalen Notiz-Typ. @@ -89,7 +58,9 @@ def resolve_note_type(registry: dict, requested: Optional[str]) -> str: return ingest_cfg.get("default_note_type", "concept") def get_chunk_config_by_profile(registry: dict, profile_name: str, note_type: str) -> Dict[str, Any]: - """Holt die Chunker-Parameter für ein spezifisches Profil aus der Registry.""" + """ + Holt die Chunker-Parameter für ein spezifisches Profil aus der Registry. + """ from app.core.chunking import get_chunk_config profiles = registry.get("chunking_profiles", {}) if profile_name in profiles: diff --git a/app/core/registry.py b/app/core/registry.py new file mode 100644 index 0000000..7b6a285 --- /dev/null +++ b/app/core/registry.py @@ -0,0 +1,43 @@ +""" +FILE: app/core/registry.py +DESCRIPTION: Zentraler Base-Layer für Konfigurations-Loading und Text-Bereinigung. + Bricht Zirkelbezüge zwischen Ingestion und LLMService auf. +VERSION: 1.0.0 +""" +import os +import yaml +from typing import Optional, List + +def load_type_registry(custom_path: Optional[str] = None) -> dict: + """Lädt die types.yaml zur Steuerung der typ-spezifischen Logik.""" + # Wir nutzen hier einen direkten Import von Settings, um Zyklen zu vermeiden + from app.config import get_settings + settings = get_settings() + path = custom_path or settings.MINDNET_TYPES_FILE + if not os.path.exists(path): + return {} + try: + with open(path, "r", encoding="utf-8") as f: + return yaml.safe_load(f) or {} + except Exception: + return {} + +def clean_llm_text(text: str, registry: Optional[dict] = None) -> str: + """ + Entfernt LLM-Steuerzeichen (, [OUT] etc.) aus einem Text. + Wird sowohl für JSON-Parsing als auch für Chat-Antworten genutzt. + """ + if not text or not isinstance(text, str): + return "" + + default_patterns = ["", "", "[OUT]", "[/OUT]"] + reg = registry or load_type_registry() + + # Lade Patterns aus llm_settings (WP-14) + patterns: List[str] = reg.get("llm_settings", {}).get("cleanup_patterns", default_patterns) + + clean = text + for p in patterns: + clean = clean.replace(p, "") + + return clean.strip() \ No newline at end of file diff --git a/app/services/llm_service.py b/app/services/llm_service.py index b5ce923..8027c3c 100644 --- a/app/services/llm_service.py +++ b/app/services/llm_service.py @@ -6,11 +6,11 @@ DESCRIPTION: Hybrid-Client für Ollama, Google GenAI (Gemini) und OpenRouter. WP-20 Fix: Bulletproof Prompt-Auflösung für format() Aufrufe. WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter structured outputs). FIX: Intelligente Rate-Limit Erkennung (429 Handling), v1-API Sync & Timeouts. -VERSION: 3.3.8 +VERSION: 3.3.9 STATUS: Active FIX: -- Integriert clean_llm_text zur Entfernung von Steuerzeichen (, [OUT] etc.) in Antworten. -- Stellt sicher, dass Chat-Antworten sauber formatiert ausgegeben werden. +- Importiert clean_llm_text von app.core.registry zur Vermeidung von Circular Imports. +- Wendet clean_llm_text auf Text-Antworten in generate_raw_response an. """ import httpx import yaml @@ -24,8 +24,8 @@ from pathlib import Path from typing import Optional, Dict, Any, Literal from app.config import get_settings -# Import der zentralen Bereinigungs-Logik (WP-14 Fix) -from app.core.ingestion.ingestion_utils import clean_llm_text +# ENTSCHEIDENDER FIX: Import der neutralen Bereinigungs-Logik (WP-14) +from app.core.registry import clean_llm_text logger = logging.getLogger(__name__) @@ -140,6 +140,7 @@ class LLMService: max_retries, base_delay, model_override, json_schema, json_schema_name, strict_json_schema ) + # WP-14 Fix: Bereinige Text-Antworten vor Rückgabe return clean_llm_text(res) if not force_json else res async def _dispatch( @@ -212,7 +213,6 @@ class LLMService: config = types.GenerateContentConfig( system_instruction=system, - # WICHTIG: Gemini 1.5+ unterstützt response_mime_type nativ response_mime_type="application/json" if force_json else "text/plain" ) response = await asyncio.wait_for(