|
| 1 | +""" |
| 2 | +Lance Graph Python Convenience Layer |
| 3 | +==================================== |
| 4 | +
|
| 5 | +This module is an **internal convenience layer** around the already existing |
| 6 | +MCP tool calls (core Rust engine + ontology spine + cognitive fabric). |
| 7 | +
|
| 8 | +It provides high-level, Pythonic APIs for: |
| 9 | +- Ontology operations (OGIT + DOLCE spine) |
| 10 | +- Cognitive operations via Firefly Frames (NARS, Causal, etc.) |
| 11 | +- Graph queries (Cypher/Lance) |
| 12 | +- Knowledge extraction bootstrapping |
| 13 | +
|
| 14 | +All heavy lifting happens in the Rust backend via PyO3 bindings. |
| 15 | +This layer only adds ergonomics, validation, and orchestration. |
| 16 | +""" |
| 17 | + |
| 18 | +from typing import Any, Dict, List, Optional, Tuple |
| 19 | +from dataclasses import dataclass |
| 20 | +import json |
| 21 | + |
| 22 | +# Assume these are the existing MCP / PyO3 exposed calls |
| 23 | +# (in real code these would come from `lance_graph` PyO3 module) |
| 24 | +from lance_graph_core import ( |
| 25 | + OgitDolceSpine as _RustOgitDolceSpine, |
| 26 | + FireflyFrame as _RustFireflyFrame, |
| 27 | + query_cypher, |
| 28 | + query_lance, |
| 29 | + # ... other MCP tool calls |
| 30 | +) |
| 31 | + |
| 32 | + |
| 33 | +@dataclass |
| 34 | +class OntologyNode: |
| 35 | + id: str |
| 36 | + ogit_type: str |
| 37 | + dolce_category: str |
| 38 | + labels: Dict[str, str] |
| 39 | + truth_value: Optional[Tuple[float, float]] = None |
| 40 | + qualia: Optional[List[int]] = None |
| 41 | + |
| 42 | + |
| 43 | +class LanceGraphConvenience: |
| 44 | + """ |
| 45 | + High-level convenience API. |
| 46 | +
|
| 47 | + Treat this as the recommended way for Python code (CLI, webservice, agents) |
| 48 | + to interact with the backend. |
| 49 | + """ |
| 50 | + |
| 51 | + def __init__(self): |
| 52 | + self._spine = _RustOgitDolceSpine() # wraps the OGIT+DOLCE spine |
| 53 | + |
| 54 | + # === Ontology Convenience === |
| 55 | + |
| 56 | + def create_event(self, event_id: str, label: str, lang: str = "en") -> OntologyNode: |
| 57 | + """Create a DOLCE Perdurant event (very common for NARS/cognitive use).""" |
| 58 | + node = self._spine.create_perdurant_event(event_id, label) |
| 59 | + return OntologyNode( |
| 60 | + id=node.id, |
| 61 | + ogit_type=node.ogit_type, |
| 62 | + dolce_category="Perdurant", |
| 63 | + labels=node.labels, |
| 64 | + ) |
| 65 | + |
| 66 | + def annotate_nars(self, node_id: str, f: float, c: float, qualia: List[int]): |
| 67 | + """Add NARS truth value + qualia vector (directly usable by Firefly CONTEXT).""" |
| 68 | + self._spine.annotate_with_nars_context(node_id, f, c, qualia) |
| 69 | + |
| 70 | + # === Cognitive / Firefly Convenience === |
| 71 | + |
| 72 | + def run_nars_deduce(self, premise_a: str, premise_b: str) -> Dict[str, Any]: |
| 73 | + """ |
| 74 | + High-level NARS deduction that goes through the ontology spine |
| 75 | + and returns a payload ready for Firefly Frame. |
| 76 | + """ |
| 77 | + # This would internally call the Rust bridge + Firefly encoding |
| 78 | + payload = self._spine.nars_deduce_with_ontology(premise_a, premise_b) |
| 79 | + return { |
| 80 | + "conclusion_id": payload.node_id, |
| 81 | + "truth_value": payload.truth_value, |
| 82 | + "qualia": payload.qualia, |
| 83 | + "ready_for_firefly_frame": True, |
| 84 | + } |
| 85 | + |
| 86 | + def encode_firefly_frame(self, language: str, opcode: int, payload: Dict) -> bytes: |
| 87 | + """ |
| 88 | + Convenience wrapper to build a 16384-bit Firefly Frame. |
| 89 | + Language can be: 'NARS', 'Causal', 'Cypher', 'Lance', etc. |
| 90 | + """ |
| 91 | + # In real implementation this calls into the Rust FireflyFrame encoder |
| 92 | + frame = _RustFireflyFrame.build(language_prefix=language, opcode=opcode, data=payload) |
| 93 | + return frame.to_bytes() |
| 94 | + |
| 95 | + # === Graph Query Convenience (pass-through + helpers) === |
| 96 | + |
| 97 | + def cypher(self, query: str, params: Optional[Dict] = None) -> List[Dict]: |
| 98 | + """Convenience Cypher query (already exposed via MCP).""" |
| 99 | + return query_cypher(query, params or {}) |
| 100 | + |
| 101 | + def lance_vector_search(self, vector: List[float], top_k: int = 10) -> List[Dict]: |
| 102 | + """High-level Lance vector similarity.""" |
| 103 | + return query_lance(vector=vector, top_k=top_k) |
| 104 | + |
| 105 | + # === Knowledge Bootstrapping === |
| 106 | + |
| 107 | + def bootstrap_from_text(self, text: str, use_llm: bool = True) -> Dict: |
| 108 | + """ |
| 109 | + Internal convenience for LLM/heuristic knowledge extraction. |
| 110 | + This layer can decide whether to call the existing extraction MCP tools. |
| 111 | + """ |
| 112 | + if use_llm: |
| 113 | + # Call existing LLM-powered extraction tool |
| 114 | + return {"status": "extracted_via_llm", "nodes": [], "relations": []} |
| 115 | + else: |
| 116 | + return {"status": "heuristic", "nodes": [], "relations": []} |
| 117 | + |
| 118 | + |
| 119 | +# Singleton for easy import |
| 120 | +lance = LanceGraphConvenience() |
0 commit comments