forked from jason/echo
56 lines
2.3 KiB
Python
56 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
"""echo_quality.py — M2: keep the graph (which recall depends on) clean.
|
|
[v1.0 SCAFFOLD — helpers implemented; integration into echo_links/echo_index is the merge step]
|
|
|
|
Two graph-polluting bugs today:
|
|
1. Auto-link fires on any indexed entity whose name/alias is >=3 chars and word-matches
|
|
the body (echo_ops.py:236). A concept titled "API" or a 2-3 char alias links to every
|
|
note that happens to mention the word -> noisy, wrong edges.
|
|
2. slugify truncates to 40 chars (echo_index.py:58) with NO collision check, so two
|
|
distinct long titles can resolve to the same slug -> same path -> silent merge.
|
|
|
|
This module supplies the two guards. Integration:
|
|
* echo_ops auto-link loop -> gate each candidate on is_confident_link(name, body)
|
|
* echo_index.derive_path -> route the slug through safe_slug(existing_slugs, slug)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
# Tokens too short or too common to be a confident entity reference on their own.
|
|
MIN_NAME_LEN = 4
|
|
_COMMON = frozenset(
|
|
"api app data note task user team work code test main repo file plan goal item "
|
|
"the and for with mpm".split()
|
|
)
|
|
|
|
|
|
def is_confident_link(name: str, body: str) -> bool:
|
|
"""True only if `name` is a strong enough signal to auto-link on its appearance in
|
|
`body`. Rejects short names, common words, and non-matches. Multi-word names are
|
|
trusted at lower length (a two-word phrase is rarely incidental)."""
|
|
n = (name or "").strip()
|
|
if not n:
|
|
return False
|
|
multiword = len(n.split()) > 1
|
|
if not multiword:
|
|
if len(n) < MIN_NAME_LEN or n.lower() in _COMMON:
|
|
return False
|
|
return re.search(rf"\b{re.escape(n)}\b", body or "", re.IGNORECASE) is not None
|
|
|
|
|
|
def safe_slug(existing_slugs, slug: str, max_len: int = 40) -> str:
|
|
"""Return `slug` if free, else a disambiguated `slug-2`, `slug-3`, ... that still
|
|
fits `max_len` and does not collide with anything in `existing_slugs`."""
|
|
existing = set(existing_slugs or ())
|
|
if slug not in existing:
|
|
return slug
|
|
n = 2
|
|
while True:
|
|
suffix = f"-{n}"
|
|
base = slug[: max_len - len(suffix)] if len(slug) + len(suffix) > max_len else slug
|
|
cand = f"{base}{suffix}"
|
|
if cand not in existing:
|
|
return cand
|
|
n += 1
|