Anpassung der Textausgabe zur Filterung der Steuerzeichen

This commit is contained in:
Lars 2025-12-27 18:59:38 +01:00
parent cd5383432e
commit e045371969
2 changed files with 40 additions and 20 deletions

View File

@ -1,37 +1,49 @@
""" """
FILE: app/core/ingestion/ingestion_utils.py FILE: app/core/ingestion/ingestion_utils.py
DESCRIPTION: Hilfswerkzeuge für JSON-Recovery, Typ-Registry und Konfigurations-Lookups. DESCRIPTION: Hilfswerkzeuge für JSON-Recovery, Typ-Registry und Konfigurations-Lookups.
AUDIT v2.13.7: Dynamisierung von Cleanup-Patterns und Default-Typen (WP-14). AUDIT v2.13.8: Zentralisierung der Text-Bereinigung für LLM-Antworten.
""" """
import os import os
import json import json
import re import re
import yaml import yaml
from typing import Any, Optional, Dict from typing import Any, Optional, Dict, List
def extract_json_from_response(text: str, registry: Optional[dict] = None) -> Any: def clean_llm_text(text: str, registry: Optional[dict] = None) -> str:
""" """
Extrahiert JSON-Daten und bereinigt LLM-Steuerzeichen (v2.11.14 Logic). Entfernt LLM-Steuerzeichen und Artefakte aus einem Text.
WP-14: Nutzt nun dynamische cleanup_patterns aus der Registry. Nutzt die cleanup_patterns aus der Registry oder Standardwerte.
""" """
if not text or not isinstance(text, str): if not text or not isinstance(text, str):
return [] return ""
# Fallback-Patterns für die Bereinigung # Fallback-Patterns, falls die Registry nicht greift
patterns = ["<s>", "</s>", "[OUT]", "[/OUT]"] default_patterns = ["<s>", "</s>", "[OUT]", "[/OUT]"]
# Falls keine Registry übergeben wurde, versuchen wir sie zu laden # Falls keine Registry übergeben wurde, versuchen wir sie zu laden
reg = registry or load_type_registry() reg = registry or load_type_registry()
if reg:
# Lade Patterns aus llm_settings (WP-14 Erweiterung) # Lade Patterns aus llm_settings (WP-14 Erweiterung)
patterns = reg.get("llm_settings", {}).get("cleanup_patterns", patterns) patterns: List[str] = reg.get("llm_settings", {}).get("cleanup_patterns", default_patterns)
clean = text clean = text
for p in patterns: for p in patterns:
clean = clean.replace(p, "") clean = clean.replace(p, "")
clean = clean.strip() return clean.strip()
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.
"""
if not text:
return []
# 1. Text zentral bereinigen
clean = clean_llm_text(text, registry)
# 2. Markdown-Code-Blöcke extrahieren
match = re.search(r"```(?:json)?\s*(.*?)\s*```", clean, re.DOTALL) match = re.search(r"```(?:json)?\s*(.*?)\s*```", clean, re.DOTALL)
payload = match.group(1) if match else clean payload = match.group(1) if match else clean

View File

@ -6,12 +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-20 Fix: Bulletproof Prompt-Auflösung für format() Aufrufe.
WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter structured outputs). WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter structured outputs).
FIX: Intelligente Rate-Limit Erkennung (429 Handling), v1-API Sync & Timeouts. FIX: Intelligente Rate-Limit Erkennung (429 Handling), v1-API Sync & Timeouts.
VERSION: 3.3.7 VERSION: 3.3.8
STATUS: Active STATUS: Active
FIX: FIX:
- Implementiert striktes max_retries Handling für alle Provider (v.a. für Chat-Stabilität). - Integriert clean_llm_text zur Entfernung von Steuerzeichen (<s>, [OUT] etc.) in Antworten.
- Synchronisiert Rate-Limit Retries mit dem max_retries Parameter. - Stellt sicher, dass Chat-Antworten sauber formatiert ausgegeben werden.
- Optimiert Logging für sofortige Fehlererkennung.
""" """
import httpx import httpx
import yaml import yaml
@ -25,6 +24,9 @@ from pathlib import Path
from typing import Optional, Dict, Any, Literal from typing import Optional, Dict, Any, Literal
from app.config import get_settings from app.config import get_settings
# Import der zentralen Bereinigungs-Logik (WP-14 Fix)
from app.core.ingestion.ingestion_utils import clean_llm_text
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -119,22 +121,26 @@ class LLMService:
) -> str: ) -> str:
""" """
Haupteinstiegspunkt für LLM-Anfragen mit Priorisierung. Haupteinstiegspunkt für LLM-Anfragen mit Priorisierung.
Wendet die Bereinigung auf Text-Antworten an.
""" """
target_provider = provider or self.settings.MINDNET_LLM_PROVIDER target_provider = provider or self.settings.MINDNET_LLM_PROVIDER
if priority == "background": if priority == "background":
async with LLMService._background_semaphore: async with LLMService._background_semaphore:
return await self._dispatch( res = await self._dispatch(
target_provider, prompt, system, force_json, target_provider, prompt, system, force_json,
max_retries, base_delay, model_override, max_retries, base_delay, model_override,
json_schema, json_schema_name, strict_json_schema 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
return await self._dispatch( res = await self._dispatch(
target_provider, prompt, system, force_json, target_provider, prompt, system, force_json,
max_retries, base_delay, model_override, max_retries, base_delay, model_override,
json_schema, json_schema_name, strict_json_schema json_schema, json_schema_name, strict_json_schema
) )
return clean_llm_text(res) if not force_json else res
async def _dispatch( async def _dispatch(
self, self,
@ -206,6 +212,7 @@ class LLMService:
config = types.GenerateContentConfig( config = types.GenerateContentConfig(
system_instruction=system, 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_mime_type="application/json" if force_json else "text/plain"
) )
response = await asyncio.wait_for( response = await asyncio.wait_for(
@ -297,6 +304,7 @@ class LLMService:
final_prompt = rag_template.format(context_str=context_str, query=query) final_prompt = rag_template.format(context_str=context_str, query=query)
# RAG Aufrufe im Chat nutzen nun standardmäßig max_retries=2 (überschreibbar) # RAG Aufrufe im Chat nutzen nun standardmäßig max_retries=2 (überschreibbar)
# Durch den Aufruf von generate_raw_response wird die Bereinigung automatisch angewendet.
return await self.generate_raw_response( return await self.generate_raw_response(
final_prompt, final_prompt,
system=system_prompt, system=system_prompt,