Update qdrant_points.py, ingestion_processor.py, and import_markdown.py to version 4.1.0: Enhance edge ID generation by incorporating target_section for improved multigraph support and symmetry integrity. Update documentation and logging for clarity, ensuring consistent ID generation across phases and compatibility with the ingestion workflow.
This commit is contained in:
parent
2da98e8e37
commit
be2bed9927
|
|
@ -2,7 +2,7 @@
|
||||||
FILE: app/core/database/qdrant_points.py
|
FILE: app/core/database/qdrant_points.py
|
||||||
DESCRIPTION: Object-Mapper für Qdrant. Konvertiert JSON-Payloads (Notes, Chunks, Edges)
|
DESCRIPTION: Object-Mapper für Qdrant. Konvertiert JSON-Payloads (Notes, Chunks, Edges)
|
||||||
in PointStructs und generiert deterministische UUIDs.
|
in PointStructs und generiert deterministische UUIDs.
|
||||||
VERSION: 4.0.0 (WP-24c: Gold-Standard Identity - 4-Parameter-ID)
|
VERSION: 4.1.0 (WP-24c: Gold-Standard Identity v4.1.0 - target_section Support)
|
||||||
STATUS: Active
|
STATUS: Active
|
||||||
DEPENDENCIES: qdrant_client, uuid, os, app.core.graph.graph_utils
|
DEPENDENCIES: qdrant_client, uuid, os, app.core.graph.graph_utils
|
||||||
LAST_ANALYSIS: 2026-01-10
|
LAST_ANALYSIS: 2026-01-10
|
||||||
|
|
@ -95,11 +95,12 @@ def _normalize_edge_payload(pl: dict) -> dict:
|
||||||
def points_for_edges(prefix: str, edge_payloads: List[dict]) -> Tuple[str, List[rest.PointStruct]]:
|
def points_for_edges(prefix: str, edge_payloads: List[dict]) -> Tuple[str, List[rest.PointStruct]]:
|
||||||
"""
|
"""
|
||||||
Konvertiert Kanten-Payloads in PointStructs.
|
Konvertiert Kanten-Payloads in PointStructs.
|
||||||
WP-24c v4.0.0: Nutzt die zentrale _mk_edge_id Funktion aus graph_utils.
|
WP-24c v4.1.0: Nutzt die zentrale _mk_edge_id Funktion aus graph_utils.
|
||||||
Dies eliminiert den ID-Drift zwischen manuellen und virtuellen Kanten.
|
Dies eliminiert den ID-Drift zwischen manuellen und virtuellen Kanten.
|
||||||
|
|
||||||
GOLD-STANDARD v4.0.0: Die ID-Generierung verwendet STRICT nur die 4 Parameter
|
GOLD-STANDARD v4.1.0: Die ID-Generierung verwendet 4 Parameter + optional target_section
|
||||||
(kind, source_id, target_id, scope). rule_id und variant werden ignoriert.
|
(kind, source_id, target_id, scope, target_section).
|
||||||
|
rule_id und variant werden ignoriert, target_section fließt ein (Multigraph-Support).
|
||||||
"""
|
"""
|
||||||
_, _, edges_col = _names(prefix)
|
_, _, edges_col = _names(prefix)
|
||||||
points: List[rest.PointStruct] = []
|
points: List[rest.PointStruct] = []
|
||||||
|
|
@ -107,23 +108,26 @@ def points_for_edges(prefix: str, edge_payloads: List[dict]) -> Tuple[str, List[
|
||||||
for raw in edge_payloads:
|
for raw in edge_payloads:
|
||||||
pl = _normalize_edge_payload(raw)
|
pl = _normalize_edge_payload(raw)
|
||||||
|
|
||||||
# Extraktion der Identitäts-Parameter (GOLD-STANDARD v4.0.0: nur 4 Parameter)
|
# Extraktion der Identitäts-Parameter (GOLD-STANDARD v4.1.0)
|
||||||
kind = pl.get("kind", "edge")
|
kind = pl.get("kind", "edge")
|
||||||
s = pl.get("source_id", "unknown-src")
|
s = pl.get("source_id", "unknown-src")
|
||||||
t = pl.get("target_id", "unknown-tgt")
|
t = pl.get("target_id", "unknown-tgt")
|
||||||
scope = pl.get("scope", "note")
|
scope = pl.get("scope", "note")
|
||||||
|
target_section = pl.get("target_section") # WP-24c v4.1.0: target_section für Section-Links
|
||||||
|
|
||||||
# Hinweis: rule_id und variant werden im Payload gespeichert,
|
# Hinweis: rule_id und variant werden im Payload gespeichert,
|
||||||
# fließen aber NICHT in die ID-Generierung ein (v4.0.0 Standard)
|
# fließen aber NICHT in die ID-Generierung ein (v4.0.0 Standard)
|
||||||
|
# target_section fließt in die ID ein (v4.1.0: Multigraph-Support für Section-Links)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Aufruf der Single-Source-of-Truth für IDs
|
# Aufruf der Single-Source-of-Truth für IDs
|
||||||
# GOLD-STANDARD v4.0.0: Nur 4 Parameter werden verwendet
|
# GOLD-STANDARD v4.1.0: 4 Parameter + optional target_section
|
||||||
point_id = _mk_edge_id(
|
point_id = _mk_edge_id(
|
||||||
kind=kind,
|
kind=kind,
|
||||||
s=s,
|
s=s,
|
||||||
t=t,
|
t=t,
|
||||||
scope=scope
|
scope=scope,
|
||||||
|
target_section=target_section
|
||||||
)
|
)
|
||||||
|
|
||||||
# Synchronisierung des Payloads mit der berechneten ID
|
# Synchronisierung des Payloads mit der berechneten ID
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ DESCRIPTION: Der zentrale IngestionService (Orchestrator).
|
||||||
WP-25a: Integration der Mixture of Experts (MoE) Architektur.
|
WP-25a: Integration der Mixture of Experts (MoE) Architektur.
|
||||||
WP-15b: Two-Pass Workflow mit globalem Kontext-Cache.
|
WP-15b: Two-Pass Workflow mit globalem Kontext-Cache.
|
||||||
WP-20/22: Cloud-Resilienz und Content-Lifecycle integriert.
|
WP-20/22: Cloud-Resilienz und Content-Lifecycle integriert.
|
||||||
AUDIT v4.0.0:
|
AUDIT v4.1.0:
|
||||||
- GOLD-STANDARD v4.0.0: Phase 2 verwendet exakt dieselbe 4-Parameter-ID wie Phase 1.
|
- GOLD-STANDARD v4.1.0: Symmetrie-Integrität korrigiert (note_id, source_id, kind, target_section).
|
||||||
|
- Phase 2 verwendet exakt dieselbe ID-Generierung wie Phase 1 (inkl. target_section).
|
||||||
- Authority-Check in Phase 2 prüft mit konsistenter ID-Generierung.
|
- Authority-Check in Phase 2 prüft mit konsistenter ID-Generierung.
|
||||||
- Eliminiert Duplikate durch inkonsistente ID-Generierung (Steinzeitaxt-Problem).
|
- Eliminiert Duplikate durch inkonsistente ID-Generierung (Steinzeitaxt-Problem).
|
||||||
- Beibehaltung der strikten 2-Phasen-Strategie (Authority-First).
|
- Beibehaltung der strikten 2-Phasen-Strategie (Authority-First).
|
||||||
VERSION: 4.0.0 (WP-24c: Gold-Standard Identity)
|
VERSION: 4.1.0 (WP-24c: Gold-Standard Identity v4.1.0)
|
||||||
STATUS: Active
|
STATUS: Active
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -146,21 +147,31 @@ class IngestionService:
|
||||||
logger.info(f"🔄 PHASE 2: Validiere {len(self.symmetry_buffer)} Symmetrien gegen Live-DB...")
|
logger.info(f"🔄 PHASE 2: Validiere {len(self.symmetry_buffer)} Symmetrien gegen Live-DB...")
|
||||||
final_virtuals = []
|
final_virtuals = []
|
||||||
for v_edge in self.symmetry_buffer:
|
for v_edge in self.symmetry_buffer:
|
||||||
src, tgt, kind = v_edge.get("note_id"), v_edge.get("target_id"), v_edge.get("kind")
|
# WP-24c v4.1.0: Korrekte Extraktion der Identitäts-Parameter
|
||||||
if not src or not tgt: continue
|
src = v_edge.get("source_id") or v_edge.get("note_id") # source_id hat Priorität
|
||||||
|
tgt = v_edge.get("target_id")
|
||||||
|
kind = v_edge.get("kind")
|
||||||
|
scope = v_edge.get("scope", "note")
|
||||||
|
target_section = v_edge.get("target_section") # WP-24c v4.1.0: target_section berücksichtigen
|
||||||
|
|
||||||
# WP-24c v4.0.0: Nutzung der zentralisierten ID-Logik aus graph_utils
|
if not all([src, tgt, kind]):
|
||||||
# GOLD-STANDARD: Exakt 4 Parameter (kind, source, target, scope)
|
continue
|
||||||
|
|
||||||
|
# WP-24c v4.1.0: Nutzung der zentralisierten ID-Logik aus graph_utils
|
||||||
|
# GOLD-STANDARD v4.1.0: ID-Generierung muss absolut synchron zu Phase 1 sein
|
||||||
|
# - Wenn target_section vorhanden, muss es in die ID einfließen
|
||||||
|
# - Dies stellt sicher, dass der Authority-Check korrekt funktioniert
|
||||||
try:
|
try:
|
||||||
v_id = _mk_edge_id(kind, src, tgt, "note")
|
v_id = _mk_edge_id(kind, src, tgt, scope, target_section=target_section)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# AUTHORITY-CHECK: Nur schreiben, wenn keine manuelle Kante existiert
|
# AUTHORITY-CHECK: Nur schreiben, wenn keine manuelle Kante existiert
|
||||||
# Prüft mit exakt derselben 4-Parameter-ID, die in Phase 1 verwendet wurde
|
# Prüft mit exakt derselben ID, die in Phase 1 verwendet wurde (inkl. target_section)
|
||||||
if not is_explicit_edge_present(self.client, self.prefix, v_id):
|
if not is_explicit_edge_present(self.client, self.prefix, v_id):
|
||||||
final_virtuals.append(v_edge)
|
final_virtuals.append(v_edge)
|
||||||
logger.info(f" 🔄 [SYMMETRY] Add inverse: {src} --({kind})--> {tgt}")
|
section_info = f" (section: {target_section})" if target_section else ""
|
||||||
|
logger.info(f" 🔄 [SYMMETRY] Add inverse: {src} --({kind})--> {tgt}{section_info}")
|
||||||
else:
|
else:
|
||||||
logger.info(f" 🛡️ [PROTECTED] Manuelle Kante gefunden. Symmetrie für {kind} unterdrückt.")
|
logger.info(f" 🛡️ [PROTECTED] Manuelle Kante gefunden. Symmetrie für {kind} unterdrückt.")
|
||||||
|
|
||||||
|
|
@ -245,14 +256,41 @@ class IngestionService:
|
||||||
if not self._is_valid_id(t_id): continue
|
if not self._is_valid_id(t_id): continue
|
||||||
|
|
||||||
resolved_kind = edge_registry.resolve(e.get("kind", "related_to"), provenance="explicit")
|
resolved_kind = edge_registry.resolve(e.get("kind", "related_to"), provenance="explicit")
|
||||||
e.update({"kind": resolved_kind, "target_id": t_id, "origin_note_id": note_id, "virtual": False})
|
# WP-24c v4.1.0: target_section aus dem Edge-Payload extrahieren und beibehalten
|
||||||
|
target_section = e.get("target_section")
|
||||||
|
e.update({
|
||||||
|
"kind": resolved_kind,
|
||||||
|
"relation": resolved_kind, # Konsistenz: kind und relation identisch
|
||||||
|
"target_id": t_id,
|
||||||
|
"source_id": e.get("source_id") or note_id, # Sicherstellen, dass source_id gesetzt ist
|
||||||
|
"origin_note_id": note_id,
|
||||||
|
"virtual": False
|
||||||
|
})
|
||||||
explicit_edges.append(e)
|
explicit_edges.append(e)
|
||||||
|
|
||||||
# Symmetrie puffern
|
# Symmetrie puffern (WP-24c v4.1.0: Korrekte Symmetrie-Integrität)
|
||||||
inv_kind = edge_registry.get_inverse(resolved_kind)
|
inv_kind = edge_registry.get_inverse(resolved_kind)
|
||||||
if inv_kind and t_id != note_id:
|
if inv_kind and t_id != note_id:
|
||||||
v_edge = e.copy()
|
# GOLD-STANDARD v4.1.0: Symmetrie-Integrität
|
||||||
v_edge.update({"note_id": t_id, "target_id": note_id, "kind": inv_kind, "virtual": True, "origin_note_id": note_id})
|
# - note_id: Besitzer-Wechsel zum Link-Ziel
|
||||||
|
# - source_id: Neue Quelle (Note-ID des Link-Ziels)
|
||||||
|
# - target_id: Ursprüngliche Quelle (note_id)
|
||||||
|
# - kind/relation: Invers setzen
|
||||||
|
# - target_section: Beibehalten (falls vorhanden)
|
||||||
|
# - scope: Immer "note" für Symmetrien (Note-Level Backbone)
|
||||||
|
v_edge = {
|
||||||
|
"note_id": t_id, # Besitzer-Wechsel: Symmetrie gehört zum Link-Ziel
|
||||||
|
"source_id": t_id, # Neue Quelle ist das Link-Ziel
|
||||||
|
"target_id": note_id, # Ziel ist die ursprüngliche Quelle
|
||||||
|
"kind": inv_kind, # Inverser Kanten-Typ
|
||||||
|
"relation": inv_kind, # Konsistenz: kind und relation identisch
|
||||||
|
"scope": "note", # Symmetrien sind immer Note-Level
|
||||||
|
"virtual": True,
|
||||||
|
"origin_note_id": note_id, # Tracking: Woher kommt die Symmetrie
|
||||||
|
}
|
||||||
|
# target_section beibehalten, falls vorhanden (für Section-Links)
|
||||||
|
if target_section:
|
||||||
|
v_edge["target_section"] = target_section
|
||||||
self.symmetry_buffer.append(v_edge)
|
self.symmetry_buffer.append(v_edge)
|
||||||
|
|
||||||
# DB Upsert
|
# DB Upsert
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
FILE: scripts/import_markdown.py
|
FILE: scripts/import_markdown.py
|
||||||
VERSION: 2.6.1 (2026-01-10)
|
VERSION: 2.6.2 (WP-24c: Gold-Standard v4.1.0)
|
||||||
STATUS: Active (Core)
|
STATUS: Active (Core)
|
||||||
COMPATIBILITY: IngestionProcessor v3.4.2+, graph_utils v1.6.2+
|
COMPATIBILITY: IngestionProcessor v4.0.0+, graph_utils v4.1.0+
|
||||||
|
|
||||||
Zweck:
|
Zweck:
|
||||||
-------
|
-------
|
||||||
|
|
@ -108,7 +108,19 @@ async def main_async(args):
|
||||||
# Diese Liste stellt sicher, dass keine System-Leichen oder temporäre Dateien
|
# Diese Liste stellt sicher, dass keine System-Leichen oder temporäre Dateien
|
||||||
# den Graphen korrumpieren oder zu ID-Kollisionen führen.
|
# den Graphen korrumpieren oder zu ID-Kollisionen führen.
|
||||||
files = []
|
files = []
|
||||||
ignore_list = [".trash", ".obsidian", ".sync", "templates", "_system", ".git"]
|
|
||||||
|
# WP-24c v4.1.0: MINDNET_IGNORE_FOLDERS aus Umgebungsvariable
|
||||||
|
# Format: Komma-separierte Liste von Ordnernamen (z.B. "trash,temp,archive")
|
||||||
|
env_ignore = os.getenv("MINDNET_IGNORE_FOLDERS", "")
|
||||||
|
env_ignore_list = [f.strip() for f in env_ignore.split(",") if f.strip()] if env_ignore else []
|
||||||
|
|
||||||
|
# Standard-Ignore-Liste (System-Ordner)
|
||||||
|
default_ignore_list = [".trash", ".obsidian", ".sync", "templates", "_system", ".git"]
|
||||||
|
|
||||||
|
# Kombinierte Ignore-Liste (Umgebungsvariable hat Priorität, wird mit Defaults kombiniert)
|
||||||
|
ignore_list = list(set(default_ignore_list + env_ignore_list))
|
||||||
|
|
||||||
|
logger.info(f"📁 Ignore-Liste: {ignore_list}")
|
||||||
|
|
||||||
for f in all_files_raw:
|
for f in all_files_raw:
|
||||||
f_str = str(f)
|
f_str = str(f)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user