exo_node/
lib.rs

1//! Node.js bindings for EXO-AI cognitive substrate via NAPI-RS
2//!
3//! High-performance Rust-based cognitive substrate with async/await support,
4//! hypergraph queries, and temporal memory.
5
6#![deny(clippy::all)]
7#![warn(clippy::pedantic)]
8
9use napi::bindgen_prelude::*;
10use napi_derive::napi;
11
12use exo_backend_classical::ClassicalBackend;
13use exo_core::{Pattern, SubstrateBackend};
14use std::sync::Arc;
15
16mod types;
17use types::*;
18
19/// EXO-AI cognitive substrate for Node.js
20///
21/// Provides vector similarity search, hypergraph queries, and temporal memory
22/// backed by the high-performance ruvector database.
23#[napi]
24pub struct ExoSubstrateNode {
25    backend: Arc<ClassicalBackend>,
26}
27
28#[napi]
29impl ExoSubstrateNode {
30    /// Create a new substrate instance
31    ///
32    /// # Example
33    /// ```javascript
34    /// const substrate = new ExoSubstrateNode({
35    ///   dimensions: 384,
36    ///   distanceMetric: 'Cosine'
37    /// });
38    /// ```
39    #[napi(constructor)]
40    pub fn new(dimensions: u32) -> Result<Self> {
41        let backend = ClassicalBackend::with_dimensions(dimensions as usize)
42            .map_err(|e| Error::from_reason(format!("Failed to create backend: {}", e)))?;
43
44        Ok(Self {
45            backend: Arc::new(backend),
46        })
47    }
48
49    /// Create a substrate with default configuration (768 dimensions)
50    ///
51    /// # Example
52    /// ```javascript
53    /// const substrate = ExoSubstrateNode.withDimensions(384);
54    /// ```
55    #[napi(factory)]
56    pub fn with_dimensions(dimensions: u32) -> Result<Self> {
57        Self::new(dimensions)
58    }
59
60    /// Store a pattern in the substrate
61    ///
62    /// Returns the ID of the stored pattern
63    ///
64    /// # Example
65    /// ```javascript
66    /// const id = await substrate.store({
67    ///   embedding: new Float32Array([1.0, 2.0, 3.0, ...]),
68    ///   metadata: '{"text": "example", "category": "demo"}',
69    ///   salience: 1.0
70    /// });
71    /// ```
72    #[napi]
73    pub fn store(&self, pattern: JsPattern) -> Result<String> {
74        let core_pattern: Pattern = pattern.try_into()?;
75        let pattern_id = core_pattern.id;
76
77        self.backend
78            .manifold_deform(&core_pattern, 0.0)
79            .map_err(|e| Error::from_reason(format!("Failed to store pattern: {}", e)))?;
80
81        Ok(pattern_id.to_string())
82    }
83
84    /// Search for similar patterns
85    ///
86    /// Returns an array of search results sorted by similarity
87    ///
88    /// # Example
89    /// ```javascript
90    /// const results = await substrate.search(
91    ///   new Float32Array([1.0, 2.0, 3.0, ...]),
92    ///   10  // top-k
93    /// );
94    /// ```
95    #[napi]
96    pub fn search(&self, embedding: Float32Array, k: u32) -> Result<Vec<JsSearchResult>> {
97        let results = self
98            .backend
99            .similarity_search(&embedding.to_vec(), k as usize, None)
100            .map_err(|e| Error::from_reason(format!("Failed to search: {}", e)))?;
101
102        Ok(results.into_iter().map(Into::into).collect())
103    }
104
105    /// Query hypergraph topology
106    ///
107    /// Performs topological data analysis queries on the substrate
108    /// Note: This feature is not yet fully implemented in the classical backend
109    ///
110    /// # Example
111    /// ```javascript
112    /// const result = await substrate.hypergraphQuery('{"BettiNumbers":{"max_dimension":3}}');
113    /// ```
114    #[napi]
115    pub fn hypergraph_query(&self, _query: String) -> Result<String> {
116        // Hypergraph queries are not supported in the classical backend yet
117        // Return a NotSupported response
118        Ok(r#"{"NotSupported":null}"#.to_string())
119    }
120
121    /// Get substrate dimensions
122    ///
123    /// # Example
124    /// ```javascript
125    /// const dims = substrate.dimensions();
126    /// console.log(`Dimensions: ${dims}`);
127    /// ```
128    #[napi]
129    pub fn dimensions(&self) -> u32 {
130        self.backend.dimension() as u32
131    }
132}
133
134/// Get the version of the EXO-AI library
135#[napi]
136pub fn version() -> String {
137    env!("CARGO_PKG_VERSION").to_string()
138}
139
140/// Test function to verify the bindings are working
141#[napi]
142pub fn hello() -> String {
143    "Hello from EXO-AI cognitive substrate!".to_string()
144}