Dateien nach "app/core" hochladen
All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 4s

This commit is contained in:
Lars 2025-11-17 21:55:41 +01:00
parent bfdd8ac6be
commit 9a7bc32051

View File

@ -8,8 +8,7 @@ Zweck:
- Unterstützt "typed inline relations": - Unterstützt "typed inline relations":
* [[rel:KIND | Target]] * [[rel:KIND | Target]]
* [[rel:KIND Target]] * [[rel:KIND Target]]
* rel: KIND [[Target]] (1 Ziel) * rel: KIND [[Target]]
* rel: KIND [[T1]] [[T2]] ... (NEU: mehrere Ziele in einer Zeile)
- Unterstützt Obsidian-Callouts: - Unterstützt Obsidian-Callouts:
* > [!edge] KIND: [[Target]] [[Target2]] ... * > [!edge] KIND: [[Target]] [[Target2]] ...
Kompatibilität: Kompatibilität:
@ -143,68 +142,35 @@ _WIKILINK_RE = re.compile(r"\[\[(?:[^\|\]]+\|)?([a-zA-Z0-9_\-#:. ]+)\]\]")
# [[rel:KIND Target]] # [[rel:KIND Target]]
_REL_PIPE = re.compile(r"\[\[\s*rel:(?P<kind>[a-z_]+)\s*\|\s*(?P<target>[^\]]+?)\s*\]\]", re.IGNORECASE) _REL_PIPE = re.compile(r"\[\[\s*rel:(?P<kind>[a-z_]+)\s*\|\s*(?P<target>[^\]]+?)\s*\]\]", re.IGNORECASE)
_REL_SPACE = re.compile(r"\[\[\s*rel:(?P<kind>[a-z_]+)\s+(?P<target>[^\]]+?)\s*\]\]", re.IGNORECASE) _REL_SPACE = re.compile(r"\[\[\s*rel:(?P<kind>[a-z_]+)\s+(?P<target>[^\]]+?)\s*\]\]", re.IGNORECASE)
# rel: KIND [[Target]] (ein Ziel) # rel: KIND [[Target]] (reines Textmuster)
_REL_TEXT_SINGLE = re.compile(r"rel\s*:\s*(?P<kind>[a-z_]+)\s*\[\[\s*(?P<target>[^\]]+?)\s*\]\]", re.IGNORECASE) _REL_TEXT = re.compile(r"rel\s*:\s*(?P<kind>[a-z_]+)\s*\[\[\s*(?P<target>[^\]]+?)\s*\]\]", re.IGNORECASE)
# rel: KIND [[T1]] [[T2]] ... (NEU: mehrere Ziele)
_REL_TEXT_MULTI = re.compile(r"rel\s*:\s*(?P<kind>[a-z_]+)\s*(?P<targets>(?:\[\[[^\]]+\]\]\s*){1,})", re.IGNORECASE)
# Re-Use für mehrfaches Extrahieren der Wikilinks am Stück
_WIKILINKS_IN_LINE = re.compile(r"\[\[([^\]]+)\]\]")
def _extract_typed_relations(text: str) -> Tuple[List[Tuple[str,str]], str]: def _extract_typed_relations(text: str) -> Tuple[List[Tuple[str,str]], str]:
""" """
Gibt Liste (kind, target) zurück und den Text mit entfernten getypten Relation-Links, Gibt Liste (kind, target) zurück und den Text mit entfernten getypten Relation-Links,
damit die generische Wikilink-Erkennung sie nicht doppelt zählt. damit die generische Wikilink-Erkennung sie nicht doppelt zählt.
Unterstützt Varianten: Unterstützt drei Varianten:
- [[rel:KIND | Target]] - [[rel:KIND | Target]]
- [[rel:KIND Target]] - [[rel:KIND Target]]
- rel: KIND [[Target]] - rel: KIND [[Target]]
- rel: KIND [[T1]] [[T2]] ...
""" """
pairs: List[Tuple[str,str]] = [] pairs: List[Tuple[str,str]] = []
def _collect(m):
def _collect_pipe(m):
k = (m.group("kind") or "").strip().lower() k = (m.group("kind") or "").strip().lower()
t = (m.group("target") or "").strip() t = (m.group("target") or "").strip()
if k and t: if k and t:
pairs.append((k, t)) pairs.append((k, t))
return "" # Link entfernen return "" # Link entfernen
def _collect_space(m): text = _REL_PIPE.sub(_collect, text)
k = (m.group("kind") or "").strip().lower() text = _REL_SPACE.sub(_collect, text)
t = (m.group("target") or "").strip() text = _REL_TEXT.sub(_collect, text)
if k and t:
pairs.append((k, t))
return ""
def _collect_text_multi(m):
k = (m.group("kind") or "").strip().lower()
targets = m.group("targets") or ""
if k and targets:
for t in _WIKILINKS_IN_LINE.findall(targets):
t = t.strip()
if t:
pairs.append((k, t))
return ""
def _collect_text_single(m):
k = (m.group("kind") or "").strip().lower()
t = (m.group("target") or "").strip()
if k and t:
pairs.append((k, t))
return ""
# Reihenfolge wichtig: zuerst MULTI, dann SINGLE, dann die [[rel:...]]-Varianten
text = _REL_TEXT_MULTI.sub(_collect_text_multi, text)
text = _REL_TEXT_SINGLE.sub(_collect_text_single, text)
text = _REL_PIPE.sub(_collect_pipe, text)
text = _REL_SPACE.sub(_collect_space, text)
return pairs, text return pairs, text
# Obsidian Callout Parser # Obsidian Callout Parser
_CALLOUT_START = re.compile(r"^\s*>\s*\[!edge\]\s*(.*)$", re.IGNORECASE) _CALLOUT_START = re.compile(r"^\s*>\s*\[!edge\]\s*(.*)$", re.IGNORECASE)
_REL_LINE = re.compile(r"^(?P<kind>[a-z_]+)\s*:\s*(?P<targets>.+?)\s*$", re.IGNORECASE) _REL_LINE = re.compile(r"^(?P<kind>[a-z_]+)\s*:\s*(?P<targets>.+?)\s*$", re.IGNORECASE)
_WIKILINKS_IN_LINE = re.compile(r"\[\[([^\]]+)\]\]")
def _extract_callout_relations(text: str) -> Tuple[List[Tuple[str,str]], str]: def _extract_callout_relations(text: str) -> Tuple[List[Tuple[str,str]], str]:
""" """
@ -283,7 +249,7 @@ def build_edges_for_note(
- belongs_to: für jeden Chunk (chunk -> note) - belongs_to: für jeden Chunk (chunk -> note)
- next / prev: zwischen aufeinanderfolgenden Chunks - next / prev: zwischen aufeinanderfolgenden Chunks
- references: pro Chunk aus window/text (via Wikilinks) - references: pro Chunk aus window/text (via Wikilinks)
- typed inline relations: [[rel:KIND | Target]] / [[rel:KIND Target]] / rel: KIND [[Target]] / rel: KIND [[T1]] [[T2]] ... - typed inline relations: [[rel:KIND | Target]] / [[rel:KIND Target]] / rel: KIND [[Target]]
- Obsidian Callouts: > [!edge] KIND: [[Target]] [[Target2]] - Obsidian Callouts: > [!edge] KIND: [[Target]] [[Target2]]
- optional note-scope references/backlinks: dedupliziert über alle Chunk-Funde + note_level_references - optional note-scope references/backlinks: dedupliziert über alle Chunk-Funde + note_level_references
- typenbasierte Default-Kanten (edge_defaults) je gefundener Referenz - typenbasierte Default-Kanten (edge_defaults) je gefundener Referenz
@ -321,14 +287,14 @@ def build_edges_for_note(
"provenance": "rule", "provenance": "rule",
"rule_id": "structure:order", "rule_id": "structure:order",
"confidence": 0.95, "confidence": 0.95,
}))) }))
edges.append(_edge("prev", "chunk", b_id, a_id, note_id, { edges.append(_edge("prev", "chunk", b_id, a_id, note_id, {
"chunk_id": b_id, "chunk_id": b_id,
"edge_id": _mk_edge_id("prev", b_id, a_id, "chunk", "structure:order"), "edge_id": _mk_edge_id("prev", b_id, a_id, "chunk", "structure:order"),
"provenance": "rule", "provenance": "rule",
"rule_id": "structure:order", "rule_id": "structure:order",
"confidence": 0.95, "confidence": 0.95,
}))) }))
# 3) references + typed inline + callouts + defaults (chunk-scope) # 3) references + typed inline + callouts + defaults (chunk-scope)
reg = _load_types_registry() reg = _load_types_registry()
@ -341,7 +307,7 @@ def build_edges_for_note(
continue continue
raw = _chunk_text_for_refs(ch) raw = _chunk_text_for_refs(ch)
# 3a) typed inline relations (inkl. MULTI-Variante) # 3a) typed inline relations
typed, remainder = _extract_typed_relations(raw) typed, remainder = _extract_typed_relations(raw)
for kind, target in typed: for kind, target in typed:
kind = kind.strip().lower() kind = kind.strip().lower()
@ -449,7 +415,7 @@ def build_edges_for_note(
edges.append(_edge(rel, "note", r, note_id, note_id, { edges.append(_edge(rel, "note", r, note_id, note_id, {
"edge_id": _mk_edge_id(rel, r, note_id, "note", f"edge_defaults:{note_type}:{rel}"), "edge_id": _mk_edge_id(rel, r, note_id, "note", f"edge_defaults:{note_type}:{rel}"),
"provenance": "rule", "provenance": "rule",
"rule_id": f"edge_defaults:{note_type}:{rel}"), "rule_id": f"edge_defaults:{note_type}:{rel}",
"confidence": 0.7, "confidence": 0.7,
})) }))