""" Knowledge/RAG tool for Minecraft command and server reference lookups. Wraps the TF-IDF index built by knowledge/build_index.py. """ import json import re from pathlib import Path from typing import Dict, Any, List KNOWLEDGE_ROOT = Path(__file__).resolve().parent.parent.parent / 'knowledge' def _tokenize(text: str) -> set: return set(re.findall(r'[a-z0-9_:/.]{2,}', (text or '').lower())) def _load_index() -> dict: idx_path = KNOWLEDGE_ROOT / 'index.json' if not idx_path.exists(): return {'docs': [], 'idf': {}} return json.loads(idx_path.read_text()) def search_knowledge(query: str, limit: int = 5) -> List[Dict[str, Any]]: """Search the knowledge index for relevant documents.""" index = _load_index() q_tokens = _tokenize(query) idf = index.get('idf', {}) results = [] for doc in index.get('docs', []): d_tokens = set(doc.get('tokens', [])) overlap = q_tokens & d_tokens if not overlap: continue score = sum(idf.get(t, 0.5) for t in overlap) title_tokens = _tokenize(doc.get('title', '')) title_overlap = q_tokens & title_tokens score += len(title_overlap) * 2.0 results.append((score, doc)) results.sort(key=lambda x: x[0], reverse=True) return [{'score': round(s, 2), 'id': d['id'], 'title': d['title'], 'snippet': d['snippet'], 'source': d['source']} for s, d in results[:limit]] def get_command_reference(command: str) -> Dict[str, Any]: """Get the full reference entry for a specific command.""" cmd_path = KNOWLEDGE_ROOT / 'mc-commands' / 'commands.json' if not cmd_path.exists(): return {'found': False, 'error': 'commands.json not found'} commands = json.loads(cmd_path.read_text()) cmd_name = command.lstrip('/').lower().strip() for entry in commands: if entry.get('command', '').lower() == cmd_name: return {'found': True, 'command': entry} if cmd_name in [a.lower() for a in entry.get('aliases', [])]: return {'found': True, 'command': entry} return {'found': False, 'error': f'No reference for /{cmd_name}'} def get_server_context(server_name: str = '') -> Dict[str, Any]: """Get server configuration context.""" srv_path = KNOWLEDGE_ROOT / 'server-context' / 'servers.json' if not srv_path.exists(): return {'found': False, 'error': 'servers.json not found'} data = json.loads(srv_path.read_text()) if not server_name: return {'found': True, 'servers': data.get('servers', []), 'version_notes': data.get('version_notes', {})} for srv in data.get('servers', []): if srv.get('name', '').lower() == server_name.lower(): return {'found': True, 'server': srv, 'version_notes': data.get('version_notes', {})} return {'found': False, 'error': f'No server named {server_name}'}