WP15 testscript
This commit is contained in:
parent
f6d8751f23
commit
d59bd1885d
|
|
@ -1,4 +1,4 @@
|
||||||
# tests/test_smart_chunking_integration.py
|
# tests/test_smart_chunking_integration.py (Final für Stabilität)
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import unittest
|
import unittest
|
||||||
|
|
@ -6,9 +6,9 @@ import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
|
import time # Nur für debug/sleep
|
||||||
|
|
||||||
# --- PFAD-KORREKTUR ---
|
# --- PFAD-KORREKTUR ---
|
||||||
# Fügt das Root-Verzeichnis zum Python-Pfad hinzu
|
|
||||||
ROOT_DIR = Path(__file__).resolve().parent.parent
|
ROOT_DIR = Path(__file__).resolve().parent.parent
|
||||||
sys.path.insert(0, str(ROOT_DIR))
|
sys.path.insert(0, str(ROOT_DIR))
|
||||||
# ----------------------
|
# ----------------------
|
||||||
|
|
@ -16,8 +16,8 @@ sys.path.insert(0, str(ROOT_DIR))
|
||||||
# Import der Kernkomponenten
|
# Import der Kernkomponenten
|
||||||
from app.core import chunker
|
from app.core import chunker
|
||||||
from app.core import derive_edges
|
from app.core import derive_edges
|
||||||
from app.services.semantic_analyzer import SemanticAnalyzer # Import der Klasse für die Instanziierung
|
from app.services.semantic_analyzer import SemanticAnalyzer
|
||||||
|
from app.core.chunker import extract_frontmatter_from_text # Wichtig für Status Check
|
||||||
|
|
||||||
# 1. Definieren der Test-Note (Simuliert eine journal.md Datei)
|
# 1. Definieren der Test-Note (Simuliert eine journal.md Datei)
|
||||||
TEST_NOTE_ID = "20251211-journal-sem-test"
|
TEST_NOTE_ID = "20251211-journal-sem-test"
|
||||||
|
|
@ -41,9 +41,13 @@ Am Nachmittag gab es einen Konflikt bei der Karate-Trainer-Ausbildung. Ein Schü
|
||||||
Abends habe ich den wöchentlichen Load-Check mit meinem Partner gemacht. Das Paar-Ritual [[leitbild-rituale-system#R5]] hilft, das Ziel [[leitbild-ziele-portfolio#Nordstern Partner]] aktiv zu verfolgen. Es ist der operative Rhythmus für uns beide.
|
Abends habe ich den wöchentlichen Load-Check mit meinem Partner gemacht. Das Paar-Ritual [[leitbild-rituale-system#R5]] hilft, das Ziel [[leitbild-ziele-portfolio#Nordstern Partner]] aktiv zu verfolgen. Es ist der operative Rhythmus für uns beide.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Helper, um den Client im vorhandenen Loop zu schließen (Muss in der Klasse sein)
|
||||||
|
async def _async_close_analyzer(analyzer):
|
||||||
|
await analyzer.close()
|
||||||
|
|
||||||
class TestSemanticChunking(unittest.TestCase):
|
class TestSemanticChunking(unittest.TestCase):
|
||||||
|
|
||||||
# 2. Ressourcen-Management (Schließt den httpx.AsyncClient sauber)
|
# 2. Ressourcen-Management
|
||||||
_analyzer_instance = None
|
_analyzer_instance = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -51,18 +55,24 @@ class TestSemanticChunking(unittest.TestCase):
|
||||||
"""Initialisiert den SemanticAnalyzer einmalig und asynchron."""
|
"""Initialisiert den SemanticAnalyzer einmalig und asynchron."""
|
||||||
# Da LLMService async ist, nutzen wir die Singleton-Instanz der Klasse
|
# Da LLMService async ist, nutzen wir die Singleton-Instanz der Klasse
|
||||||
cls._analyzer_instance = SemanticAnalyzer()
|
cls._analyzer_instance = SemanticAnalyzer()
|
||||||
# Stellen Sie sicher, dass der Chunker diese Instanz verwenden kann.
|
# Stellt sicher, dass der Chunker diese Instanz verwenden kann.
|
||||||
# Dies ist im chunker.py Code über _get_semantic_analyzer_instance() abgedeckt.
|
|
||||||
chunker._semantic_analyzer_instance = cls._analyzer_instance
|
chunker._semantic_analyzer_instance = cls._analyzer_instance
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
"""Schließt den httpx.AsyncClient nach allen Tests."""
|
"""Schließt den httpx.AsyncClient nach allen Tests (Korrektur des Event Loop Fehlers)."""
|
||||||
if cls._analyzer_instance:
|
if cls._analyzer_instance:
|
||||||
asyncio.run(cls._analyzer_instance.close())
|
# Wir nutzen run_coroutine_threadsafe, da der Loop geschlossen ist
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
if loop.is_running():
|
||||||
|
# Wenn Loop noch läuft, planen wir den Aufruf
|
||||||
|
asyncio.run_coroutine_threadsafe(cls._analyzer_instance.close(), loop)
|
||||||
|
else:
|
||||||
|
# Andernfalls starten wir einen neuen temporären Loop nur zum Schließen
|
||||||
|
asyncio.run(cls._analyzer_instance.close())
|
||||||
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Lädt die Konfiguration, um die Strategie zu prüfen
|
|
||||||
self.config = chunker.get_chunk_config(TEST_NOTE_TYPE)
|
self.config = chunker.get_chunk_config(TEST_NOTE_TYPE)
|
||||||
|
|
||||||
def test_a_strategy_selection(self):
|
def test_a_strategy_selection(self):
|
||||||
|
|
@ -72,10 +82,8 @@ class TestSemanticChunking(unittest.TestCase):
|
||||||
|
|
||||||
def test_b_llm_chunking_and_injection(self):
|
def test_b_llm_chunking_and_injection(self):
|
||||||
"""
|
"""
|
||||||
Prüft den gesamten End-to-End-Flow:
|
Prüft den gesamten End-to-End-Flow: 1. LLM-Chunking, 2. Kanten-Injektion, 3. Kanten-Erkennung.
|
||||||
1. LLM-Chunking (muss > 1 Chunk liefern)
|
(Diese Tests setzen voraus, dass das LLM JSON liefert)
|
||||||
2. Kanten-Injektion (als [[rel:...]])
|
|
||||||
3. Kanten-Erkennung durch derive_edges.py
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# --- 1. Chunking (Asynchron) ---
|
# --- 1. Chunking (Asynchron) ---
|
||||||
|
|
@ -87,11 +95,11 @@ class TestSemanticChunking(unittest.TestCase):
|
||||||
|
|
||||||
print(f"\n--- LLM Chunker Output: {len(chunks)} Chunks ---")
|
print(f"\n--- LLM Chunker Output: {len(chunks)} Chunks ---")
|
||||||
|
|
||||||
# Assertion B1: Zerlegung (Die Fallback-Logik des LLM liefert bei Fehler 1 Chunk)
|
# Assertion B1: Zerlegung (Das LLM muss mehr als 1 Chunk liefern)
|
||||||
self.assertTrue(len(chunks) > 1,
|
self.assertTrue(len(chunks) > 1,
|
||||||
"Assertion B1 Fehler: Das LLM sollte den Text in mehrere semantische Chunks zerlegen.")
|
"Assertion B1 Fehler: LLM hat nicht zerlegt (Fallback aktiv). Prüfe LLM-Stabilität.")
|
||||||
|
|
||||||
# --- 2. Injektion prüfen (Der Chunk-Text muss die Links enthalten) ---
|
# --- 2. Injektion prüfen ---
|
||||||
chunk_1_text = chunks[0].text
|
chunk_1_text = chunks[0].text
|
||||||
self.assertIn("[[rel:", chunk_1_text,
|
self.assertIn("[[rel:", chunk_1_text,
|
||||||
"Assertion B2 Fehler: Der Chunk-Text muss die injizierte [[rel: Kante enthalten.")
|
"Assertion B2 Fehler: Der Chunk-Text muss die injizierte [[rel: Kante enthalten.")
|
||||||
|
|
@ -104,18 +112,16 @@ class TestSemanticChunking(unittest.TestCase):
|
||||||
|
|
||||||
print(f"--- Edge Derivation Output: {len(edges)} Kanten ---")
|
print(f"--- Edge Derivation Output: {len(edges)} Kanten ---")
|
||||||
|
|
||||||
# 4. Assertions: Prüfen auf Existenz spezifischer, vom LLM generierter Kanten
|
# Assertion B3: Mindestens 3 LLM-Kanten (inline:rel)
|
||||||
llm_generated_edges = [
|
llm_generated_edges = [
|
||||||
e for e in edges
|
e for e in edges
|
||||||
if e.get('rule_id') == 'inline:rel' and e.get('source_id').startswith(TEST_NOTE_ID + '#sem')
|
if e.get('rule_id') == 'inline:rel' and e.get('source_id').startswith(TEST_NOTE_ID + '#sem')
|
||||||
]
|
]
|
||||||
|
|
||||||
# Assertion B3: Mindestens 3 LLM-Kanten (eine pro semantischem Abschnitt)
|
|
||||||
self.assertTrue(len(llm_generated_edges) >= 3,
|
self.assertTrue(len(llm_generated_edges) >= 3,
|
||||||
"Assertion B3 Fehler: Mindestens 3 LLM-generierte Kanten (eine pro semantischem Abschnitt).")
|
"Assertion B3 Fehler: Es wurden weniger als 3 semantische Kanten gefunden.")
|
||||||
|
|
||||||
# Assertion B4: Check für die Matrix-Logik / Werte-Kante (Chunk 1)
|
# Assertion B4: Check für die Matrix-Logik / Werte-Kante
|
||||||
# Erwartet: derived_from oder based_on zu 'leitbild-werte'
|
|
||||||
has_matrix_kante = any(
|
has_matrix_kante = any(
|
||||||
e['target_id'].startswith('leitbild-werte') and e['kind'] in ['based_on', 'derived_from']
|
e['target_id'].startswith('leitbild-werte') and e['kind'] in ['based_on', 'derived_from']
|
||||||
for e in llm_generated_edges
|
for e in llm_generated_edges
|
||||||
|
|
@ -149,5 +155,5 @@ class TestSemanticChunking(unittest.TestCase):
|
||||||
print(f"\n✅ Prevention Test: Draft-Status hat LLM-Chunking verhindert (Fallback ID: {chunks[0].id}).")
|
print(f"\n✅ Prevention Test: Draft-Status hat LLM-Chunking verhindert (Fallback ID: {chunks[0].id}).")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print("Starte den Semantic Chunking Integrationstest. Stelle sicher, dass Ollama und die Konfiguration korrekt sind.")
|
print("Starte den Semantic Chunking Integrationstest.")
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Loading…
Reference in New Issue
Block a user