šŸ“„ verify.py 6,171 bytes Monday 19:54 šŸ“‹ Raw

!/usr/bin/env python3

"""Complete verification of all 7 success criteria for the Knowledge Graph."""
import sys
import os
import json

os.chdir(os.path.dirname(os.path.abspath(file)))
sys.path.insert(0, '.')

from graph import (
add_entity, add_relation, get_entity, search_entities,
explore, merge_entities, decay_stale, infer_relations,
health, init_db, _rebuild_index, DB_PATH
)
import sqlite3

PASS = 0
FAIL = 0

def check(name, condition, detail=""):
global PASS, FAIL
if condition:
PASS += 1
print(f" āœ… {name}")
else:
FAIL += 1
print(f" āŒ {name} — {detail}")

Clean slate

init_db()
conn = sqlite3.connect(DB_PATH)
conn.execute("DELETE FROM relations")
conn.execute("DELETE FROM entities")
conn.commit()
conn.close()
_rebuild_index()

print("=" * 60)
print("KNOWLEDGE GRAPH — SUCCESS CRITERIA VERIFICATION")
print("=" * 60)

=== CRITERION 1: Health check ===

print("\nšŸ“‹ Criterion 1: Health endpoint returns 200")
h = health()
check("status is 'ok'", h["status"] == "ok")
check("DB path exists", os.path.exists(h["database"]["path"]))
check("embedding model reachable", h["embedding"]["reachable"])

=== CRITERION 2: Add entity and retrieve by ID ===

print("\nšŸ“‹ Criterion 2: Add entity and retrieve by ID")
e1 = add_entity("person", "Alice Smith",
"Alice is a software engineer living in Chicago.",
aliases=["Alice"], tags=["engineer", "chicago"], confidence=0.95)
entity = get_entity(e1)
check("entity returned", entity is not None, f"got {entity}")
check("correct subject", entity["subject"] == "Alice Smith")
check("correct type", entity["type"] == "person")
check("correct confidence", entity["confidence"] == 0.95)
check("aliases stored", "Alice" in entity["aliases"])
check("tags stored", "chicago" in entity["tags"])

=== CRITERION 3: Add relation between two entities ===

print("\nšŸ“‹ Criterion 3: Add relation between two entities")
e2 = add_entity("place", "Chicago", "Chicago, Illinois, USA.", tags=["city", "illinois"])
e3 = add_entity("person", "Bob Jones", "Bob is a designer in Chicago.", tags=["designer", "chicago"])

r1 = add_relation(e1, e2, "lives_in", confidence=1.0)
r2 = add_relation(e2, e3, "located_in", confidence=0.9)

entity1 = get_entity(e1)
check("entity 1 has relations", len(entity1["relations_out"]) > 0)
rel = entity1["relations_out"][0]
check("relation type is lives_in", rel["relation_type"] == "lives_in")
check("target is Chicago", rel["target_subject"] == "Chicago")

entity3 = get_entity(e3)
check("entity 3 has incoming relation", len(entity3["relations_in"]) > 0, f"got {len(entity3['relations_in'])}")
check("incoming rel points to Chicago", entity3["relations_in"][0]["relation_type"] == "located_in")

=== CRITERION 4: Semantic search ===

print("\nšŸ“‹ Criterion 4: Semantic search returns relevant results")
results = search_entities("software developer in Illinois", top_k=3)
check("search returned results", len(results) > 0, f"got {len(results)} results")
check("Alice in top results", any(r["subject"] == "Alice Smith" for r in results))

results2 = search_entities("design professional", top_k=3)
check("Bob in design search", any(r["subject"] == "Bob Jones" for r in results2))

Search should order by relevance

scores = [r["similarity"] for r in results]
check("results sorted by similarity", all(scores[i] >= scores[i+1] for i in range(len(scores)-1)))

=== CRITERION 5: Explore returns entity + 1-hop neighbors ===

print("\nšŸ“‹ Criterion 5: Explore returns entity + 1-hop neighbors")
ex = explore(e1, depth=1)
check("explore returns dict with root", "root" in ex)
check("root is Alice Smith", ex["root"]["subject"] == "Alice Smith")
check("nodes contains neighbors", len(ex["nodes"]) > 0)
check("Chicago in neighbors", any(n["subject"] == "Chicago" for n in ex["nodes"].values()))
check("edges found", len(ex["edges"]) > 0)

Depth 2 exploration

e4 = add_entity("person", "Carol Chen", "Carol is Bob's colleague.", tags=["colleague"])
add_relation(e4, e3, "colleague_of", confidence=1.0)

ex2 = explore(e2, depth=2)
check("depth 2 finds Carol via Bob", any(
n["subject"] == "Carol Chen" for n in ex2.get("nodes", {}).values()
) or any(
"Carol" in str(e) for e in ex2.get("edges", [])
))

=== CRITERION 6: Vague relation types rejected ===

print("\nšŸ“‹ Criterion 6: Vague relation types are rejected")
vague_types = ["related_to", "associated_with", "connected_to", "linked_to",
"has_relation", "involves", "correlates_with"]
all_rejected = True
for vt in vague_types:
try:
add_relation(e1, e2, vt)
all_rejected = False
print(f" āŒ '{vt}' was NOT rejected")
except ValueError:
pass

check(f"all {len(vague_types)} vague types rejected", all_rejected)

Valid types should work

valid_test = add_relation(e1, e4, "friend_of", confidence=0.5)
check("valid type 'friend_of' accepted", valid_test is not None)

Invalid entity types rejected

try:
add_entity("robot", "TestBot", "A robot.")
check("invalid entity type rejected", False)
except ValueError:
check("invalid entity type rejected", True)

=== CRITERION 7: Duplicate subjects merged ===

print("\nšŸ“‹ Criterion 7: Duplicate subjects merged, not duplicated")
e_dup = add_entity("person", "alice smith", "Same person, different case.", tags=["duplicate"])
check("dedup returns original ID", e_dup == e1, f"{e_dup} vs {e1}")

Verify merged data

merged = get_entity(e1)
check("tags merged", "duplicate" in merged["tags"], f"tags: {merged['tags']}")
check("original tag preserved", "engineer" in merged["tags"])
check("different case in aliases", "alice smith" in merged["aliases"])

Different entity types with same name should NOT merge

e_diff_type = add_entity("project", "Alice Smith", "A project named after Alice.")
check("different type, same name = separate entity", e_diff_type != e1,
f"{e_diff_type} vs {e1}")
check("project entity accessible", get_entity(e_diff_type) is not None)

print()
print("=" * 60)
print(f"RESULTS: {PASS} passed, {FAIL} failed out of {PASS + FAIL} checks")
print("=" * 60)

sys.exit(0 if FAIL == 0 else 1)