From e9532e8878b1c43232cf3e22e2b5cdeeaf0f26cf Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 28 Dec 2025 10:40:28 +0100 Subject: [PATCH] =?UTF-8?q?script=5F=C3=9Cberpr=C3=BCfung=20und=20Kommenta?= =?UTF-8?q?rheader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/assert_payload_schema.py | 59 +++++- scripts/audit_chunks.py | 60 ++++++ scripts/audit_edges_vs_expectations.py | 80 +++++--- scripts/audit_vault_vs_qdrant.py | 73 ++++--- scripts/backfill_edges.py | 57 ++++++ scripts/debug_edge_loss.py | 95 +++++++++- scripts/debug_note_payload.py | 58 +++++- scripts/debug_qdrant_state.py | 53 +++++- scripts/diag_payload_indexes.py | 55 +++++- scripts/dump_note_chunks.py | 54 ++++++ scripts/edges_dryrun.py | 78 ++++++-- scripts/edges_full_check.py | 69 +++++-- scripts/export_markdown.py | 91 ++++++--- scripts/fix_frontmatter.py | 66 ++++++- scripts/gc_qdrant_after_vault_scan.py | 115 ++++++----- scripts/health_check_mindnet.py | 86 ++++++--- scripts/import_markdown.py | 70 ++++++- scripts/make_test_vault.py | 77 +++++--- scripts/ollama_tool_runner.py | 67 +++++-- scripts/parse_validate_notes.py | 63 ++++++- scripts/payload_dryrun.py | 231 +++++++++++++++-------- scripts/preview_chunks.py | 56 ++++++ scripts/prune_qdrant_vs_vault.py | 124 ++++++++---- scripts/report_hashes.py | 64 +++++-- scripts/reset_qdrant.py | 96 ++++++---- scripts/resolve_unresolved_references.py | 70 +++++-- scripts/setup_mindnet_collections.py | 68 +++++-- scripts/validate_edges.py | 73 +++++-- scripts/verify_chunk_texts.py | 99 ++++++---- scripts/wp04_smoketest.py | 70 +++++-- 30 files changed, 1835 insertions(+), 542 deletions(-) diff --git a/scripts/assert_payload_schema.py b/scripts/assert_payload_schema.py index c5cc279..b67367e 100644 --- a/scripts/assert_payload_schema.py +++ b/scripts/assert_payload_schema.py @@ -1,18 +1,59 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -tests/assert_payload_schema.py (compat v1.1) +FILE: scripts/assert_payload_schema.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Prüft, ob die erwarteten Payload-Indizes (payload_schema) auf den drei Collections vorhanden sind. -Kompatibel mit älteren qdrant-client Versionen (ohne with_payload_schema im Wrapper). +Zweck: +------- +Prüft, ob die erwarteten Payload-Indizes auf allen Collections vorhanden sind. +Validiert Schema-Integrität für CI/CD und Wartung. -Collections & Pflichtfelder: -- mindnet_notes : note_id, type, title, updated, tags -- mindnet_chunks : note_id, chunk_id, index, type, tags -- mindnet_edges : note_id, kind, scope, source_id, target_id, chunk_id +Funktionsweise: +--------------- +1. Ermittelt Collections für das Präfix (notes, chunks, edges) +2. Lädt Payload-Schema für jede Collection +3. Prüft, ob alle Pflichtfelder indiziert sind: + - notes: note_id, type, title, updated, tags + - chunks: note_id, chunk_id, index, type, tags + - edges: note_id, kind, scope, source_id, target_id, chunk_id +4. Gibt Validierungs-Ergebnis aus -Nutzung: - python3 -m tests.assert_payload_schema +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Validierungs-Ergebnissen + * collections: Schema-Status pro Collection + * missing: Fehlende Indizes + * valid: true/false +- Exit-Code 0: Alle Indizes vorhanden +- Exit-Code 1: Fehlende Indizes gefunden + +Verwendung: +----------- +- CI/CD-Validierung nach ensure_payload_indexes +- Diagnose von Index-Problemen +- Wartung und Audit + +Hinweise: +--------- +- Kompatibel mit verschiedenen qdrant-client Versionen +- Nutzt Fallback-Strategie für Schema-Abfrage + +Aufruf: +------- +python3 -m scripts.assert_payload_schema --prefix mindnet + +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.1.0: Kompatibilität mit verschiedenen qdrant-client Versionen +v1.0.0: Initial Release """ from __future__ import annotations diff --git a/scripts/audit_chunks.py b/scripts/audit_chunks.py index 65ac7a1..5c13521 100644 --- a/scripts/audit_chunks.py +++ b/scripts/audit_chunks.py @@ -1,4 +1,64 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/audit_chunks.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Audit-Tool zur Analyse der Chunk-Qualität im Vault. +Erkennt Probleme wie überdimensionierte Chunks, leere Chunks und defekte Nachbarschafts-Links. + +Funktionsweise: +--------------- +1. Scannt alle Markdown-Dateien im Vault +2. Für jede Datei: + - Erzeugt Chunks via assemble_chunks + - Prüft Token-Anzahl pro Chunk + - Validiert Nachbarschafts-Links (prev/next) +3. Aggregiert Statistiken und identifiziert Probleme + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Zusammenfassung + * notes: Anzahl verarbeiteter Notizen + * chunks_total: Gesamtanzahl Chunks + * chunks_per_note_avg: Durchschnittliche Chunks pro Note + * tokens_avg: Durchschnittliche Token pro Chunk + * tokens_p95: 95. Perzentil der Token-Verteilung + * issues_counts: Anzahl gefundener Probleme +- Zusätzlich: Liste der ersten 20 Probleme pro Kategorie + * oversize: Chunks > 600 Tokens + * broken_neighbors: Defekte prev/next Links + * empty: Leere Chunks (0 Tokens) + +Verwendung: +----------- +- Qualitätskontrolle nach Chunking-Änderungen +- Identifikation von Problemen vor dem Import +- Monitoring der Chunk-Verteilung + +Hinweise: +--------- +- Nutzt synchrones assemble_chunks (kann async sein) +- Heuristik für Oversize: > 600 Tokens +- Prüft nur strukturelle Integrität, keine semantische Qualität + +Aufruf: +------- +python3 -m scripts.audit_chunks --vault ./vault + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release +""" from __future__ import annotations import argparse, os, json, glob, statistics as stats from app.core.parser import read_markdown, normalize_frontmatter, validate_required_frontmatter diff --git a/scripts/audit_edges_vs_expectations.py b/scripts/audit_edges_vs_expectations.py index 389ccea..89f611e 100644 --- a/scripts/audit_edges_vs_expectations.py +++ b/scripts/audit_edges_vs_expectations.py @@ -1,34 +1,66 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Script: audit_edges_vs_expectations.py — Prüfe Kanten in Qdrant gegen Vault-Erwartungen -Version: 1.0.0 -Datum: 2025-09-09 +FILE: scripts/audit_edges_vs_expectations.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Zweck ------ -- Liest Edges/Chunks/Notes aus Qdrant. -- Ermittelt erwartete Kanten-Anzahlen aus dem Vault: - * belongs_to : sollte == #Chunks - * next / prev : je Note (#Chunks_in_Note - 1) - * references : Summe aller Chunk-Wikilinks - * backlink : Summe einzigartiger Wikilinks pro Note (Note-Level) -- Vergleicht IST vs. SOLL und meldet Abweichungen. - -ENV/Qdrant ----------- -QDRANT_URL, QDRANT_API_KEY (optional), COLLECTION_PREFIX (Default: mindnet) - -Aufrufe +Zweck: ------- - # Gesamtaudit - python3 -m scripts.audit_edges_vs_expectations --vault ./test_vault +Prüft Edge-Anzahlen in Qdrant gegen erwartete Werte aus dem Vault. +Validiert strukturelle Integrität des Graphen. - # Mit anderem Prefix - python3 -m scripts.audit_edges_vs_expectations --vault ./test_vault --prefix mindnet_dev +Funktionsweise: +--------------- +1. Liest Edges/Chunks/Notes aus Qdrant +2. Ermittelt erwartete Edge-Anzahlen aus Vault: + - belongs_to: sollte == #Chunks pro Note + - next/prev: je Note (#Chunks - 1) + - references: Summe aller Chunk-Wikilinks + - backlink: Summe einzigartiger Wikilinks (Note-Level) +3. Vergleicht IST vs. SOLL +4. Meldet Abweichungen - # Details anzeigen - python3 -m scripts.audit_edges_vs_expectations --vault ./test_vault --details +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Vergleichs-Ergebnissen + * expected: Erwartete Edge-Anzahlen + * actual: Tatsächliche Edge-Anzahlen + * discrepancies: Abweichungen + * per_note: Details pro Note (mit --details) +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Validierung nach Importen +- Debugging von Edge-Problemen +- Qualitätskontrolle + +Hinweise: +--------- +- Prüft strukturelle, nicht semantische Korrektheit +- Kann bei großen Vaults langsam sein + +Aufruf: +------- +python3 -m scripts.audit_edges_vs_expectations --vault ./vault +python3 -m scripts.audit_edges_vs_expectations --vault ./vault --prefix mindnet_dev --details + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--prefix TEXT Collection-Präfix (Default: mindnet) +--details Zeigt Details pro Note + +Umgebungsvariablen: +------------------- +QDRANT_URL, QDRANT_API_KEY (optional), COLLECTION_PREFIX + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0 (2025-09-09): Initial Release """ from __future__ import annotations diff --git a/scripts/audit_vault_vs_qdrant.py b/scripts/audit_vault_vs_qdrant.py index b295b3c..050d0ab 100644 --- a/scripts/audit_vault_vs_qdrant.py +++ b/scripts/audit_vault_vs_qdrant.py @@ -1,38 +1,61 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Name: scripts/audit_vault_vs_qdrant.py -Version: v1.0.0 (2025-09-05) -Kurzbeschreibung: - Prüft die Konsistenz zwischen Obsidian-Vault und Qdrant: - - Zählt Markdown-Dateien mit gültiger Frontmatter (title, id, type, status, created). - - Zählt Wikilink-Vorkommen im Vault (regex wie in derive_edges.py). - - Liest Zählungen aus Qdrant (Notes/Chunks/Edges je kind). - - Vergleicht erwartete Wikilink-Anzahl (Vault) vs. tatsächlich importierte Edges (Qdrant). - - Listet Auffälligkeiten pro Note (z. B. Wikilinks im Vault, aber keine references in Qdrant). +FILE: scripts/audit_vault_vs_qdrant.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Aufruf (aus Projekt-Root, im venv): - python3 -m scripts.audit_vault_vs_qdrant --vault ./vault --prefix mindnet +Zweck: +------- +Prüft Konsistenz zwischen Vault und Qdrant. +Vergleicht erwartete vs. tatsächliche Edge-Anzahlen. -Parameter: - --vault Pfad zum Vault (z. B. ./vault) - --prefix Collection-Prefix in Qdrant (Default: mindnet) - --limit Max. Punkte pro Scroll-Seite aus Qdrant (Default: 1000) +Funktionsweise: +--------------- +1. Scannt Vault: + - Zählt Markdown-Dateien mit gültiger Frontmatter + - Zählt Wikilink-Vorkommen (regex wie in derive_edges.py) +2. Liest Qdrant: + - Zählt Notes/Chunks/Edges + - Gruppiert Edges nach kind +3. Vergleicht: + - Erwartete Wikilink-Anzahl (Vault) vs. references (Qdrant) + - Listet Auffälligkeiten pro Note -Voraussetzungen: - - Aktives Python venv mit installiertem qdrant-client. - - Zugriff auf Qdrant per ENV (QDRANT_URL, QDRANT_API_KEY optional). +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Vergleichs-Ergebnissen + * vault_stats: Zählungen aus Vault + * qdrant_stats: Zählungen aus Qdrant + * discrepancies: Abweichungen und Auffälligkeiten +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Konsistenz-Check nach Importen +- Validierung der Edge-Erzeugung +- Debugging von fehlenden Links Hinweise: - - Der Wikilink-Regex entspricht dem in app/core/derive_edges.py verwendeten Muster. (Quelle: derive_edges.py) # :contentReference[oaicite:3]{index=3} - - Pflicht-Frontmatter wird wie in app/core/parser.py geprüft. (Quelle: parser.py) # :contentReference[oaicite:4]{index=4} - - Collection-Namen & 1D-Edge-Vektoren folgen app/core/qdrant.py / qdrant_points.py. (Quellen: qdrant.py, qdrant_points.py) # +--------- +- Wikilink-Regex entspricht derive_edges.py +- Prüft strukturelle, nicht semantische Korrektheit -Changelog: - v1.0.0: Erste Version. +Aufruf: +------- +python3 -m scripts.audit_vault_vs_qdrant --vault ./vault --prefix mindnet -Autor: - mindnet – Datenimporte & Sync +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--prefix TEXT Collection-Präfix (Default: mindnet) +--limit INT Max. Punkte pro Scroll-Seite (Default: 1000) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0 (2025-09-05): Initial Release """ from __future__ import annotations import argparse, os, glob, re, json diff --git a/scripts/backfill_edges.py b/scripts/backfill_edges.py index a5cd16a..5699077 100644 --- a/scripts/backfill_edges.py +++ b/scripts/backfill_edges.py @@ -1,4 +1,61 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/backfill_edges.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Füllt fehlende Edges nach, indem es Wikilinks aus dem Vault extrahiert +und in Qdrant schreibt. Nützlich nach Migrationen oder wenn Edges verloren gingen. + +Funktionsweise: +--------------- +1. Scannt alle Markdown-Dateien im Vault +2. Für jede Datei: + - Erstellt Note-Stub (minimaler Payload) + - Erstellt einen Chunk mit dem gesamten Body + - Extrahiert Wikilinks via derive_wikilink_edges +3. Baut Lookup-Index (Titel/Alias -> note_id) +4. Löst Wikilinks auf und erzeugt Edges +5. Schreibt Edges in Qdrant + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Statistiken + * notes_scanned: Anzahl verarbeiteter Notizen + * edges_upserted: Anzahl geschriebener Edges +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Nach Migrationen oder Datenverlust +- Wenn Edges fehlen oder unvollständig sind +- Zur Reparatur des Graphen + +Hinweise: +--------- +- Überschreibt existierende Edges (kein Merge) +- Nutzt vereinfachtes Chunking (1 Chunk = gesamter Body) +- Erzeugt nur Wikilink-Edges, keine anderen Edge-Typen + +Aufruf: +------- +python3 -m scripts.backfill_edges --vault ./vault +python3 -m scripts.backfill_edges --vault ./vault --exclude /.obsidian/ /_backup/ + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--exclude PATH Pfade zum Ausschließen (Default: /.obsidian/, /_backup_frontmatter/) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release +""" from __future__ import annotations import argparse, glob, json, os from typing import List, Tuple diff --git a/scripts/debug_edge_loss.py b/scripts/debug_edge_loss.py index ed91423..6074a36 100644 --- a/scripts/debug_edge_loss.py +++ b/scripts/debug_edge_loss.py @@ -1,3 +1,67 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/debug_edge_loss.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active (Debug-Tool) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Debug-Tool zur Analyse von Edge-Verlust-Problemen bei der Chunk-Verarbeitung. +Hilft zu verstehen, warum bestimmte Edges nicht erkannt oder verloren gehen. + +Funktionsweise: +--------------- +1. Analysiert eine einzelne Markdown-Datei +2. Führt zwei Analyseschritte durch: + a) Pre-Scan: Extrahiert alle Edge-Kandidaten aus dem rohen Text + - Wikilinks + - Typed Relations + - Callout Relations + b) Chunk-Analyse: + - Erstellt Chunks mit konfigurierbarem Profil + - Prüft, welche Edges in jedem Chunk gefunden werden + - Vergleicht mit Pre-Scan-Ergebnissen +3. Gibt detaillierte Analyse pro Chunk aus + +Ergebnis-Interpretation: +------------------------ +- [1] Globale Kandidaten: Alle Edge-Kandidaten, die im Text vorhanden sind +- [2] Chunk-Struktur: Für jeden Chunk: + * Section-Pfad + * Text-Snippet + * Gefundene explizite Kanten (callout:edge, inline:rel) + * Warnung, falls Callout im Text vorhanden, aber nicht erkannt + +Verwendung: +----------- +- Debugging von Edge-Verlust bei Chunk-Grenzen +- Validierung der Edge-Extraktion in verschiedenen Chunk-Kontexten +- Analyse von Problemen mit Callout-Erkennung + +Hinweise: +--------- +- Asynchrones Skript (nutzt assemble_chunks async API) +- Konfigurierbares Chunking-Profil (Standard: sliding_window, 400/600 chars) +- Smart Edge Allocation ist standardmäßig deaktiviert für klare Analyse + +Aufruf: +------- +python3 scripts/debug_edge_loss.py [PFAD_ZUR_DATEI.md] + +Parameter: +---------- +Keine CLI-Parameter. Dateipfad als erstes Argument oder Standard-Pfad. + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Entfernt: _extract_all_edges_from_md (nicht mehr vorhanden) + - Ersetzt durch: extract_wikilinks, extract_typed_relations, extract_callout_relations + - Chunk-Payload-Struktur angepasst (window, type hinzugefügt) +v1.0.0: Erster Release +""" import asyncio import os import sys @@ -6,8 +70,10 @@ from pathlib import Path # Pfad-Setup sys.path.insert(0, os.path.abspath(".")) -from app.core.chunking import assemble_chunks, _extract_all_edges_from_md +from app.core.chunking import assemble_chunks from app.core.derive_edges import build_edges_for_note +from app.core.parser import extract_wikilinks +from app.core.graph.graph_extractors import extract_typed_relations, extract_callout_relations # Mock für Settings, falls nötig os.environ["MINDNET_LLM_MODEL"] = "phi3:mini" @@ -19,9 +85,25 @@ async def analyze_file(file_path: str): text = f.read() # 1. Globale Kandidaten (Was sieht der Pre-Scan?) - # Wir simulieren den Aufruf, den der Chunker macht + # Wir extrahieren alle Edge-Kandidaten aus dem Text note_id = Path(file_path).stem - candidates = _extract_all_edges_from_md(text, note_id, "concept") + candidates = [] + + # Wikilinks extrahieren + wikilinks = extract_wikilinks(text) + for wl in wikilinks: + candidates.append(f"wikilink:{wl}") + + # Typed Relations extrahieren + typed_rels, _ = extract_typed_relations(text) + for kind, target in typed_rels: + candidates.append(f"typed:{kind}:{target}") + + # Callout Relations extrahieren + callout_rels, _ = extract_callout_relations(text) + for kind, target in callout_rels: + candidates.append(f"callout:{kind}:{target}") + print(f"\n[1] Globale Kandidaten (Pre-Scan):") for c in candidates: print(f" - {c}") @@ -45,7 +127,12 @@ async def analyze_file(file_path: str): # Was findet derive_edges in diesem rohen Chunk? # Wir simulieren das Payload-Dict, das derive_edges erwartet - chunk_pl = {"text": chunk.text, "window": chunk.window, "chunk_id": chunk.id} + chunk_pl = { + "text": chunk.text, + "window": chunk.window or chunk.text, + "chunk_id": chunk.id, + "type": "concept" + } edges = build_edges_for_note(note_id, [chunk_pl]) found_explicitly = [f"{e['kind']}:{e.get('target_id')}" for e in edges if e['rule_id'] in ['callout:edge', 'inline:rel']] diff --git a/scripts/debug_note_payload.py b/scripts/debug_note_payload.py index 1142a34..4e5c554 100644 --- a/scripts/debug_note_payload.py +++ b/scripts/debug_note_payload.py @@ -1,15 +1,62 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/debug_note_payload.py -Zeigt, welche app.core.note_payload geladen wird, ruft make_note_payload auf -und druckt Typ/Preview der Rückgabe. +FILE: scripts/debug_note_payload.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active (Debug-Tool) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Debug-Tool zur Analyse der Note-Payload-Erzeugung. +Zeigt, welches Modul geladen wird und die erzeugte Payload-Struktur. + +Funktionsweise: +--------------- +1. Lädt Note-Payload-Modul +2. Liest Markdown-Datei +3. Ruft make_note_payload auf +4. Zeigt Modul-Pfad, Rückgabe-Typ und Payload-Inhalt + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Debug-Informationen + * module_path: Pfad zum geladenen Modul + * return_type: Typ der Rückgabe + * keys: Liste der Payload-Keys (falls Dict) + * JSON: Vollständige Payload (falls Dict) + +Verwendung: +----------- +- Debugging von Payload-Erzeugungsproblemen +- Validierung der Modul-Ladung +- Analyse der Payload-Struktur + +Hinweise: +--------- +- Nutzt app.core.ingestion.ingestion_note_payload (nicht mehr app.core.note_payload) +- Zeigt vollständige Payload-Struktur Aufruf: - python3 -m scripts.debug_note_payload --file ./test_vault/40_concepts/concept-alpha.md --vault-root ./test_vault +------- +python3 -m scripts.debug_note_payload --file ./vault/note.md --vault-root ./vault + +Parameter: +---------- +--file PATH Pfad zur Markdown-Datei (erforderlich) +--vault-root PATH Vault-Root für relative Pfade (optional) +--hash-mode MODE Hash-Modus (optional) +--hash-normalize N Hash-Normalisierung (optional) +--hash-source SRC Hash-Quelle (optional) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Geändert: app.core.note_payload → app.core.ingestion.ingestion_note_payload +v1.0.0: Initial Release """ import argparse, json, os, pprint -from app.core import note_payload as np +from app.core.ingestion import ingestion_note_payload as np from app.core.parser import read_markdown def main(): @@ -24,7 +71,6 @@ def main(): print("module_path:", getattr(np, "__file__", "")) parsed = read_markdown(args.file) res = np.make_note_payload(parsed, vault_root=args.vault_root, - hash_mode=args.hash_mode, hash_normalize=args.hash_normalize, hash_source=args.hash_source, file_path=args.file) diff --git a/scripts/debug_qdrant_state.py b/scripts/debug_qdrant_state.py index 7d363ab..d1d1281 100644 --- a/scripts/debug_qdrant_state.py +++ b/scripts/debug_qdrant_state.py @@ -1,14 +1,55 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/debug_qdrant_state.py -Zeigt Prefix, Collections und einfache Counts (notes/chunks/edges) + sample note_ids. +FILE: scripts/debug_qdrant_state.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active (Debug-Tool) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Zeigt schnelle Übersicht über den Qdrant-Zustand. +Gibt Prefix, Collections, Punkt-Anzahlen und Beispiel-IDs aus. + +Funktionsweise: +--------------- +1. Verbindet mit Qdrant +2. Ermittelt Collections für das Präfix +3. Zählt Points in jeder Collection +4. Extrahiert Beispiel-IDs (erste 5) + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Zustands-Übersicht + * prefix: Collection-Präfix + * collections: Namen der Collections + * counts: Punkt-Anzahlen pro Collection + * samples: Beispiel-IDs pro Collection + +Verwendung: +----------- +- Schnelle Status-Prüfung +- Debugging von Verbindungsproblemen +- Validierung der Collection-Struktur + +Hinweise: +--------- +- Nutzt count() für exakte Zählung (falls verfügbar) +- Fallback auf scroll() bei Problemen Aufruf: - export COLLECTION_PREFIX="mindnet" - python3 -m scripts.debug_qdrant_state - # oder: - python3 -m scripts.debug_qdrant_state --prefix mindnet +------- +python3 -m scripts.debug_qdrant_state +python3 -m scripts.debug_qdrant_state --prefix mindnet_dev + +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release """ from __future__ import annotations import argparse, os, json diff --git a/scripts/diag_payload_indexes.py b/scripts/diag_payload_indexes.py index 1b8fc05..ff9e769 100644 --- a/scripts/diag_payload_indexes.py +++ b/scripts/diag_payload_indexes.py @@ -1,13 +1,56 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/diag_payload_indexes.py (compat v1.2) +FILE: scripts/diag_payload_indexes.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Zeigt payload_schema je Collection; kompatibel mit älteren qdrant-client Versionen. -Strategie (in dieser Reihenfolge): - 1) openapi_client.collections_api.get_collection(..., with_payload_schema=True) - 2) client.get_collection(...) - 3) HTTP GET /collections/{name}?with_payload_schema=true +Zweck: +------- +Zeigt das Payload-Schema (Indizes) für alle Collections eines Präfixes. +Nützlich zur Diagnose von Index-Problemen oder Validierung der Schema-Struktur. + +Funktionsweise: +--------------- +1. Ermittelt Collections für das Präfix (notes, chunks, edges) +2. Für jede Collection: + - Versucht Payload-Schema über verschiedene Methoden zu laden: + * qdrant-client API (get_collection) + * HTTP GET direkt (Fallback) + - Zeigt Schema als JSON +3. Gibt Schema-Übersicht aus + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON pro Collection mit payload_schema +- Zeigt vorhandene Indizes (keyword, text) und deren Konfiguration +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Diagnose von Index-Problemen +- Validierung nach ensure_payload_indexes +- Dokumentation der Schema-Struktur + +Hinweise: +--------- +- Kompatibel mit verschiedenen qdrant-client Versionen +- Nutzt Fallback-Strategie für maximale Kompatibilität + +Aufruf: +------- +python3 -m scripts.diag_payload_indexes --prefix mindnet + +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.2.0: Kompatibilität mit verschiedenen qdrant-client Versionen +v1.0.0: Initial Release """ from __future__ import annotations diff --git a/scripts/dump_note_chunks.py b/scripts/dump_note_chunks.py index 54b8514..9641c0e 100644 --- a/scripts/dump_note_chunks.py +++ b/scripts/dump_note_chunks.py @@ -1,4 +1,58 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/dump_note_chunks.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Gibt die Chunks einer bestimmten Note in lesbarer Form aus. +Nützlich zur Analyse der Chunk-Struktur und -Inhalte. + +Funktionsweise: +--------------- +1. Sucht Note nach note-id im Vault +2. Erzeugt Chunks via assemble_chunks +3. Gibt Chunks formatiert aus: + - Chunk-ID, Token-Anzahl, Section-Pfad + - Vollständiger Chunk-Text + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Formatierter Text + * Header: "# Titel (note-id) — X chunks" + * Pro Chunk: "--- chunk_id | tokens | section_path ---" + * Gefolgt vom Chunk-Text +- Exit-Code 0: Erfolgreich +- Fehlermeldung, wenn Note nicht gefunden + +Verwendung: +----------- +- Analyse der Chunk-Struktur einer spezifischen Note +- Debugging von Chunking-Problemen +- Validierung der Chunk-Inhalte + +Hinweise: +--------- +- Nutzt synchrones assemble_chunks (kann async sein) +- Gibt nur erste gefundene Note aus (bei Duplikaten) + +Aufruf: +------- +python3 -m scripts.dump_note_chunks --vault ./vault --note-id my-note-id + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--note-id ID Note-ID zum Dumpen (erforderlich) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release +""" from __future__ import annotations import argparse, os, glob from app.core.parser import read_markdown, normalize_frontmatter, validate_required_frontmatter diff --git a/scripts/edges_dryrun.py b/scripts/edges_dryrun.py index c6daf1a..22f3ffa 100644 --- a/scripts/edges_dryrun.py +++ b/scripts/edges_dryrun.py @@ -1,22 +1,70 @@ #!/usr/bin/env python3 -# scripts/edges_dryrun.py # -*- coding: utf-8 -*- """ -Dry-Run: Erzeuge Edges aus einem Vault **ohne** Qdrant-Upsert. -- Liest Markdown mit YAML-Frontmatter -- Chunking: einfacher Absatz-Chunker (Index + text) -- Kanten: nutzt app.core.edges.build_edges_for_note() -- Ausgabe: JSON pro Note mit Edge-Counts und 3 Beispiel-Payloads +FILE: scripts/edges_dryrun.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Simuliert die Edge-Erzeugung aus Markdown-Dateien ohne Datenbank-Upsert. +Nützlich zur Analyse, welche Kanten aus einem Vault generiert würden. + +Funktionsweise: +--------------- +1. Scannt alle .md-Dateien im Vault +2. Für jede Datei: + - Parst Markdown mit Frontmatter + - Erstellt einfache Absatz-basierte Chunks (vereinfachtes Chunking) + - Extrahiert Edge-Kandidaten via build_edges_for_note(): + * Wikilinks [[...]] + * Typed Relations [[rel:KIND|Target]] + * Callout Relations [!edge] KIND: [[Target]] + * Struktur-Kanten (belongs_to, next, prev) + - Zählt Edges nach Typ (kind/relation) +3. Gibt JSON-Report aus mit Edge-Statistiken pro Note + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON-Array mit einem Objekt pro Note +- Jedes Objekt enthält: + * path: Dateipfad + * note_id: Note-Identifier + * type: Note-Typ + * chunks: Anzahl der Chunks + * edges_total: Gesamtanzahl der Edges + * edges_by_kind: Dictionary mit Edge-Anzahl pro Typ + * samples: Erste 3 Edges als Beispiele + +Verwendung: +----------- +- Analyse der Graph-Struktur vor dem Import +- Debugging von fehlenden oder unerwarteten Edges +- Validierung der Edge-Extraktion-Logik + +Hinweise: +--------- +- Verwendet vereinfachtes Chunking (Absatz-basiert), nicht das vollständige Chunking-System +- Edge-Extraktion entspricht der Produktions-Logik (app.core.derive_edges) +- Keine Datenbank-Operationen, rein analytisch Aufruf: - python3 -m scripts.edges_dryrun --vault ./vault +------- +python3 -m scripts.edges_dryrun --vault ./vault +python3 -m scripts.edges_dryrun --vault ./vault --include-note-scope-refs -Optional: - --include-note-scope-refs # auch Frontmatter-Links (links[].target_id) als Note-Scope-Referenzen +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--include-note-scope-refs Berücksichtigt auch Frontmatter-Links (links[].target_id) -Voraussetzungen: - - app/core/parser.py (read_markdown, normalize_frontmatter) - - app/core/edges.py (dieses Modul forwardet zur v2-Implementierung) +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Geändert: app.core.edges → app.core.derive_edges + - Parameter korrigiert: chunk_payloads → chunks, note_level_refs → note_level_references +v1.0.0: Erster Release """ from __future__ import annotations @@ -28,7 +76,7 @@ from pathlib import Path from typing import Dict, List, Optional from app.core.parser import read_markdown, normalize_frontmatter -from app.core.edges import build_edges_for_note +from app.core.derive_edges import build_edges_for_note def _iter_markdown(vault: str): for p in Path(vault).rglob("*.md"): @@ -83,8 +131,8 @@ def main(): edges = build_edges_for_note( note_id=note_id, - chunk_payloads=chunks, - note_level_refs=note_refs, + chunks=chunks, + note_level_references=note_refs, include_note_scope_refs=include_note_scope, ) kinds = {} diff --git a/scripts/edges_full_check.py b/scripts/edges_full_check.py index c551548..b024861 100644 --- a/scripts/edges_full_check.py +++ b/scripts/edges_full_check.py @@ -1,19 +1,64 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/edges_full_check.py -Zählt und validiert Kanten in Qdrant. Erkennt folgende Rule-Gruppen: -- explicit_total: rule_id startswith "explicit:" (z.B. explicit:wikilink, explicit:note_scope) -- callout_total: rule_id == "callout:edge" -- inline_total: rule_id startswith "inline:" (z.B. inline:rel) -- defaults_total: rule_id startswith "edge_defaults:" -- structure: rule_id in {"structure:belongs_to","structure:order"} +FILE: scripts/edges_full_check.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Gibt zusätzlich: -- edges_by_kind (aggregiert) -- notes/chunks/edges Anzahlen -- multi_callout_detected: True, falls ein Chunk mehrere Callout-Ziele der gleichen Relation enthält -- per_note_checks: belongs_to == chunks, next == prev == (chunks-1) +Zweck: +------- +Umfassende Validierung der Edge-Struktur in Qdrant. +Analysiert Edge-Typen, Rule-Gruppen und strukturelle Integrität. + +Funktionsweise: +--------------- +1. Lädt alle Edges aus {prefix}_edges +2. Gruppiert Edges nach rule_id: + - explicit: rule_id startswith "explicit:" (wikilink, note_scope) + - callout: rule_id == "callout:edge" + - inline: rule_id startswith "inline:" (rel) + - defaults: rule_id startswith "edge_defaults:" + - structure: rule_id in {"structure:belongs_to", "structure:order"} +3. Prüft strukturelle Integrität: + - belongs_to == chunks pro Note + - next == prev == (chunks-1) pro Note + - Multi-Callout-Erkennung +4. Aggregiert Statistiken + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit umfassender Analyse + * counts: notes/chunks/edges Anzahlen + * edges_by_kind: Aggregierte Edge-Anzahl pro Typ + * rule_groups: Zählung nach Rule-Gruppen + * per_note_checks: Strukturelle Validierung pro Note + * multi_callout_detected: Boolean +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Umfassende Graph-Analyse +- Validierung nach größeren Änderungen +- Debugging von Edge-Problemen + +Hinweise: +--------- +- Kann bei großen Graphen langsam sein +- Prüft strukturelle, nicht semantische Korrektheit + +Aufruf: +------- +python3 -m scripts.edges_full_check --prefix mindnet + +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release """ from __future__ import annotations diff --git a/scripts/export_markdown.py b/scripts/export_markdown.py index 0e41d0d..2a973b0 100644 --- a/scripts/export_markdown.py +++ b/scripts/export_markdown.py @@ -1,39 +1,74 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Script: scripts/export_markdown.py — Qdrant → Markdown (Vault) -Version: 1.4.1 -Datum: 2025-09-10 +FILE: scripts/export_markdown.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Funktion --------- -Exportiert Notes (Frontmatter + Body) aus Qdrant in einen Zielordner. Der Body wird -bevorzugt aus dem Feld `fulltext` rekonstruiert; falls leer/nicht vorhanden, aus Chunks -(Sortierung: seq → chunk_index → Nummer in chunk_id). Pfade werden **relativ** geschrieben. +Zweck: +------- +Exportiert Notes (Frontmatter + Body) aus Qdrant zurück in Markdown-Dateien. +Nützlich für Backup, Migration oder Notfall-Wiederherstellung. -Optionen --------- ---out PATH Zielordner (erforderlich) ---prefix TEXT Collection-Prefix (CLI überschreibt ENV COLLECTION_PREFIX) ---note-id ID Nur eine Note exportieren ---overwrite Existierende Dateien überschreiben ---include-edges MODE none|yaml|footer (Default: none) ---flatten-paths Alle Dateien flach schreiben; Originalpfad in FM: orig_path +Funktionsweise: +--------------- +1. Liest alle Notes aus Qdrant (Collection: {prefix}_notes) +2. Für jede Note: + - Rekonstruiert Frontmatter aus Payload-Feldern + - Rekonstruiert Body: + * Primär: aus `fulltext` Feld (falls vorhanden) + * Fallback: aus Chunks (sortiert nach seq → chunk_index → chunk_id) + - Optional: Fügt Edges als Links hinzu (yaml/footer) +3. Schreibt Markdown-Dateien mit relativen Pfaden -ENV ---- +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON pro Datei mit note_id, path, decision (write/skip-exists) +- Abschluss: "Done. Exported notes: X" +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Backup vor größeren Änderungen +- Migration zwischen Instanzen +- Notfall-Wiederherstellung bei Vault-Verlust +- Analyse der in Qdrant gespeicherten Daten + +Hinweise: +--------- +- Pfade werden relativ geschrieben (wie im Original-Vault) +- --flatten-paths: Alle Dateien in einen Ordner (orig_path in Frontmatter) +- Body-Rekonstruktion aus Chunks kann bei sehr langen Dokumenten unvollständig sein +- Edge-Export optional (none/yaml/footer) + +Aufruf: +------- +python3 -m scripts.export_markdown --out ./_exportVault +python3 -m scripts.export_markdown --out ./_exportVault --note-id concept-alpha --include-edges yaml +python3 -m scripts.export_markdown --out ./_exportVault --flatten-paths --overwrite + +Parameter: +---------- +--out PATH Zielordner für exportierte Markdown-Dateien (erforderlich) +--prefix TEXT Collection-Präfix (überschreibt ENV COLLECTION_PREFIX) +--note-id ID Nur eine bestimmte Note-ID exportieren (optional) +--overwrite Existierende Dateien überschreiben (sonst skip) +--include-edges MODE none|yaml|footer (Default: none) + - none: Keine Edges + - yaml: Als YAML-Feld 'references' im Frontmatter + - footer: Als Markdown-Links am Ende +--flatten-paths Alle Dateien flach schreiben (orig_path in Frontmatter) + +Umgebungsvariablen: +------------------- COLLECTION_PREFIX, QDRANT_URL | QDRANT_HOST/QDRANT_PORT | QDRANT_API_KEY -Beispiele ---------- - export COLLECTION_PREFIX="mindnet" - python3 -m scripts.export_markdown --out ./_exportVault - - # Nur eine Note, Edges als YAML-Feld 'references' - python3 -m scripts.export_markdown --out ./_exportVault --note-id concept-alpha --include-edges yaml - - # Flach schreiben mit Überschreiben - python3 -m scripts.export_markdown --out ./_exportVault --flatten-paths --overwrite +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Aktualisiert: Import-Pfade für neue Struktur +v1.4.1 (2025-09-10): Initial Release """ from __future__ import annotations diff --git a/scripts/fix_frontmatter.py b/scripts/fix_frontmatter.py index b5f04d0..c8ad986 100644 --- a/scripts/fix_frontmatter.py +++ b/scripts/fix_frontmatter.py @@ -1,4 +1,68 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/fix_frontmatter.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Repariert oder ergänzt fehlende Frontmatter-Felder in Markdown-Dateien. +Erzeugt stabile IDs, erkennt Typen und erstellt vollständige Frontmatter-Struktur. + +Funktionsweise: +--------------- +1. Scannt alle Markdown-Dateien im Vault +2. Für jede Datei: + - Liest vorhandenes Frontmatter + - Erkennt fehlende Felder (id, title, type, created) + - Generiert fehlende Werte: + * id: Stabile ID aus Pfad, Titel und Datum + * title: Aus H1, Dateiname oder "Untitled" + * type: Aus Pfad/Tags (journal, task, project, etc.) + * created: Aus Dateiname oder mtime + - Validiert Payload (optional) + - Schreibt korrigiertes Frontmatter zurück + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON pro Datei mit Änderungen + * path: Dateipfad + * changes: Dict mit alten/neuen Werten + * backup_path: Pfad zur Backup-Datei (falls --backup) +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Migration von Legacy-Vaults ohne Frontmatter +- Reparatur beschädigter Frontmatter +- Standardisierung von Frontmatter-Strukturen + +Hinweise: +--------- +- Erstellt Backups vor Änderungen (mit --backup) +- Idempotent: Überspringt bereits vollständige Frontmatter +- Nutzt Heuristiken für Typ-Erkennung (Pfad/Tags) + +Aufruf: +------- +python3 -m scripts.fix_frontmatter --vault ./vault --apply +python3 -m scripts.fix_frontmatter --vault ./vault --backup --apply + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--backup Erstellt Backup-Dateien vor Änderungen +--apply Führt tatsächliche Änderungen durch (sonst Dry-Run) +--validate Validiert Note-Payload nach Fix + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert + - Hinweis: validate_note_payload existiert nicht mehr (wird entfernt) +v1.0.0: Initial Release +""" from __future__ import annotations import argparse, os, re, sys, json, shutil, time, hashlib from dataclasses import dataclass @@ -6,7 +70,7 @@ from typing import Dict, Tuple, Optional, List from slugify import slugify from app.core.parser import read_markdown, normalize_frontmatter from app.core.parser import FRONTMATTER_RE # für Re-Inject -from app.core.validate_note import validate_note_payload +# NOTE: validate_note_payload existiert nicht mehr - entfernt from app.core.ingestion.ingestion_note_payload import make_note_payload DATE_IN_NAME = re.compile(r"(?P\d{4})[-_\.]?(?P\d{2})[-_\.]?(?P\d{2})") diff --git a/scripts/gc_qdrant_after_vault_scan.py b/scripts/gc_qdrant_after_vault_scan.py index d546b09..3405d1c 100644 --- a/scripts/gc_qdrant_after_vault_scan.py +++ b/scripts/gc_qdrant_after_vault_scan.py @@ -1,50 +1,73 @@ #!/usr/bin/env python3 -# scripts/gc_qdrant_after_vault_scan.py -# Version: 1.0.0 (2025-09-05) -# -# Zweck -# ----- -# Garbage-Collector für Qdrant: löscht Einträge (Edges, Chunks, Notes), deren Note-ID -# nicht mehr im Obsidian-Vault vorhanden ist. So bleiben Vektorindex/Graph mit dem Vault konsistent. -# -# Aufrufparameter -# --------------- -# --vault PATH Pfad zum Obsidian-Vault (z. B. ./vault) [erforderlich] -# --mode {edges,content,all} -# edges -> nur Kanten mit source_id/target_id ∉ Vault-Notes löschen -# content -> Chunks & Notes löschen, deren note_id ∉ Vault-Notes (keine Edges) -# all -> zuerst edges, dann content -# --prefix PREFIX Collection-Präfix (Default: aus ENV COLLECTION_PREFIX oder "mindnet") -# --apply Ohne diesen Schalter: Dry-Run (nur Vorschau). Mit Schalter: ausführen. -# --yes Sicherheitsabfrage überspringen (non-interaktiv) -# --batch-size N Größe für In-Filter (Default: 1000) -# -# Hinweise -# -------- -# - Benötigt funktionierendes Python-venv mit allen Abhängigkeiten von mindnet (qdrant-client usw.). -# - Läuft gegen die drei Collections {prefix}_notes, {prefix}_chunks, {prefix}_edges (keine anderen Projekte). -# - Nutzt OR-Filter (Filter.should) korrekt, keine minimum_should (Pydantic v2 lässt die Option nicht zu). -# -# Änderungen ggü. vorher -# ---------------------- -# - Neu: eigenes GC-Tool; berührt ausschließlich das übergebene Projekt-Präfix. -# - Sichere Dry-Run-Vorschau mit Zählung pro Collection; interaktive Bestätigung. -# - Löscht Edges mittels Filter auf (source_id ∈ MISSING) ODER (target_id ∈ MISSING). -# - Löscht Chunks/Notes mittels Filter auf note_id ∈ MISSING. -# -# Beispiele -# --------- -# DRY RUN: python3 -m scripts.gc_qdrant_after_vault_scan --vault ./vault --mode all -# APPLY: python3 -m scripts.gc_qdrant_after_vault_scan --vault ./vault --mode all --apply -# CI-Modus: python3 -m scripts.gc_qdrant_after_vault_scan --vault ./vault --mode edges --apply --yes -# -# Kompatibilität / Kontext -# ------------------------ -# - Frontmatter-Validierung & Parsing wie im Projekt (parser.read_markdown, validate_required_frontmatter). :contentReference[oaicite:0]{index=0} -# - Note-Payload-Schema (u. a. note_id, hash_fulltext) wie spezifiziert. :contentReference[oaicite:1]{index=1} -# - Collection-Namen & Anlage-Logik über app.core.qdrant (notes/chunks = dim, edges = 1D). :contentReference[oaicite:2]{index=2} -# - Edges nutzen Felder source_id/target_id; Chunks/Notes tragen note_id in payload. :contentReference[oaicite:3]{index=3} :contentReference[oaicite:4]{index=4} -# +# -*- coding: utf-8 -*- +""" +FILE: scripts/gc_qdrant_after_vault_scan.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Garbage-Collector für Qdrant: löscht Einträge, deren Note-ID nicht mehr +im Vault vorhanden ist. Hält Vektorindex/Graph mit dem Vault konsistent. + +Funktionsweise: +--------------- +1. Scannt Vault und sammelt alle note_ids +2. Lädt note_ids aus Qdrant +3. Berechnet Differenz (verwaiste IDs) +4. Löscht verwaiste Einträge: + - mode=edges: Nur Edges mit source_id/target_id ∉ Vault + - mode=content: Chunks & Notes mit note_id ∉ Vault + - mode=all: Zuerst edges, dann content +5. Nutzt Batch-Filter für effiziente Löschung + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Preview und Statistiken + * mode: DRY-RUN oder APPLY + * vault_note_ids: Anzahl Notizen im Vault + * qdrant_note_ids: Anzahl Notizen in Qdrant + * orphans: Anzahl verwaister Notizen + * deleted: Anzahl gelöschter Einträge (nach --apply) +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Regelmäßige Wartung nach Vault-Bereinigung +- Konsistenz-Check zwischen Vault und Datenbank +- Vor Migrationen + +Sicherheitsmerkmale: +------------------- +- Dry-Run standardmäßig (keine Änderungen ohne --apply) +- Interaktive Bestätigung (außer mit --yes) +- Nur betroffene Collections werden geändert + +Aufruf: +------- +python3 -m scripts.gc_qdrant_after_vault_scan --vault ./vault --mode all +python3 -m scripts.gc_qdrant_after_vault_scan --vault ./vault --mode all --apply +python3 -m scripts.gc_qdrant_after_vault_scan --vault ./vault --mode edges --apply --yes + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--mode MODE edges | content | all (Default: all) + - edges: Nur Edges löschen + - content: Nur Chunks & Notes löschen + - all: Beides (zuerst edges, dann content) +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) +--apply Führt tatsächliches Löschen durch (sonst Dry-Run) +--yes Keine interaktive Bestätigung +--batch-size N Batch-Größe für Filter (Default: 1000) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Aktualisiert: Import-Pfade für neue Struktur +v1.0.0 (2025-09-05): Initial Release +""" from __future__ import annotations import argparse, os, glob, json, sys from typing import Iterable, Set, Tuple, List diff --git a/scripts/health_check_mindnet.py b/scripts/health_check_mindnet.py index ece0a48..4a0d0da 100644 --- a/scripts/health_check_mindnet.py +++ b/scripts/health_check_mindnet.py @@ -1,40 +1,70 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- """ -scripts/health_check_mindnet.py +FILE: scripts/health_check_mindnet.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Health-Check für den mindnet-Retriever-/Query-Endpoint. +Zweck: +------- +Health-Check für den mindnet /query-Retriever-Endpoint. +Prüft Verfügbarkeit, Antwortzeit und Ergebnisqualität. -Funktion: -- Führt POST-Requests auf /query in verschiedenen Modi aus (standard: semantic + hybrid). -- Prüft Status-Code, JSON-Struktur und Anzahl der Treffer. -- Kennzeichnet Probleme als: - - status="ok" - - status="warning" (z.B. Timeout) - - status="error" (harte Fehler wie HTTP-Fehler, JSON-Fehler etc.) +Funktionsweise: +--------------- +1. Führt POST-Requests auf /query in verschiedenen Modi aus +2. Prüft für jeden Modus: + - HTTP-Status-Code (erwartet 200) + - JSON-Struktur (erwartet Objekt mit results-Array) + - Anzahl der Treffer (mindestens min-results) + - Antwortzeit (Latency) +3. Klassifiziert Ergebnisse: + - ok: Alles in Ordnung + - warning: Timeout oder unerwartetes Verhalten + - error: HTTP-Fehler, JSON-Fehler, zu wenige Ergebnisse -Exit-Code: -- Default (tolerant): - - overall_status = "ok" (inkl. warnings) → Exit-Code 0 - - overall_status = "error" → Exit-Code 1 -- Mit --strict: - - warnings werden wie errors behandelt → Exit-Code 1 +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Menschlich lesbare Zusammenfassung + JSON-Report +- overall_status: ok | warning | error +- Exit-Code 0: ok (tolerant) oder ok/warning (strict) +- Exit-Code 1: error (tolerant) oder warning/error (strict) -Beispiele: +Verwendung: +----------- +- Monitoring und Alerting (z.B. cronjob) +- CI/CD-Pipelines (Validierung nach Deployment) +- Debugging von Performance-Problemen +- Integration in externe Monitoring-Tools (n8n, etc.) - python scripts/health_check_mindnet.py \ - --url http://127.0.0.1:8001/query \ - --query "embeddings" \ - --top-k 3 +Hinweise: +--------- +- Standard: Prüft semantic und hybrid Modi +- Tolerant: Warnings führen nicht zu Exit-Code 1 +- Strict: Warnings werden wie Errors behandelt +- JSON-Ausgabe für maschinelle Weiterverarbeitung - python scripts/health_check_mindnet.py \ - --url http://127.0.0.1:8001/query \ - --query "embeddings" \ - --top-k 3 \ - --timeout 15 \ - --modes hybrid +Aufruf: +------- +python3 scripts/health_check_mindnet.py --url http://127.0.0.1:8001/query +python3 scripts/health_check_mindnet.py --query "test" --top-k 5 --modes semantic hybrid +python3 scripts/health_check_mindnet.py --strict - # Strenger Modus (warnings → Exit-Code 1) - python scripts/health_check_mindnet.py --strict +Parameter: +---------- +--url URL Vollständige URL des /query-Endpunkts (Default: http://127.0.0.1:8001/query) +--query TEXT Test-Query (Default: embeddings) +--top-k INT Anzahl der erwarteten Ergebnisse (Default: 3) +--timeout FLOAT Timeout in Sekunden pro Request (Default: 5.0) +--modes LIST Zu prüfende Modi (Default: semantic hybrid) +--min-results INT Minimale Anzahl erwarteter Ergebnisse (Default: 1) +--strict Warnings als Fehler behandeln (Exit-Code 1 bei warnings) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release """ from __future__ import annotations diff --git a/scripts/import_markdown.py b/scripts/import_markdown.py index 544ae40..ebf4914 100644 --- a/scripts/import_markdown.py +++ b/scripts/import_markdown.py @@ -1,11 +1,69 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- """ -scripts/import_markdown.py -CLI-Tool zum Importieren von Markdown-Dateien in Qdrant. -WP-15b: Implementiert den Two-Pass Workflow (Pre-Scan + Processing). -Sorgt dafür, dass der LocalBatchCache vor der Verarbeitung robust gefüllt wird. -Indiziert Notizen nach ID, Titel und Dateiname für maximale Link-Kompatibilität. -VERSION: 2.4.1 +FILE: scripts/import_markdown.py +VERSION: 2.4.1 (2025-12-15) +STATUS: Active (Core) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Hauptwerkzeug zum Importieren von Markdown-Dateien aus einem Vault in Qdrant. +Implementiert den Two-Pass Workflow (WP-15b) für robuste Edge-Validierung. + +Funktionsweise: +--------------- +1. PASS 1: Global Pre-Scan + - Scannt alle Markdown-Dateien im Vault + - Extrahiert Note-Kontext (ID, Titel, Dateiname) + - Füllt LocalBatchCache für semantische Edge-Validierung + - Indiziert nach ID, Titel und Dateiname für Link-Auflösung + +2. PASS 2: Semantic Processing + - Verarbeitet Dateien in Batches (20 Dateien, max. 5 parallel) + - Nutzt gefüllten Cache für binäre Edge-Validierung + - Erzeugt Notes, Chunks und Edges in Qdrant + - Respektiert Hash-basierte Change Detection + +Ergebnis-Interpretation: +------------------------ +- Log-Ausgabe: Fortschritt und Statistiken +- Stats: processed, skipped, errors +- Exit-Code 0: Erfolgreich (auch wenn einzelne Dateien Fehler haben) +- Ohne --apply: Dry-Run (keine DB-Änderungen) + +Verwendung: +----------- +- Regelmäßiger Import nach Vault-Änderungen +- Initial-Import eines neuen Vaults +- Re-Indexierung mit --force + +Hinweise: +--------- +- Two-Pass Workflow sorgt für robuste Edge-Validierung +- Change Detection verhindert unnötige Re-Indexierung +- Parallele Verarbeitung für Performance (max. 5 gleichzeitig) +- Cloud-Resilienz durch Semaphore-Limits + +Aufruf: +------- +python3 -m scripts.import_markdown --vault ./vault --apply +python3 -m scripts.import_markdown --vault ./vault --prefix mindnet_dev --force --apply + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (Default: ./vault) +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) +--force Erzwingt Re-Indexierung aller Dateien (ignoriert Hashes) +--apply Führt tatsächliche DB-Schreibvorgänge durch (sonst Dry-Run) + +Änderungen: +----------- +v2.4.1 (2025-12-15): WP-15b Two-Pass Workflow + - Implementiert Pre-Scan für LocalBatchCache + - Indizierung nach ID, Titel und Dateiname + - Batch-Verarbeitung mit Semaphore-Limits +v2.0.0: Initial Release """ import asyncio import os diff --git a/scripts/make_test_vault.py b/scripts/make_test_vault.py index 2c5e770..282383e 100644 --- a/scripts/make_test_vault.py +++ b/scripts/make_test_vault.py @@ -1,45 +1,60 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -make_test_vault.py — erzeugt einen minimalen, nachvollziehbaren Test-Vault +FILE: scripts/make_test_vault.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active (Test-Tool) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Version: 1.2 (2025-09-09) -Änderungen ggü. 1.1: -- Fügt **externe Links** (http/mailto) in `exp-two.md` hinzu, damit der Chunk-Payload-Exporter - `external_links` realistisch testen kann. -- Kleine Textanpassungen zur stabilen Chunk-Bildung (Absatzstruktur). +Zweck: +------- +Erzeugt einen minimalen, nachvollziehbaren Test-Vault für Entwicklung und Tests. +Enthält verschiedene Szenarien: leere Links, externe Links, verschiedene Note-Typen. -Zweck -- Kleiner Obsidian-ähnlicher Vault zum Durchspielen des Importers (Chunks/Edges). -- Szenarien: leere Links, spätere Anlage fehlender Noten, Chunk-Neuaufteilung, externe Links. +Funktionsweise: +--------------- +1. Erstellt Verzeichnisstruktur (concepts, experiences, projects) +2. Generiert Test-Notizen mit vollständigem Frontmatter: + - concept-alpha.md (Basis-Concept) + - exp-one.md (verlinkt auf concept-alpha und missing-note) + - exp-two.md (verlinkt auf concept-alpha + externe Links) + - project-demo.md (verlinkt auf concept-alpha und exp-one) +3. Optional: Erstellt missing-note (mit --with-missing) -Struktur (Default: ./test_vault) -- 40_concepts/concept-alpha.md -- 20_experiences/exp-one.md (verlinkt auf [[concept-alpha]] und [[missing-note]]) -- 20_experiences/exp-two.md (verlinkt auf [[concept-alpha]] + externe Links) -- 30_projects/project-demo.md (verlinkt auf [[concept-alpha]] und [[exp-one]]) +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Erfolgsmeldung mit erstellten Dateien +- Exit-Code 0: Erfolgreich +- Exit-Code 1: Fehler (z.B. Verzeichnis existiert bereits ohne --force) -Voraussetzungen -- Keine. Rein Dateigenerierung. +Verwendung: +----------- +- Entwicklung und Testing +- Demo und Dokumentation +- CI/CD-Tests -Aufruf - python3 -m scripts.make_test_vault [--out ./test_vault] [--force] [--with-missing] +Hinweise: +--------- +- Erstellt realistische Test-Daten mit verschiedenen Edge-Szenarien +- Enthält "Red Links" (missing-note) für Link-Auflösungs-Tests +- Externe Links für Chunk-Payload-Tests -Parameter - --out Zielverzeichnis (Standard: ./test_vault) - --force Bestehenden Ordner löschen und neu anlegen. - --with-missing Auch die 'missing-note' direkt anlegen (Standard: erst im 2. Lauf) +Aufruf: +------- +python3 -m scripts.make_test_vault --out ./test_vault +python3 -m scripts.make_test_vault --out ./test_vault --force --with-missing -Hinweise für Tests -1) Erster Import (dry-run oder --apply): Es gibt einen „leeren“ Link [[missing-note]]. -2) Lege danach eine Datei für „missing-note“ an (mit gleicher ID im YAML) und importiere erneut: - -> Erwartung: Edges für ehemals leeren Link werden korrekt nachgezogen (references + backlink). -3) Ändere den Body von exp-one.md so, dass andere Chunk-Grenzen entstehen und importiere erneut: - -> Erwartung: Chunks/Edges/Note werden für betroffene Noten aktualisiert. -4) Prüfe externe Links (verify_chunk_texts.py betrifft nur Text; externe Links siehst du in chunk_payloads). +Parameter: +---------- +--out PATH Zielverzeichnis (Default: ./test_vault) +--force Löscht bestehenden Ordner und legt neu an +--with-missing Erstellt auch missing-note direkt (Standard: erst im 2. Lauf) -Kompatibel mit: -- note.schema.json: 'created'/'updated' müssen strings sein. (Wichtig!) +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.2 (2025-09-09): Externe Links hinzugefügt +v1.0.0: Initial Release """ from __future__ import annotations diff --git a/scripts/ollama_tool_runner.py b/scripts/ollama_tool_runner.py index 7e11e51..3735f8c 100644 --- a/scripts/ollama_tool_runner.py +++ b/scripts/ollama_tool_runner.py @@ -1,21 +1,62 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/ollama_tool_runner.py — Minimaler Tool-Caller für Ollama + mindnet +FILE: scripts/ollama_tool_runner.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) Zweck: - Führt den Tool-Call-Loop für mindnet_query / mindnet_subgraph aus. - Nutzt /tools/ollama (Schema) und deine FastAPI (/query, /graph). -ENV: - OLLAMA=http://127.0.0.1:11434 - API_BASE=http://127.0.0.1:8000 - MODEL=llama3.1 -Nutzung: - python3 scripts/ollama_tool_runner.py "Frage an den Agenten" -Version: - 0.1.0 (Erstanlage) -Stand: - 2025-10-07 +------- +Minimaler Tool-Caller für Ollama mit mindnet-Tools. +Führt Tool-Call-Loop für mindnet_query und mindnet_subgraph aus. + +Funktionsweise: +--------------- +1. Lädt Tool-Schema von /tools/ollama Endpoint +2. Sendet Query an Ollama mit Tool-Definitionen +3. Ollama entscheidet, welche Tools aufzurufen sind +4. Führt Tool-Calls aus: + - mindnet_query: Ruft /query Endpoint auf + - mindnet_subgraph: Ruft /graph Endpoint auf +5. Wiederholt bis keine weiteren Tool-Calls nötig sind + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Konversations-Verlauf mit Tool-Calls und Antworten +- Exit-Code 0: Erfolgreich +- Exit-Code 1: Fehler (z.B. API nicht erreichbar) + +Verwendung: +----------- +- Testing von Tool-Integration +- Demo der Agent-Funktionalität +- Entwicklung neuer Tools + +Hinweise: +--------- +- Nutzt Ollama als LLM-Backend +- Tool-Schema wird von mindnet API bereitgestellt +- Unterstützt mehrere Tool-Calls in einer Konversation + +Aufruf: +------- +python3 scripts/ollama_tool_runner.py "Frage an den Agenten" + +Parameter: +---------- +Keine CLI-Parameter. Query als erstes Argument. + +Umgebungsvariablen: +------------------- +OLLAMA (Default: http://127.0.0.1:11434) +API_BASE (Default: http://127.0.0.1:8000) +MODEL (Default: llama3.1) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v0.1.0 (2025-10-07): Initial Release """ from __future__ import annotations diff --git a/scripts/parse_validate_notes.py b/scripts/parse_validate_notes.py index d341fed..89b5638 100644 --- a/scripts/parse_validate_notes.py +++ b/scripts/parse_validate_notes.py @@ -1,13 +1,63 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/parse_validate_notes.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Validiert alle Markdown-Dateien in einem Vault auf strukturelle Korrektheit. +Prüft Frontmatter-Validität und Note-Payload-Erzeugung ohne Datenbank-Upsert. + +Funktionsweise: +--------------- +1. Scannt rekursiv alle .md-Dateien im angegebenen Vault-Verzeichnis +2. Für jede Datei: + - Liest und parst Markdown mit Frontmatter + - Validiert erforderliche Frontmatter-Felder (id, title, type) + - Erzeugt Note-Payload (validiert durch JSON-Serialisierung) +3. Gibt Statistik aus: Anzahl valider vs. invalider Dateien + +Ergebnis-Interpretation: +------------------------ +- Exit-Code 0: Alle Dateien sind valide +- Exit-Code 1: Mindestens eine Datei ist invalide +- Ausgabe: "✅ Valid: X | ❌ Invalid: Y" +- Fehlerdetails werden auf stderr ausgegeben + +Häufige Fehler: +-------------- +- Fehlende oder ungültige Frontmatter-Felder (id, title, type) +- JSON-Serialisierungsfehler im Payload (z.B. ungültige Datentypen) +- Encoding-Probleme beim Lesen der Datei + +Aufruf: +------- +python3 -m scripts.parse_validate_notes --vault ./vault +python3 -m scripts.parse_validate_notes --vault ./vault --include "**/*.md" --exclude /.obsidian/ /_imported/ + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--include PATTERN Glob-Pattern für Dateien (Default: "**/*.md") +--exclude PATH Pfade zum Ausschließen (Default: /.obsidian/, /_imported/) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Entfernt: app.core.note_payload (ersetzt durch ingestion_note_payload) + - Entfernt: app.core.validate_note (Validierung in make_note_payload integriert) + - Entfernt: jsonschema.ValidationError (nicht mehr benötigt) +v1.0.0: Erster Release +""" from __future__ import annotations import argparse import os import sys import glob -from jsonschema import ValidationError from app.core.parser import read_markdown, validate_required_frontmatter, normalize_frontmatter -from app.core.note_payload import make_note_payload -from app.core.validate_note import validate_note_payload from app.core.ingestion.ingestion_note_payload import make_note_payload @@ -39,11 +89,10 @@ def main(): parsed = read_markdown(path) fm = normalize_frontmatter(parsed.frontmatter) validate_required_frontmatter(fm) - # Note-Payload gemäß note.schema.json validieren - payload = make_note_payload(parsed, vault_root=root) - validate_note_payload(payload) + # Note-Payload wird in make_note_payload bereits validiert (JSON-Serialisierung) + payload = make_note_payload(parsed, vault_root=root, file_path=path) ok += 1 - except (ValidationError, ValueError) as e: + except (ValueError, TypeError, KeyError, AttributeError) as e: failed += 1 print(f"❌ {os.path.relpath(path, root)} :: {e}", file=sys.stderr) diff --git a/scripts/payload_dryrun.py b/scripts/payload_dryrun.py index f2ee242..25ca832 100644 --- a/scripts/payload_dryrun.py +++ b/scripts/payload_dryrun.py @@ -1,24 +1,158 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/payload_dryrun.py (zeigt, was VOR dem Upsert tatsächlich in den Payloads steht) -- KEIN Überschreiben der Note-Payload mehr -- types.yaml ist maßgeblich (gemäß app/core/note_payload.py & chunk_payload.py) +FILE: scripts/payload_dryrun.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Zeigt die Payload-Struktur, die vor dem Datenbank-Upsert erzeugt würde. +Nützlich zur Validierung der Payload-Erzeugung und Konfiguration (types.yaml). + +Funktionsweise: +--------------- +1. Scannt alle Markdown-Dateien im Vault +2. Für jede Datei: + - Parst Markdown mit Frontmatter + - Erzeugt Note-Payload (wie in make_note_payload) + - Erstellt Chunks via assemble_chunks + - Erzeugt Chunk-Payloads (wie in make_chunk_payloads) + - Optional: Erzeugt Edges (mit --with-edges) +3. Gibt JSON pro Note aus mit Payload-Zusammenfassung + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON-Objekte (ein Objekt pro Note, eine Zeile pro Objekt) +- Jedes Objekt enthält: + * note_id, title, type, path + * note_payload: retriever_weight, chunk_profile + * chunks_summary: count, first (erste 3 Chunks mit Metadaten) + * edges_summary (nur mit --with-edges): total, by_kind + +Verwendung: +----------- +- Validierung der types.yaml Konfiguration +- Debugging von Payload-Erzeugungsproblemen +- Analyse der Chunk-Struktur vor dem Import +- Prüfung von retriever_weight und chunk_profile Zuweisungen + +Hinweise: +--------- +- types.yaml ist maßgeblich für Payload-Erzeugung +- Frontmatter-Überschreibungen werden berücksichtigt +- Keine Datenbank-Operationen, rein analytisch +- Chunking nutzt vollständiges assemble_chunks (nicht vereinfacht) + +Aufruf: +------- +python3 -m scripts.payload_dryrun --vault ./vault +python3 -m scripts.payload_dryrun --vault ./vault --note-id my-note-id +python3 -m scripts.payload_dryrun --vault ./vault --with-edges + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--note-id ID Nur eine bestimmte Note verarbeiten (optional) +--with-edges Edge-Erzeugung einschließen (optional) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Entfernt: Fallback für app.core.edges + - Verwendet direkt: app.core.derive_edges + - Parameter korrigiert: chunk_payloads → chunks, note_level_refs → note_level_references +v1.0.0: Erster Release """ from __future__ import annotations -import argparse, os, json +import argparse, os, json, asyncio from typing import Any, Dict, List, Optional from app.core.parser import read_markdown, normalize_frontmatter, validate_required_frontmatter from app.core.chunking import assemble_chunks from app.core.ingestion.ingestion_note_payload import make_note_payload from app.core.ingestion.ingestion_chunk_payload import make_chunk_payloads -try: - from app.core.derive_edges import build_edges_for_note -except Exception: - from app.core.edges import build_edges_for_note # type: ignore +from app.core.derive_edges import build_edges_for_note -def main(): +async def process_file(path: str, root: str, args): + """Verarbeitet eine einzelne Datei asynchron.""" + parsed = read_markdown(path) + if not parsed: + return None + fm = normalize_frontmatter(parsed.frontmatter) + try: + validate_required_frontmatter(fm) + except Exception as e: + print(json.dumps({"path": path, "error": f"invalid frontmatter: {e}"})) + return None + + if args.note_id and fm.get("id") != args.note_id: + return None + + # Note-Payload exakt so, wie der Importer ihn baut (types.yaml maßgeblich) + note_pl = make_note_payload(parsed, + vault_root=root, + hash_source="parsed", + hash_normalize="canonical", + file_path=path) + + body_text = getattr(parsed, "body", "") or "" + chunks = await assemble_chunks(fm["id"], body_text, fm.get("type","concept")) + + chunk_note = { + "frontmatter": fm, + "id": fm.get("id"), + "type": fm.get("type"), + "title": fm.get("title"), + "path": note_pl.get("path") or path, + "note_id": note_pl.get("note_id"), + "tags": fm.get("tags"), + } + # make_chunk_payloads bestimmt Werte ebenfalls aus types.yaml (Frontmatter wird ignoriert) + chunk_pls = make_chunk_payloads( + chunk_note, + note_pl["path"], + chunks, + note_text=body_text, + types_cfg=None, # Loader aus Datei; kein Override von außen + file_path=path, + ) + + out = { + "note_id": note_pl.get("note_id") or fm.get("id"), + "title": fm.get("title"), + "type": fm.get("type"), + "note_payload": { + "retriever_weight": note_pl.get("retriever_weight"), + "chunk_profile": note_pl.get("chunk_profile") + }, + "chunks_summary": { + "count": len(chunk_pls), + "first": [ + {k: chunk_pls[i].get(k) for k in ("chunk_id","index","ord","retriever_weight","chunk_profile","neighbors_prev","neighbors_next")} + for i in range(min(3, len(chunk_pls))) + ] + }, + "path": note_pl.get("path") + } + + if args.with_edges: + edges = build_edges_for_note( + note_id=note_pl.get("note_id") or fm.get("id"), + chunks=chunk_pls, + note_level_references=note_pl.get("references") or [], + include_note_scope_refs=False, + ) + kinds = {} + for e in edges: + k = (e.get("relation") or e.get("kind") or "edge") + kinds[k] = kinds.get(k, 0) + 1 + out["edges_summary"] = {"total": len(edges), "by_kind": kinds} + + return out + +async def main_async(): ap = argparse.ArgumentParser() ap.add_argument("--vault", required=True) ap.add_argument("--note-id") @@ -35,81 +169,12 @@ def main(): files.sort() for path in files: - parsed = read_markdown(path) - if not parsed: - continue - fm = normalize_frontmatter(parsed.frontmatter) - try: - validate_required_frontmatter(fm) - except Exception as e: - print(json.dumps({"path": path, "error": f"invalid frontmatter: {e}"})) - continue + result = await process_file(path, root, args) + if result: + print(json.dumps(result, ensure_ascii=False)) - if args.note_id and fm.get("id") != args.note_id: - continue - - # Note-Payload exakt so, wie der Importer ihn baut (types.yaml maßgeblich) - note_pl = make_note_payload(parsed, - vault_root=root, - hash_mode="body", - hash_normalize="canonical", - hash_source="parsed", - file_path=path) - - body_text = getattr(parsed, "body", "") or "" - chunks = assemble_chunks(fm["id"], body_text, fm.get("type","concept")) - - chunk_note = { - "frontmatter": fm, - "id": fm.get("id"), - "type": fm.get("type"), - "title": fm.get("title"), - "path": note_pl.get("path") or path, - "note_id": note_pl.get("note_id"), - "tags": fm.get("tags"), - } - # make_chunk_payloads bestimmt Werte ebenfalls aus types.yaml (Frontmatter wird ignoriert) - chunk_pls = make_chunk_payloads( - chunk_note, - note_pl["path"], - chunks, - note_text=body_text, - types_cfg=None, # Loader aus Datei; kein Override von außen - file_path=path, - ) - - out = { - "note_id": note_pl.get("note_id") or fm.get("id"), - "title": fm.get("title"), - "type": fm.get("type"), - "note_payload": { - "retriever_weight": note_pl.get("retriever_weight"), - "chunk_profile": note_pl.get("chunk_profile") - }, - "chunks_summary": { - "count": len(chunk_pls), - "first": [ - {k: chunk_pls[i].get(k) for k in ("chunk_id","index","ord","retriever_weight","chunk_profile","neighbors_prev","neighbors_next")} - for i in range(min(3, len(chunk_pls))) - ] - }, - "path": note_pl.get("path") - } - - if args.with_edges: - edges = build_edges_for_note( - note_id=note_pl.get("note_id") or fm.get("id"), - chunk_payloads=chunk_pls, - note_level_refs=note_pl.get("references") or [], - include_note_scope_refs=False, - ) - kinds = {} - for e in edges: - k = (e.get("relation") or e.get("kind") or "edge") - kinds[k] = kinds.get(k, 0) + 1 - out["edges_summary"] = {"total": len(edges), "by_kind": kinds} - - print(json.dumps(out, ensure_ascii=False)) +def main(): + asyncio.run(main_async()) if __name__ == "__main__": main() diff --git a/scripts/preview_chunks.py b/scripts/preview_chunks.py index 25bb25a..6653601 100644 --- a/scripts/preview_chunks.py +++ b/scripts/preview_chunks.py @@ -1,4 +1,60 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/preview_chunks.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Zeigt eine Vorschau der Chunk-Struktur für Notizen im Vault. +Nützlich zur Analyse der Chunking-Strategie und Nachbarschafts-Links. + +Funktionsweise: +--------------- +1. Scannt alle Markdown-Dateien im Vault +2. Für jede Datei: + - Erzeugt Note-Payload + - Erstellt Chunks via assemble_chunks + - Erzeugt Chunk-Payloads +3. Gibt JSON pro Note aus mit Chunk-Details + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON-Objekte (ein Objekt pro Note, eine Zeile pro Objekt) +- Jedes Objekt enthält: + * note_id, title + * num_chunks: Anzahl der Chunks + * avg_tokens: Durchschnittliche Token pro Chunk + * chunks: Array mit Chunk-Details (id, tokens, section, prev, next) + +Verwendung: +----------- +- Analyse der Chunk-Verteilung +- Validierung der Nachbarschafts-Links +- Debugging von Chunking-Problemen + +Hinweise: +--------- +- Nutzt synchrones assemble_chunks (kann async sein) +- Zeigt nur Struktur, keine Inhalte + +Aufruf: +------- +python3 -m scripts.preview_chunks --vault ./vault +python3 -m scripts.preview_chunks --vault ./vault --note-id my-note-id + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--note-id ID Nur eine bestimmte Note verarbeiten (optional) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release +""" from __future__ import annotations import argparse, os, glob, json from app.core.parser import read_markdown, normalize_frontmatter, validate_required_frontmatter diff --git a/scripts/prune_qdrant_vs_vault.py b/scripts/prune_qdrant_vs_vault.py index 59e0905..9314235 100644 --- a/scripts/prune_qdrant_vs_vault.py +++ b/scripts/prune_qdrant_vs_vault.py @@ -1,38 +1,79 @@ -# scripts/prune_qdrant_vs_vault.py -# ----------------------------------------------------------------------------- -# Name: prune_qdrant_vs_vault.py -# Version: 1.0.0 (2025-09-08) -# Zweck: Entfernt verwaiste Qdrant-Einträge (notes/chunks/edges), wenn -# die zugehörigen Markdown-Dateien im Vault nicht mehr existieren. -# -# Was es macht: -# - Liest alle note_id aus dem Vault (Frontmatter: id / note_id). -# - Liest alle note_id aus Qdrant (mindnet_notes). -# - Bildet die Differenz (nur in Qdrant vorhandene, im Vault fehlende). -# - Löscht für jede verwaiste note_id: -# * Notes-Point(s) -# * Alle Chunks der Note -# * Alle Edges, die auf diese Note referenzieren -# (source_id == note_id ODER target_id == note_id ODER -# source_note_id == note_id ODER target_note_id == note_id) -# -# Hinweise: -# - Kein globaler Delete. Nur betroffene note_id. -# - Dry-Run standardmäßig; tatsächliches Löschen erst mit --apply. -# - Interaktive Bestätigung (abschaltbar mit --yes). -# -# Aufruf: -# python3 -m scripts.prune_qdrant_vs_vault --vault ./vault --prefix mindnet -# python3 -m scripts.prune_qdrant_vs_vault --vault ./vault --prefix mindnet --apply -# python3 -m scripts.prune_qdrant_vs_vault --vault ./vault --prefix mindnet --apply --yes -# -# Voraussetzungen: -# - Ausführung im aktivierten venv empfohlen: source .venv/bin/activate -# - Qdrant läuft lokal (oder URL/API-Key in ENV), siehe app/core/qdrant.py -# -# Änderungen: -# - 1.0.0: Erster Release. -# ----------------------------------------------------------------------------- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +FILE: scripts/prune_qdrant_vs_vault.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- +Bereinigt verwaiste Einträge in Qdrant, wenn die zugehörigen Markdown-Dateien +im Vault nicht mehr existieren. Stellt Konsistenz zwischen Vault und Datenbank her. + +Funktionsweise: +--------------- +1. Liest alle note_id aus dem Vault (aus Frontmatter: id / note_id) +2. Liest alle note_id aus Qdrant (Collection: {prefix}_notes) +3. Berechnet Differenz: note_ids nur in Qdrant vorhanden (verwaist) +4. Für jede verwaiste note_id (nur mit --apply): + - Löscht Note-Point(s) aus {prefix}_notes + - Löscht alle Chunks der Note aus {prefix}_chunks + - Löscht alle Edges, die auf diese Note referenzieren: + * source_id == note_id + * target_id == note_id + * source_note_id == note_id + * target_note_id == note_id + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Preview-Informationen + * mode: "DRY-RUN" oder "APPLY" + * counts: Statistiken (vault_note_ids, qdrant_note_ids, orphans) + * orphans_sample: Erste 20 verwaiste note_ids +- Bei --apply: Zusätzlich JSON mit deleted-Statistiken + * deleted.notes: Anzahl gelöschter Note-Points + * deleted.chunks: Anzahl gelöschter Chunks + * deleted.edges: Anzahl gelöschter Edges + +Verwendung: +----------- +- Regelmäßige Wartung nach Vault-Bereinigung +- Vor Migrationen oder größeren Umstrukturierungen +- Konsistenz-Check zwischen Vault und Datenbank + +Sicherheitsmerkmale: +------------------- +- Dry-Run standardmäßig (keine Änderungen ohne --apply) +- Interaktive Bestätigung (außer mit --yes) +- Nur betroffene note_ids werden gelöscht (kein globaler Delete) +- Zeigt Preview vor Ausführung + +Aufruf: +------- +# Dry-Run (nur Analyse) +python3 -m scripts.prune_qdrant_vs_vault --vault ./vault --prefix mindnet + +# Tatsächliches Löschen +python3 -m scripts.prune_qdrant_vs_vault --vault ./vault --prefix mindnet --apply + +# Ohne Rückfrage +python3 -m scripts.prune_qdrant_vs_vault --vault ./vault --prefix mindnet --apply --yes + +Parameter: +---------- +--vault PATH Pfad zum Vault-Verzeichnis (erforderlich) +--prefix TEXT Collection-Präfix (Default: mindnet) +--apply Führt tatsächliches Löschen durch (sonst nur Dry-Run) +--yes Keine interaktive Bestätigung + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Geändert: parse_markdown_file → read_markdown + normalize_frontmatter + - Parsing-Logik an neue API angepasst +v1.0.0 (2025-09-08): Erster Release +""" import argparse import json @@ -45,18 +86,19 @@ from qdrant_client import QdrantClient from qdrant_client.http import models as rest from app.core.qdrant import QdrantConfig, get_client, collection_names -from app.core.parser import parse_markdown_file # nutzt euer bestehendes Parsing/Schema +from app.core.parser import read_markdown, normalize_frontmatter def read_vault_note_ids(vault_dir: Path) -> Set[str]: note_ids: Set[str] = set() for p in vault_dir.rglob("*.md"): try: - parsed = parse_markdown_file(str(p)) - fm = parsed.frontmatter if hasattr(parsed, "frontmatter") else (parsed.get("frontmatter") or {}) - nid = fm.get("id") or fm.get("note_id") - if nid: - note_ids.add(str(nid)) + parsed = read_markdown(str(p)) + if parsed: + fm = normalize_frontmatter(parsed.frontmatter) if parsed.frontmatter else {} + nid = fm.get("id") or fm.get("note_id") + if nid: + note_ids.add(str(nid)) except Exception: # still und leise weiter – wir wollen robust sein continue diff --git a/scripts/report_hashes.py b/scripts/report_hashes.py index b9aaa29..1ce690c 100644 --- a/scripts/report_hashes.py +++ b/scripts/report_hashes.py @@ -1,25 +1,59 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Script: scripts/report_hashes.py — Übersicht & Lücken bei Mehrfach-Hashes -Version: 1.0.0 -Datum: 2025-09-10 +FILE: scripts/report_hashes.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Funktion --------- -Listet je Note die vorhandenen Einträge im Feld `hashes` (Signaturen: ::) -und meldet fehlende Soll-Keys. Eignet sich als CI-Check. +Zweck: +------- +Erstellt Übersicht über Hash-Signaturen in Note-Payloads. +Meldet fehlende Soll-Keys für Multi-Hash Change Detection. -Optionen --------- ---prefix TEXT Collection-Prefix (CLI überschreibt ENV) ---require K [K ...] Zusätzliche Soll-Keys (Default: body|frontmatter|full:parsed:canonical) ---fail-on-missing Exitcode 2, wenn fehlende Keys gefunden werden +Funktionsweise: +--------------- +1. Lädt alle Notes aus {prefix}_notes +2. Prüft für jede Note das `hashes` Feld +3. Vergleicht vorhandene Keys mit Soll-Keys +4. Meldet fehlende Keys pro Note -Beispiele +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Hash-Übersicht + * notes_total: Anzahl geprüfter Notizen + * notes_with_missing: Anzahl Notizen mit fehlenden Keys + * missing_keys: Liste fehlender Keys pro Note +- Exit-Code 0: Keine fehlenden Keys (oder --fail-on-missing nicht gesetzt) +- Exit-Code 2: Fehlende Keys gefunden (nur mit --fail-on-missing) + +Verwendung: +----------- +- CI/CD-Validierung der Hash-Integrität +- Audit nach Migrationen +- Prüfung der Change Detection Funktionalität + +Hinweise: --------- - python3 -m scripts.report_hashes --prefix mindnet - python3 -m scripts.report_hashes --require frontmatter:raw:none --fail-on-missing +- Multi-Hash Format: :: +- Standard Soll-Keys: body:parsed:canonical, full:parsed:canonical +- Kann zusätzliche Keys via --require anfordern + +Aufruf: +------- +python3 -m scripts.report_hashes --prefix mindnet +python3 -m scripts.report_hashes --require frontmatter:raw:none --fail-on-missing + +Parameter: +---------- +--prefix TEXT Collection-Präfix (überschreibt ENV COLLECTION_PREFIX) +--require KEY ... Zusätzliche Soll-Keys (Default: body:parsed:canonical, full:parsed:canonical) +--fail-on-missing Exit-Code 2, wenn fehlende Keys gefunden werden + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0 (2025-09-10): Initial Release """ from __future__ import annotations diff --git a/scripts/reset_qdrant.py b/scripts/reset_qdrant.py index de43c08..27913bd 100644 --- a/scripts/reset_qdrant.py +++ b/scripts/reset_qdrant.py @@ -1,49 +1,75 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Name: scripts/reset_qdrant.py -Version: v1.2.1 (2025-12-11) -Kurzbeschreibung: - Sicheres Zurücksetzen der Qdrant-Collections für EIN Projektpräfix. Das Skript - ermittelt zunächst die tatsächlich betroffenen Collections und zeigt eine - Übersicht an. Anschließend wird der User um Bestätigung gebeten (interaktive - Abfrage), bevor eine der Aktionen ausgeführt wird: +FILE: scripts/reset_qdrant.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active (Core) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) - - wipe: Löscht die Collections komplett und legt sie gemäß Projekt-Defaults neu an. - **Neu**: Danach werden *immer* die Payload-Indizes & Schema-Ergänzungen - über ensure_payload_indexes() idempotent eingerichtet (falls nicht mit --no-indexes deaktiviert). - - truncate: Löscht nur alle Points (Inhalte), behält die Collection-Settings. - **Neu**: Optional (Default) werden im Anschluss die Payload-Indizes geprüft/ergänzt. +Zweck: +------- +Sicheres Zurücksetzen der Qdrant-Collections für ein Projektpräfix. +Ermöglicht vollständigen Neustart (wipe) oder Löschen nur der Inhalte (truncate). -Aufruf (aus Projekt-Root, im venv): - python3 -m scripts.reset_qdrant --mode wipe [--prefix PREFIX] [--yes] [--dry-run] - python3 -m scripts.reset_qdrant --mode truncate [--prefix PREFIX] [--yes] [--dry-run] +Funktionsweise: +--------------- +1. Ermittelt betroffene Collections (notes, chunks, edges für Präfix) +2. Zeigt Preview der existierenden Collections +3. Interaktive Bestätigung (außer mit --yes) +4. Führt Aktion aus: + - wipe: Löscht Collections komplett, legt neu an, richtet Indizes ein + - truncate: Löscht nur Points, behält Collection-Settings, prüft Indizes +5. Richtet Payload-Indizes idempotent ein (außer mit --no-indexes) -Parameter: - --mode wipe | truncate (Pflicht) - --prefix Collection-Prefix (Default: env COLLECTION_PREFIX oder 'mindnet') - --yes Keine Rückfrage, direkt ausführen (CI/CD geeignet) - --dry-run Nur anzeigen, was passieren würde; nichts ändern - --no-indexes Überspringt den Schritt ensure_payload_indexes() (Standard: Indizes/Schema werden geprüft/ergänzt) +Ergebnis-Interpretation: +------------------------ +- Preview: Zeigt betroffene Collections vor Ausführung +- Exit-Code 0: Erfolgreich +- Exit-Code 1: Abgebrochen oder keine Aktion +- Exit-Code 2: Verbindungs- oder Konfigurationsfehler -Umgebungsvariablen (optional): - QDRANT_URL, QDRANT_API_KEY, COLLECTION_PREFIX, VECTOR_DIM (Default 384) - (Zusätzliche ENV für Named Vectors etc. werden innerhalb ensure_collections/ensure_payload_indexes berücksichtigt.) +Verwendung: +----------- +- Vor größeren Migrationen oder Schema-Änderungen +- Bei Inkonsistenzen in der Datenbank +- Für Entwicklung/Testing (schneller Reset) +- CI/CD-Pipelines (mit --yes) Sicherheitsmerkmale: - - Betrachtet ausschließlich Collections des angegebenen Präfixes. - - Listet vor Ausführung die tatsächlich existierenden Ziel-Collections auf. - - Fragt interaktiv nach Bestätigung (es sei denn --yes ist gesetzt). +------------------- +- Betrachtet nur Collections des angegebenen Präfixes +- Listet betroffene Collections vor Ausführung auf +- Interaktive Bestätigung (außer mit --yes) +- Dry-Run Modus verfügbar -Exitcodes: - 0 = OK, 1 = abgebrochen/keine Aktion, 2 = Verbindungs-/Konfigurationsfehler +Aufruf: +------- +python3 -m scripts.reset_qdrant --mode wipe --prefix mindnet +python3 -m scripts.reset_qdrant --mode truncate --prefix mindnet --yes +python3 -m scripts.reset_qdrant --mode wipe --dry-run -Changelog: - v1.2.1: Fix: load_dotenv() hinzugefügt, damit VECTOR_DIM aus .env gelesen wird. - v1.2.0: ensure_payload_indexes() nach wipe/truncate standardmäßig ausführen (idempotent); --no-indexes Flag ergänzt. - v1.1.1: Stabilisierung & Preview (2025-09-05). - v1.1.0: Interaktive Bestätigung, --yes/--dry-run hinzugefügt, Preview der betroffenen Collections. - v1.0.0: Wipe/Truncate ohne Bestätigungsabfrage. +Parameter: +---------- +--mode MODE wipe | truncate (Pflicht) + - wipe: Collections löschen & neu anlegen + - truncate: Nur Points löschen, Settings behalten +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) +--yes Keine Rückfrage, direkt ausführen (CI/CD) +--dry-run Nur anzeigen, was passieren würde +--no-indexes Überspringt ensure_payload_indexes() (Standard: Indizes werden geprüft/ergänzt) + +Umgebungsvariablen: +------------------- +QDRANT_URL, QDRANT_API_KEY, COLLECTION_PREFIX, VECTOR_DIM (Default: 768) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Aktualisiert: Import-Pfade für neue Struktur +v1.2.1 (2025-12-11): Fix load_dotenv() für VECTOR_DIM +v1.2.0: ensure_payload_indexes() standardmäßig nach wipe/truncate +v1.1.0: Interaktive Bestätigung, --yes/--dry-run +v1.0.0: Initial Release """ from __future__ import annotations import argparse diff --git a/scripts/resolve_unresolved_references.py b/scripts/resolve_unresolved_references.py index f990202..b159844 100644 --- a/scripts/resolve_unresolved_references.py +++ b/scripts/resolve_unresolved_references.py @@ -1,20 +1,66 @@ #!/usr/bin/env python3 -# scripts/resolve_unresolved_references.py +# -*- coding: utf-8 -*- """ -resolve_unresolved_references.py — Unaufgelöste Wikilinks in Qdrant nachträglich auflösen +FILE: scripts/resolve_unresolved_references.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Version: 1.1.0 (Fixed for v2.6 Architecture) +Zweck: +------- +Löst unaufgelöste Wikilinks in Qdrant nachträglich auf. +Findet Edges mit status=="unresolved" und versucht sie über Titel/Alias aufzulösen. -Zweck ------- -- Findet Edges in {prefix}_edges mit payload.status=="unresolved". -- Baut einen In-Memory Index aller Notizen (Titel/Alias -> ID). -- Aktualisiert die Edges (setzt target_id, entfernt status). -- Erzeugt symmetrische 'backlink'-Kanten für 'references'. +Funktionsweise: +--------------- +1. Baut Lookup-Index aus allen Notizen: + - Mapping: lower(title) -> note_id + - Mapping: lower(alias) -> note_id +2. Findet alle Edges mit status=="unresolved" +3. Versucht Auflösung über Lookup-Index +4. Aktualisiert erfolgreich aufgelöste Edges: + - Setzt target_id + - Entfernt status + - Fügt resolution="healed_by_script" hinzu +5. Erzeugt Backlinks für erfolgreich aufgelöste references-Edges -Aufruf ------- - python3 -m scripts.resolve_unresolved_references --apply +Ergebnis-Interpretation: +------------------------ +- Log-Ausgabe: Fortschritt und Statistiken +- "Resolvable: X/Y": Anzahl aufgelösbarer Edges +- Ohne --apply: Dry-Run (keine DB-Änderungen) +- Exit-Code 0: Erfolgreich + +Verwendung: +----------- +- Nach Import von neuen Notizen, die bestehende Links auflösen +- Reparatur von "Red Links" (Links auf noch nicht existierende Notizen) +- Wartung des Graphen nach größeren Änderungen + +Hinweise: +--------- +- Sucht nach status=="unresolved" in Edge-Payloads +- Auflösung erfolgt über Titel und Aliases (case-insensitive) +- Erzeugt automatisch Backlinks für references-Edges +- Batch-Verarbeitung für Performance + +Aufruf: +------- +python3 -m scripts.resolve_unresolved_references --apply +python3 -m scripts.resolve_unresolved_references --prefix mindnet --limit 1000 --apply + +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX) +--apply Führt tatsächliche DB-Änderungen durch (sonst Dry-Run) +--limit INT Maximale Anzahl zu verarbeitender Edges (0=alle, Default: 0) +--batch INT Batch-Größe für Upserts (Default: 100) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.1.0: Fixed for v2.6 Architecture +v1.0.0: Initial Release """ import argparse diff --git a/scripts/setup_mindnet_collections.py b/scripts/setup_mindnet_collections.py index 8f7541d..145d9e5 100644 --- a/scripts/setup_mindnet_collections.py +++ b/scripts/setup_mindnet_collections.py @@ -1,23 +1,63 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- """ +FILE: scripts/setup_mindnet_collections.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) + +Zweck: +------- Richtet die Qdrant-Collections für das mindnet-Projekt ein. +Legt Collections und Payload-Indizes an (idempotent). -Collections: -- mindnet_chunks : semantische Suche über Markdown-Text-Chunks (Vektor: dim/Cosine) -- mindnet_notes : 1 Punkt pro Notiz (Metadaten, optional Titel-Embedding) -- mindnet_edges : explizite Link-Kanten (Dummy-Vektor size=1; Filter über Payload) +Funktionsweise: +--------------- +1. Prüft Qdrant-Verfügbarkeit (optional /ready Endpoint) +2. Legt drei Collections an: + - {prefix}_chunks: Semantische Suche über Text-Chunks (Vektor: dim/Cosine) + - {prefix}_notes: Metadaten pro Notiz (Vektor: dim/Cosine) + - {prefix}_edges: Link-Kanten (Dummy-Vektor size=1, Filter über Payload) +3. Richtet Payload-Indizes ein: + - Keyword-Indizes für häufige Filter-Felder + - Text-Index für Volltextsuche (chunks.text) -Eigenschaften: -- Idempotent: legt nur an, wenn eine Collection noch nicht existiert -- Legt sinnvolle Payload-Indizes an (keyword/text) -- Ohne "global"-Seiteneffekte; Qdrant-URL wird sauber übergeben +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Status pro Collection ([+] angelegt, [=] existiert bereits) +- Abschluss: JSON-Liste aller vorhandenen Collections +- Exit-Code 0: Erfolgreich +- Exit-Code 1: Fehler (z.B. Qdrant nicht erreichbar) -Aufrufbeispiel: - python3 setup_mindnet_collections.py \ - --qdrant-url http://127.0.0.1:6333 \ - --prefix mindnet \ - --dim 384 \ - --distance Cosine +Verwendung: +----------- +- Initial-Setup einer neuen mindnet-Instanz +- Nach Qdrant-Reset oder Migration +- Validierung der Collection-Struktur + +Hinweise: +--------- +- Idempotent: Überspringt existierende Collections +- Nutzt HTTP-API direkt (kein qdrant-client) +- Legt nur Basis-Indizes an (erweiterte Indizes via ensure_payload_indexes) + +Aufruf: +------- +python3 -m scripts.setup_mindnet_collections --qdrant-url http://127.0.0.1:6333 --prefix mindnet --dim 768 +python3 -m scripts.setup_mindnet_collections --prefix mindnet_dev --dim 768 --distance Cosine + +Parameter: +---------- +--qdrant-url URL Qdrant-URL (Default: http://127.0.0.1:6333) +--prefix TEXT Collection-Präfix (Default: mindnet) +--dim INT Vektor-Dimension (Default: 384, empfohlen: 768 für nomic) +--distance METRIC Cosine | Euclid | Dot (Default: Cosine) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung + - Dokumentation aktualisiert +v1.0.0: Initial Release """ from __future__ import annotations diff --git a/scripts/validate_edges.py b/scripts/validate_edges.py index 6bc605e..7497a8f 100644 --- a/scripts/validate_edges.py +++ b/scripts/validate_edges.py @@ -1,23 +1,66 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- """ -scripts/validate_edges.py +FILE: scripts/validate_edges.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Validiert die von WP-03 erzeugten Edges in Qdrant: -- Zählt Kanten je Typ (references, backlink, references_at) -- Prüft: Für jede "references" (resolved) existiert eine "backlink"-Gegenkante -- Prüft: "backlink" darf nicht "unresolved" sein -- Prüft: "references_at".source_id existiert in {prefix}_chunks -- Prüft: "references"/"backlink" source/target existieren in {prefix}_notes -- Prüft: doppelte edge_id (sollte 0 sein, da UUIDv5 aus edge_id) -Gibt ein kompaktes JSON-Resultat + optionale Detail-Listen aus. +Zweck: +------- +Validiert die Integrität der Edges in Qdrant. +Prüft strukturelle Korrektheit, Referenz-Integrität und Konsistenz. -Umgebung/Parameter: -- QDRANT_URL (Default: http://127.0.0.1:6333) -- QDRANT_API_KEY (optional) -- COLLECTION_PREFIX (Default: mindnet) +Funktionsweise: +--------------- +1. Lädt alle Edges aus {prefix}_edges +2. Führt mehrere Validierungen durch: + - Zählt Edges nach Typ (references, backlink, etc.) + - Prüft: Für jede "references" existiert "backlink"-Gegenkante + - Prüft: "backlink" darf nicht "unresolved" sein + - Prüft: "references_at".source_id existiert in chunks + - Prüft: source/target existieren in notes + - Prüft: doppelte edge_id (sollte 0 sein) +3. Gibt kompaktes JSON-Resultat + optionale Detail-Listen aus -Beispiel: - python scripts/validate_edges.py --prefix mindnet +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Validierungs-Ergebnissen + * counts_by_kind: Anzahl Edges pro Typ + * validation_results: Ergebnisse der einzelnen Prüfungen + * issues: Liste gefundener Probleme (optional mit --verbose) +- Exit-Code 0: Alle Validierungen bestanden +- Exit-Code 1: Validierungsfehler gefunden + +Verwendung: +----------- +- Qualitätskontrolle nach Importen +- Debugging von Graph-Problemen +- CI/CD-Validierung + +Hinweise: +--------- +- Prüft strukturelle Integrität, nicht semantische Korrektheit +- Kann bei großen Graphen langsam sein + +Aufruf: +------- +python3 -m scripts.validate_edges --prefix mindnet +python3 -m scripts.validate_edges --prefix mindnet --verbose + +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX oder mindnet) +--verbose Zeigt detaillierte Problem-Listen + +Umgebungsvariablen: +------------------- +QDRANT_URL (Default: http://127.0.0.1:6333), QDRANT_API_KEY, COLLECTION_PREFIX + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0: Initial Release """ from __future__ import annotations import argparse diff --git a/scripts/verify_chunk_texts.py b/scripts/verify_chunk_texts.py index e76576c..3ba3943 100644 --- a/scripts/verify_chunk_texts.py +++ b/scripts/verify_chunk_texts.py @@ -1,58 +1,73 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Script: scripts/verify_chunk_texts.py -Version: 1.0.0 -Datum: 2025-09-09 +FILE: scripts/verify_chunk_texts.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) -Kurzbeschreibung +Zweck: +------- +Verifiziert, dass Chunk-Texte in Qdrant korrekt gespeichert sind und +der Note-Body aus Chunks rekonstruiert werden kann. + +Funktionsweise: --------------- -Verifiziert, dass in Qdrant für jede Note die zugehörigen Chunks ein Textfeld -enthalten und der Body (notes.payload.fulltext) aus den Chunk-Texten sinnvoll -rekonstruiert werden kann. +1. Lädt alle Notes aus {prefix}_notes +2. Für jede Note: + - Lädt zugehörige Chunks + - Prüft, dass alle Chunks Text enthalten (text/content/raw) + - Rekonstruiert Body aus Chunks (sortiert nach chunk_index/chunk_id) + - Berechnet Coverage: gefundene Textsegmente / Fulltext-Länge +3. Validiert gegen notes.payload.fulltext -Prüfungen pro Note: -- Alle Chunks vorhanden (>=1). -- Jedes Chunk-Payload hat einen nutzbaren Textschlüssel: "text" (bevorzugt), sonst "content", sonst "raw". -- Reihenfolge der Chunks wird stabil bestimmt (payload.chunk_index -> Nummer aus chunk_id). -- Coverage: Summe der im Fulltext gefundenen Chunk-Textsegmente / len(Fulltext) (Toleranz für Overlaps). - -> OK wenn coverage >= 0.90 (konfigurierbar via --min-coverage) +Ergebnis-Interpretation: +------------------------ +- Ausgabe: JSON mit Validierungs-Ergebnissen + * summary: Gesamtstatistiken + * per_note: Details pro Note + * coverage: Durchschnittliche Coverage +- Exit-Code 0: Alle Prüfungen bestanden +- Exit-Code 1: Probleme gefunden -Ausgabe: -- JSON mit Gesamtsummen und Details je Note. +Verwendung: +----------- +- Validierung nach Importen +- Diagnose von Text-Rekonstruktionsproblemen +- Qualitätskontrolle -ENV: -- QDRANT_URL | QDRANT_HOST/QDRANT_PORT | QDRANT_API_KEY -- COLLECTION_PREFIX (Fallback, wenn --prefix fehlt) +Hinweise: +--------- +- Coverage-Toleranz für Overlaps (Standard: >= 0.90) +- Prüft Text-Felder: text (bevorzugt), content, raw -Beispiele: - # Alle Notes prüfen (Prefix aus ENV) - python3 -m scripts.verify_chunk_texts +Aufruf: +------- +python3 -m scripts.verify_chunk_texts --prefix mindnet +python3 -m scripts.verify_chunk_texts --note-id concept-alpha --min-coverage 0.95 - # Nur eine Note prüfen - python3 -m scripts.verify_chunk_texts --note-id concept-alpha +Parameter: +---------- +--prefix TEXT Collection-Präfix (Default: ENV COLLECTION_PREFIX) +--note-id ID Nur eine bestimmte Note prüfen (optional) +--min-coverage F Minimale Coverage (Default: 0.90) - # Prefix explizit setzen und strengere Coverage verlangen - python3 -m scripts.verify_chunk_texts --prefix mindnet --min-coverage 0.95 +Umgebungsvariablen: +------------------- +QDRANT_URL | QDRANT_HOST/QDRANT_PORT | QDRANT_API_KEY, COLLECTION_PREFIX + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v1.0.0 (2025-09-09): Initial Release """ from __future__ import annotations import argparse, json, os, re, sys from typing import Dict, List, Tuple, Optional -from qdrant_client import QdrantClient from qdrant_client.http import models as rest -def _names(prefix: str) -> Tuple[str,str,str]: - return f"{prefix}_notes", f"{prefix}_chunks", f"{prefix}_edges" - -def _client() -> QdrantClient: - url = os.getenv("QDRANT_URL") - if not url: - host = os.getenv("QDRANT_HOST", "127.0.0.1") - port = int(os.getenv("QDRANT_PORT", "6333")) - url = f"http://{host}:{port}" - api_key = os.getenv("QDRANT_API_KEY") or None - return QdrantClient(url=url, api_key=api_key) +from app.core.qdrant import QdrantConfig, get_client, collection_names def _chunk_sort_key(p: Dict, pid: str) -> Tuple[int,int,str]: # Primär: payload.chunk_index, sekundär: Nummer am Ende der ID (#cNN oder #NN), sonst 0 @@ -104,9 +119,11 @@ def main() -> None: ap.add_argument("--min-coverage", type=float, default=0.90, help="Mindestabdeckung durch Chunks (Default: 0.90)") args = ap.parse_args() - prefix = args.prefix or os.getenv("COLLECTION_PREFIX", "mindnet") - notes_col, chunks_col, _ = _names(prefix) - cli = _client() + cfg = QdrantConfig.from_env() + if args.prefix: + cfg.prefix = args.prefix + notes_col, chunks_col, _ = collection_names(cfg.prefix) + cli = get_client(cfg) # Notes abrufen (optional filter by note_id) notes_filter = None @@ -125,7 +142,7 @@ def main() -> None: if off is None: break - results = [] + results: List[Dict] = [] total_missing_text = 0 total_notes_ok = 0 for n in notes: diff --git a/scripts/wp04_smoketest.py b/scripts/wp04_smoketest.py index 83b4b40..e004fdf 100644 --- a/scripts/wp04_smoketest.py +++ b/scripts/wp04_smoketest.py @@ -1,28 +1,60 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -scripts/wp04_smoketest.py — E2E-Schnelltest der WP-04 Endpunkte +FILE: scripts/wp04_smoketest.py +VERSION: 2.1.0 (2025-12-15) +STATUS: Active (Test-Tool) +COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) Zweck: - - Holt exemplarisch einen Vektor aus {prefix}_chunks (falls verfügbar), - - ruft POST /query mit diesem Vektor auf, - - ruft GET /graph/ für die Top-Note auf, - - druckt Kurzresultate auf STDOUT. +------- +E2E-Schnelltest der WP-04 Endpunkte (/query und /graph). +Validiert grundlegende Funktionalität der Retrieval- und Graph-APIs. -Kompatibilität: - Python 3.12+, requests, qdrant-client -Version: - 0.1.0 (Erstanlage) -Stand: - 2025-10-07 -Bezug: - WP-04 /query & /graph -Nutzung: - export QDRANT_URL="http://localhost:6333" - export MINDNET_PREFIX="mindnet" - python3 scripts/wp04_smoketest.py --api http://localhost:8000 -Änderungsverlauf: - 0.1.0 (2025-10-07) – Erstanlage. +Funktionsweise: +--------------- +1. Holt exemplarischen Chunk-Vektor aus Qdrant +2. Ruft POST /query mit Test-Query auf +3. Ruft GET /graph/ für Top-Note auf +4. Gibt Kurzresultate aus + +Ergebnis-Interpretation: +------------------------ +- Ausgabe: Test-Ergebnisse + * query_result: Ergebnis des /query Aufrufs + * graph_result: Ergebnis des /graph Aufrufs + * status: ok/error +- Exit-Code 0: Alle Tests bestanden +- Exit-Code 1: Fehler (z.B. API nicht erreichbar, keine Daten) + +Verwendung: +----------- +- Schnelle Validierung nach Deployment +- CI/CD-Tests +- Debugging von API-Problemen + +Hinweise: +--------- +- Benötigt vorhandene Daten in Qdrant +- Testet nur grundlegende Funktionalität + +Aufruf: +------- +python3 scripts/wp04_smoketest.py --api http://localhost:8000 + +Parameter: +---------- +--api URL Base-URL der mindnet API (Default: http://localhost:8000) + +Umgebungsvariablen: +------------------- +QDRANT_URL (Default: http://localhost:6333) +MINDNET_PREFIX (Default: mindnet) + +Änderungen: +----------- +v2.1.0 (2025-12-15): Dokumentation aktualisiert +v0.1.0 (2025-10-07): Initial Release """ from __future__ import annotations