| """ |
| Category lookups for topic_metadata.json. |
| |
| Used by the router to resolve "compare this with other X" style queries |
| that reference the previous topic implicitly, without naming a second |
| topic explicitly (e.g. "compare BFS with other graph algorithms"). |
| """ |
|
|
| import json |
| import os |
|
|
| import config |
| from logs.logger import get_logger |
|
|
| logger = get_logger(__name__) |
|
|
| _metadata_cache = None |
|
|
|
|
| def _load_metadata() -> dict: |
| global _metadata_cache |
| if _metadata_cache is None: |
| if not os.path.exists(config.TOPIC_METADATA_PATH): |
| logger.warning("topic_metadata.json not found; category lookups disabled.") |
| _metadata_cache = {} |
| else: |
| with open(config.TOPIC_METADATA_PATH, "r", encoding="utf-8") as f: |
| _metadata_cache = json.load(f) |
| return _metadata_cache |
|
|
|
|
| def get_category(topic_name: str) -> str | None: |
| """Returns the category string for a canonical topic name, or None.""" |
| for entry in _load_metadata().values(): |
| if entry.get("topic") == topic_name: |
| return entry.get("category") |
| return None |
|
|
|
|
| def get_sibling_topics(topic_name: str, exclude: list = None, limit: int = 2) -> list: |
| """ |
| Returns up to `limit` other topic names sharing the same category as |
| `topic_name` (e.g. siblings of "Breadth-First Search" in category |
| "Graph Traversal" -> ["Depth-First Search", "Dijkstra's Algorithm"]). |
| """ |
| exclude = set(exclude or []) | {topic_name} |
| category = get_category(topic_name) |
| if not category: |
| return [] |
|
|
| siblings = [ |
| entry["topic"] |
| for entry in _load_metadata().values() |
| if entry.get("category") == category and entry.get("topic") not in exclude |
| ] |
| return siblings[:limit] |