mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-06-22 10:58:03 +08:00
fix(understand-knowledge): Windows path + zod schema null compatibility
Four single-line fixes that make `/understand-knowledge` work end-to-end on Windows.
## Root causes
1. Path separator mismatch (3 occurrences in parse-knowledge-base.py)
- `str(rel.with_suffix(""))` returns backslash-separated stems on Windows
(e.g. `entities\foo`), while wikilinks always use forward slashes
(`[[entities/foo]]`).
- Result: name_map keys and article_ids hold `entities\foo`, while
`resolve_wikilink()` looks up `entities/foo` -> 100% miss.
- Tested on Windows 11 + Python 3.14: 151/151 wikilinks unresolved,
0 edges built from wikilinks.
Fix: use `rel.with_suffix("").as_posix()` in all three places
(lines 235, 316, 330 on main).
2. Null vs missing field (1 occurrence)
- `"category": category or None` writes `null` when category is empty.
- `KnowledgeMetaSchema.category` in packages/core/src/schema.ts is
`z.string().optional()`, which accepts `undefined`/missing but
rejects `null`.
- Result: every article node fails GraphNodeSchema validation in the
dashboard (`Invalid input: expected string, received null`),
all nodes get dropped, dashboard renders empty.
Fix: omit the field when empty using dict spread.
## After
Tested with a 27-article Karpathy wiki on Windows:
- before: 151 unresolved wikilinks, 0 edges, 0 nodes rendered in dashboard
- after: 0 unresolved, 110 wikilink edges + 23 LLM-implicit edges,
all 53 nodes (article/topic/entity/claim) render correctly
No behavior change on macOS/Linux: `as_posix()` is a no-op when the OS
already uses `/`, and dict spread produces the same key as the previous
truthy branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -232,7 +232,7 @@ def build_name_to_stem_map(wiki_root: Path) -> dict[str, str]:
|
|||||||
basename_counts: dict[str, int] = {}
|
basename_counts: dict[str, int] = {}
|
||||||
for md_file in wiki_root.rglob("*.md"):
|
for md_file in wiki_root.rglob("*.md"):
|
||||||
rel = md_file.relative_to(wiki_root)
|
rel = md_file.relative_to(wiki_root)
|
||||||
stem = str(rel.with_suffix("")) # e.g., "decisions/decision-foo"
|
stem = rel.with_suffix("").as_posix() # e.g., "decisions/decision-foo"
|
||||||
basename = md_file.stem # e.g., "decision-foo"
|
basename = md_file.stem # e.g., "decision-foo"
|
||||||
# Full relative path always maps uniquely
|
# Full relative path always maps uniquely
|
||||||
name_map[stem.lower()] = stem
|
name_map[stem.lower()] = stem
|
||||||
@@ -313,7 +313,7 @@ def parse_wiki(root: Path) -> dict:
|
|||||||
article_ids: set[str] = set()
|
article_ids: set[str] = set()
|
||||||
for md_file in sorted(wiki_root.rglob("*.md")):
|
for md_file in sorted(wiki_root.rglob("*.md")):
|
||||||
rel = md_file.relative_to(wiki_root)
|
rel = md_file.relative_to(wiki_root)
|
||||||
stem = str(rel.with_suffix(""))
|
stem = rel.with_suffix("").as_posix()
|
||||||
# Only filter infra files at root level (no parent directory)
|
# Only filter infra files at root level (no parent directory)
|
||||||
if rel.parent == Path(".") and rel.name.lower() in INFRA_FILES:
|
if rel.parent == Path(".") and rel.name.lower() in INFRA_FILES:
|
||||||
continue
|
continue
|
||||||
@@ -327,7 +327,7 @@ def parse_wiki(root: Path) -> dict:
|
|||||||
|
|
||||||
for md_file in sorted(wiki_root.rglob("*.md")):
|
for md_file in sorted(wiki_root.rglob("*.md")):
|
||||||
rel = md_file.relative_to(wiki_root)
|
rel = md_file.relative_to(wiki_root)
|
||||||
stem = str(rel.with_suffix(""))
|
stem = rel.with_suffix("").as_posix()
|
||||||
basename = md_file.stem
|
basename = md_file.stem
|
||||||
|
|
||||||
# Skip infrastructure files only at wiki root level
|
# Skip infrastructure files only at wiki root level
|
||||||
@@ -381,7 +381,7 @@ def parse_wiki(root: Path) -> dict:
|
|||||||
"complexity": complexity,
|
"complexity": complexity,
|
||||||
"knowledgeMeta": {
|
"knowledgeMeta": {
|
||||||
"wikilinks": [wl["target"] for wl in wikilinks],
|
"wikilinks": [wl["target"] for wl in wikilinks],
|
||||||
"category": category or None,
|
**({"category": category} if category else {}),
|
||||||
"content": text[:3000], # First 3000 chars for LLM analysis
|
"content": text[:3000], # First 3000 chars for LLM analysis
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user