146 lines
6.5 KiB
Python
146 lines
6.5 KiB
Python
# tests/test_smart_chunking_integration.py
|
|
|
|
import asyncio
|
|
import unittest
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List, Dict
|
|
|
|
# --- PFAD-KORREKTUR ---
|
|
# Fügt das Root-Verzeichnis zum Python-Pfad hinzu (wie zuvor besprochen)
|
|
ROOT_DIR = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT_DIR))
|
|
# ----------------------
|
|
|
|
# Import der Kernkomponenten
|
|
from app.core import chunker
|
|
from app.core import derive_edges
|
|
|
|
# WICHTIG: Wir importieren extract_frontmatter_from_text NICHT mehr aus
|
|
# note_payload.py, sondern entfernen den Import, da er für den Test nicht direkt nötig ist.
|
|
|
|
# ANNAHME: Der Test kann die Logik des Parsings und der Edge-Derivation nutzen,
|
|
# ohne note_payload direkt zu importieren.
|
|
|
|
# 1. Definieren der Test-Datei (Muss im Vault existieren, wenn es ein echter Integrationstest ist)
|
|
TEST_NOTE_ID = "20251211-journal-sem-test"
|
|
TEST_NOTE_TYPE = "journal"
|
|
|
|
TEST_MARKDOWN = """
|
|
---
|
|
id: 20251211-journal-sem-test
|
|
title: Tägliches Log - Semantischer Test
|
|
type: journal
|
|
status: active
|
|
created: 2025-12-11
|
|
tags: [test, daily-log]
|
|
---
|
|
# Log-Eintrag 2025-12-11
|
|
|
|
Heute war ein guter Tag. Zuerst habe ich mit der R1 Meditation begonnen, um meinen Nordstern Fokus zu klären. Das Ritual [[leitbild-rituale-system]] hat mir geholfen, ruhig in den Tag zu starten. Ich habe gespürt, wie wichtig meine [[leitbild-werte#Integrität]] für meine Entscheidungen ist. Das ist das Fundament.
|
|
|
|
Am Nachmittag gab es einen Konflikt bei der Karate-Trainer-Ausbildung. Ein Schüler war uneinsichtig. Ich habe die Situation nach [[leitbild-prinzipien#P4 Gerechtigkeit & Fairness]] behandelt und beide Seiten gehört (Steelman). Das war anstrengend, aber ich habe meine [[leitbild-rollen#Karate-Trainer]] Mission erfüllt. Die Konsequenz war klar und ruhig.
|
|
|
|
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.
|
|
"""
|
|
|
|
class TestSemanticChunking(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
# Setzt die Konfiguration auf den Typ 'journal'
|
|
self.config = chunker.get_chunk_config(TEST_NOTE_TYPE)
|
|
|
|
def test_a_strategy_selection(self):
|
|
"""Prüft, ob die Strategie 'semantic_llm' für den Typ 'journal' gewählt wird."""
|
|
self.assertEqual(self.config['strategy'], 'semantic_llm',
|
|
"Fehler: 'journal' sollte die Strategie 'semantic_llm' nutzen.")
|
|
|
|
def test_b_llm_chunking_and_injection(self):
|
|
"""
|
|
Prüft den gesamten End-to-End-Flow:
|
|
1. LLM-Chunking
|
|
2. Kanten-Injektion (als [[rel:...]])
|
|
3. Kanten-Erkennung durch derive_edges.py
|
|
"""
|
|
# --- 1. Chunking (Asynchron) ---
|
|
chunks = asyncio.run(chunker.assemble_chunks(
|
|
note_id=TEST_NOTE_ID,
|
|
md_text=TEST_MARKDOWN,
|
|
note_type=TEST_NOTE_TYPE
|
|
))
|
|
|
|
print(f"\n--- LLM Chunker Output: {len(chunks)} Chunks ---")
|
|
self.assertTrue(len(chunks) > 1,
|
|
"Erwartung: Das LLM sollte den Text in mehrere semantische Chunks zerlegen.")
|
|
|
|
# --- 2. Injektion prüfen (Der Chunk-Text muss die Links enthalten) ---
|
|
chunk_1_text = chunks[0].text
|
|
print(f"Chunk 1 Text (Anfang): {chunk_1_text[:100]}...")
|
|
self.assertIn("[[rel:", chunk_1_text,
|
|
"Fehler: Der Chunk-Text muss die injizierte [[rel: Kante enthalten.]")
|
|
|
|
# --- 3. Kanten-Derivation (Synchron) ---
|
|
edges = derive_edges.build_edges_for_note(
|
|
note_id=TEST_NOTE_ID,
|
|
chunks=[c.__dict__ for c in chunks] # Chunker-Objekte in Dicts konvertieren
|
|
)
|
|
|
|
print(f"--- Edge Derivation Output: {len(edges)} Kanten ---")
|
|
|
|
# 4. Assertions: Prüfen auf Existenz spezifischer, vom LLM generierter Kanten
|
|
|
|
llm_generated_edges = [
|
|
e for e in edges
|
|
if e.get('rule_id') == 'inline:rel' and e.get('source_id').startswith(TEST_NOTE_ID + '#sem')
|
|
]
|
|
|
|
print(f"Gefundene LLM-Kanten (inline:rel): {len(llm_generated_edges)}")
|
|
self.assertTrue(len(llm_generated_edges) >= 3,
|
|
"Erwartung: Mindestens 3 LLM-generierte Kanten (eine pro semantischem Abschnitt).")
|
|
|
|
# Check für die spezifische Kante 'uses' (oder 'based_on'/'derived_from' von der Matrix)
|
|
has_ritual_kante = any(
|
|
e['target_id'] == 'leitbild-rituale-system'
|
|
and e['source_id'].startswith(TEST_NOTE_ID + '#sem00')
|
|
for e in llm_generated_edges
|
|
)
|
|
self.assertTrue(has_ritual_kante,
|
|
"Fehler: Der LLM-Chunker hat die Kante zu 'leitbild-rituale-system' nicht korrekt an Chunk 1 gebunden.")
|
|
|
|
# Check für die Matrix-Logik (z.B. 'derived_from' zu 'leitbild-werte')
|
|
has_matrix_kante = any(
|
|
e['target_id'].startswith('leitbild-werte') and e['kind'] in ['based_on', 'derived_from', 'references']
|
|
for e in llm_generated_edges
|
|
)
|
|
self.assertTrue(has_matrix_kante,
|
|
"Fehler: Die Matrix-Logik wurde nicht aktiv oder das LLM hat die Werte-Kante nicht erkannt.")
|
|
|
|
print("\n✅ Integrationstest für Semantic Chunking erfolgreich.")
|
|
|
|
def test_c_draft_status_prevention(self):
|
|
"""Prüft, ob 'draft' Status semantic_llm auf by_heading überschreibt."""
|
|
|
|
DRAFT_MARKDOWN = TEST_MARKDOWN.replace("status: active", "status: draft")
|
|
|
|
# 1. Chunking mit Draft Status
|
|
chunks = asyncio.run(chunker.assemble_chunks(
|
|
note_id=TEST_NOTE_ID,
|
|
md_text=DRAFT_MARKDOWN,
|
|
note_type=TEST_NOTE_TYPE
|
|
))
|
|
|
|
# 2. Prüfen der Chunker-IDs
|
|
# Wenn LLM genutzt wird, ist die ID 'sem'. Wenn by_heading genutzt wird,
|
|
# ist die ID standardmäßig 'c' und die Logik ist anders.
|
|
|
|
self.assertFalse(chunks[0].id.startswith(TEST_NOTE_ID + '#sem'),
|
|
"Fehler: LLM-Chunking wurde für den Status 'draft' nicht verhindert (ID startet mit #sem).")
|
|
|
|
# Da 'by_heading' der Fallback ist, sollte die ID mit '#c' starten
|
|
self.assertTrue(chunks[0].id.startswith(TEST_NOTE_ID + '#c'),
|
|
"Fehler: Fallback-Strategie 'by_heading' wurde nicht korrekt ausgeführt.")
|
|
|
|
print(f"\n✅ Prevention Test: Draft-Status hat LLM-Chunking verhindert (Fallback ID: {chunks[0].id}).")
|
|
|
|
# --- Ende des Test-Skripts --- |