@stateset/nsr
v0.2.0
Published
Node.js bindings for the NSR (Neuro-Symbolic Recursive) AI framework
Maintainers
Readme
@stateset/nsr
Node.js bindings for the NSR (Neuro-Symbolic Recursive) AI framework.
NSR is a state-of-the-art hybrid AI framework that combines neural network pattern recognition with symbolic logical reasoning for robust, explainable AI systems. These native bindings provide high-performance access to the NSR Machine from Node.js and TypeScript applications.
Installation
npm install @stateset/nsrRequirements
- Node.js >= 16
- Supported platforms:
- macOS (x64, ARM64)
- Linux (x64, ARM64, glibc and musl)
- Windows (x64, ARM64)
Quick Start
import { NSRMachine, GroundedInput, SemanticValue, TrainingExample } from '@stateset/nsr';
// Create a machine using the SCAN preset (compositional generalization)
const machine = scanMachine();
// Or create with custom configuration
const customMachine = new NSRMachineBuilder()
.embeddingDim(64)
.hiddenSize(128)
.addSymbol('walk')
.addSymbol('run')
.addSymbol('jump')
.build();
// Create training examples
const examples = [
TrainingExample.fromText('walk', SemanticValue.actions(['WALK'])),
TrainingExample.fromText('run', SemanticValue.actions(['RUN'])),
TrainingExample.fromText('jump', SemanticValue.actions(['JUMP'])),
TrainingExample.fromText('walk twice', SemanticValue.actions(['WALK', 'WALK'])),
];
// Train the machine
const stats = machine.train(examples);
console.log(`Trained on ${stats.totalExamples} examples in ${stats.trainingTimeMs}ms`);
// Run inference
const result = machine.infer([GroundedInput.text('jump twice')]);
console.log(`Output: ${result.output()}`);
console.log(`Confidence: ${result.confidence()}`);Features
- Grounded Symbol System (GSS): Unified representation combining perception, syntax, and semantics
- Neural Perception: Maps raw inputs (text, numbers, images) to symbol probabilities
- Program Synthesis: Learns functional programs for semantic computation
- Compositional Generalization: Achieves near-perfect accuracy on SCAN, PCFG, HINT, and COGS benchmarks
- TypeScript Support: Full type definitions included
API Reference
Input Types
GroundedInput
Raw input to the NSR system.
// Create different input types
const text = GroundedInput.text('hello world');
const num = GroundedInput.number(42.0);
const img = GroundedInput.image(pixelData, width, height, channels);
const emb = GroundedInput.embedding([0.1, 0.2, 0.3, ...]);
const nil = GroundedInput.nil();
// Check input type
text.isText(); // true
text.isNumber(); // false
text.isNil(); // false
// Get value
text.asText(); // 'hello world'
num.asNumber(); // 42.0Output Types
SemanticValue
Computed semantic output from NSR inference.
// Create different output types
const int = SemanticValue.integer(42);
const float = SemanticValue.float(3.14);
const bool = SemanticValue.boolean(true);
const str = SemanticValue.string('result');
const sym = SemanticValue.symbol('WALK');
const actions = SemanticValue.actions(['WALK', 'TURN_LEFT', 'WALK']);
const list = SemanticValue.list([SemanticValue.integer(1), SemanticValue.integer(2)]);
const nil = SemanticValue.null();
// Get values
int.asInteger(); // 42
float.asFloat(); // 3.14
str.asString(); // 'result'
actions.asActions(); // ['WALK', 'TURN_LEFT', 'WALK']
// Check type
nil.isNull(); // true
nil.isError(); // falseProgram Types
Primitive
Built-in primitive operations for program synthesis.
// Arithmetic
Primitive.add(); // Addition
Primitive.sub(); // Subtraction
Primitive.mul(); // Multiplication
Primitive.div(); // Division
// Comparison
Primitive.eq(); // Equality
Primitive.lt(); // Less than
Primitive.gt(); // Greater than
// Logic
Primitive.and(); // Logical AND
Primitive.or(); // Logical OR
Primitive.not(); // Logical NOT
// List operations
Primitive.cons(); // List construction
Primitive.car(); // First element
Primitive.cdr(); // Rest of list
// Utility
Primitive.identity(); // Identity function
// Get info
const add = Primitive.add();
add.arity(); // 2
add.name(); // 'add'Program
Functional programs for computing semantics.
// Constant program
const constProg = Program.constant(SemanticValue.integer(42));
// Variable reference (for lambda bodies)
const varProg = Program.var(0);
// Child reference (access child nodes in GSS)
const childProg = Program.child(0);
// Primitive application
const addProg = Program.primitive(Primitive.add(), [
Program.child(0),
Program.child(1)
]);
// Lambda abstraction
const lambdaProg = Program.lambda(2, addProg);
// Function application
const applyProg = Program.apply(lambdaProg, [
Program.constant(SemanticValue.integer(1)),
Program.constant(SemanticValue.integer(2))
]);
// Conditional
const condProg = Program.ifThenElse(
Program.child(0),
Program.constant(SemanticValue.string('yes')),
Program.constant(SemanticValue.string('no'))
);
// Program metrics
constProg.depth(); // 1
constProg.size(); // 1
constProg.isConstant(); // trueTraining
TrainingExample
A single training example for the NSR machine.
// Create from inputs and output
const example = new TrainingExample(
[GroundedInput.text('walk'), GroundedInput.text('twice')],
SemanticValue.actions(['WALK', 'WALK']),
0.5 // optional difficulty (0.0 - 1.0)
);
// Convenience constructors
const textExample = TrainingExample.fromText('hello', SemanticValue.string('HELLO'));
const tokenExample = TrainingExample.fromTokens(['walk', 'left'], SemanticValue.actions(['TURN_LEFT', 'WALK']));
// Properties
example.difficulty; // 0.5
example.inputCount; // 2TrainingStats
Statistics returned from training.
interface TrainingStats {
totalExamples: number; // Number of examples trained on
successfulAbductions: number; // Successful program inductions
trainingTimeMs: number; // Training duration in milliseconds
}Machine Configuration
NSRConfig
Configuration options for the NSR machine.
interface NSRConfig {
embeddingDim: number; // Embedding vector dimension (default: 64)
hiddenSize: number; // Hidden layer size (default: 128)
maxSeqLen: number; // Maximum sequence length (default: 64)
beamWidth: number; // Beam search width (default: 5)
enableSynthesis: boolean; // Enable program synthesis (default: true)
maxProgramDepth: number; // Maximum program depth (default: 6)
}NSRMachine
The main neuro-symbolic reasoning machine.
Construction
// Default machine
const machine = new NSRMachine();
// With configuration
const configuredMachine = NSRMachine.withConfig({
embeddingDim: 128,
hiddenSize: 256,
maxSeqLen: 128,
beamWidth: 10,
enableSynthesis: true,
maxProgramDepth: 8
});
// Using builder (recommended)
const builtMachine = new NSRMachineBuilder()
.embeddingDim(64)
.hiddenSize(128)
.maxSeqLen(64)
.beamWidth(5)
.addSymbol('walk')
.addSymbol('run')
.enableSynthesis(true)
.withExplainability()
.build();Inference
const result = machine.infer([GroundedInput.text('walk twice')]);
result.output(); // Output value as string
result.confidence(); // Confidence score (0.0 - 1.0)
result.symbols(); // Predicted symbol sequence
result.nodeCount(); // Number of GSS nodes
result.logProbability(); // Log probability of predictionTraining
const examples = [
TrainingExample.fromText('a', SemanticValue.integer(1)),
TrainingExample.fromText('b', SemanticValue.integer(2)),
];
const stats = machine.train(examples);
// stats.totalExamples: 2
// stats.successfulAbductions: 2
// stats.trainingTimeMs: 15Evaluation
const testExamples = [
TrainingExample.fromText('a', SemanticValue.integer(1)),
TrainingExample.fromText('c', SemanticValue.integer(3)),
];
const result = machine.evaluate(testExamples);
// result.accuracy: 0.5
// result.correct: 1
// result.total: 2Symbol Management
// Add symbols
const walkId = machine.addSymbol('walk');
const ids = machine.addSymbols(['run', 'jump', 'turn']);
// Query symbols
machine.getSymbolName(0); // 'walk'
machine.getSymbolId('walk'); // 0
machine.getAllSymbols(); // ['walk', 'run', 'jump', 'turn']
machine.vocabularySize; // 4Statistics
const stats = machine.statistics;
// stats.trainingExamples: 100
// stats.successfulInferences: 95
// stats.programsLearned: 12
// stats.vocabularySize: 20Preset Machines
Pre-configured machines for standard benchmarks:
import { scanMachine, pcfgMachine, hintMachine, cogsMachine } from '@stateset/nsr';
// SCAN: Compositional command interpretation
// "walk twice" -> ['WALK', 'WALK']
const scan = scanMachine();
// PCFG: Probabilistic context-free grammar tasks
// Character-level sequence transduction
const pcfg = pcfgMachine();
// HINT: Hierarchical arithmetic expressions
// "( 2 + 3 ) * 4" -> 20
const hint = hintMachine();
// COGS: Compositional generalization challenge
// Semantic parsing with systematic generalization
const cogs = cogsMachine();Utility Functions
import { version } from '@stateset/nsr';
// Get library version
console.log(version()); // '0.2.0'Examples
SCAN-style Command Learning
import { scanMachine, GroundedInput, SemanticValue, TrainingExample } from '@stateset/nsr';
const machine = scanMachine();
// Train on primitive commands
const trainData = [
TrainingExample.fromText('walk', SemanticValue.actions(['WALK'])),
TrainingExample.fromText('run', SemanticValue.actions(['RUN'])),
TrainingExample.fromText('jump', SemanticValue.actions(['JUMP'])),
TrainingExample.fromText('turn left', SemanticValue.actions(['TURN_LEFT'])),
TrainingExample.fromText('turn right', SemanticValue.actions(['TURN_RIGHT'])),
// Compositional examples
TrainingExample.fromText('walk twice', SemanticValue.actions(['WALK', 'WALK'])),
TrainingExample.fromText('jump and walk', SemanticValue.actions(['JUMP', 'WALK'])),
];
machine.train(trainData);
// Test compositional generalization
const result = machine.infer([GroundedInput.text('run twice and turn left')]);
console.log(result.output()); // ['RUN', 'RUN', 'TURN_LEFT']Arithmetic Expression Evaluation
import { hintMachine, GroundedInput, SemanticValue, TrainingExample } from '@stateset/nsr';
const machine = hintMachine();
// Train on arithmetic examples
const trainData = [
TrainingExample.fromText('1 + 2', SemanticValue.integer(3)),
TrainingExample.fromText('3 * 4', SemanticValue.integer(12)),
TrainingExample.fromText('( 2 + 3 ) * 2', SemanticValue.integer(10)),
];
machine.train(trainData);
// Evaluate new expressions
const result = machine.infer([GroundedInput.text('( 1 + 2 ) * 3')]);
console.log(result.output()); // 9Custom Symbol Vocabulary
import { NSRMachineBuilder, GroundedInput, SemanticValue, TrainingExample } from '@stateset/nsr';
// Build a machine for sentiment analysis
const machine = new NSRMachineBuilder()
.embeddingDim(128)
.hiddenSize(256)
.addSymbol('positive')
.addSymbol('negative')
.addSymbol('neutral')
.enableSynthesis(false) // Classification only
.build();
const trainData = [
TrainingExample.fromText('great product', SemanticValue.symbol('positive')),
TrainingExample.fromText('terrible experience', SemanticValue.symbol('negative')),
TrainingExample.fromText('it was okay', SemanticValue.symbol('neutral')),
];
machine.train(trainData);
const result = machine.infer([GroundedInput.text('amazing quality')]);
console.log(result.output()); // 'positive'
console.log(result.confidence()); // 0.92Building from Source
Prerequisites
- Rust 1.75+
- Node.js 16+
- npm or yarn
Build Steps
# Clone the repository
git clone https://github.com/stateset/stateset-nsr
cd stateset-nsr/nodejs
# Install dependencies
npm install
# Build native addon (release)
npm run build
# Build native addon (debug)
npm run build:debug
# Run tests
npm testCross-compilation
The package uses NAPI-RS for cross-platform native addons. Pre-built binaries are provided for common platforms. To build for other platforms:
# Build for all platforms
npm run artifacts
# Build universal binary (macOS)
npm run universalPlatform Support
| Platform | Architecture | libc | Status | |----------|--------------|------|--------| | macOS | x64 | - | Supported | | macOS | ARM64 | - | Supported | | Linux | x64 | glibc | Supported | | Linux | x64 | musl | Supported | | Linux | ARM64 | glibc | Supported | | Linux | ARM64 | musl | Supported | | Windows | x64 | MSVC | Supported | | Windows | ARM64 | MSVC | Supported |
Performance
The Node.js bindings provide near-native performance by using NAPI-RS to call directly into the Rust implementation. Key performance characteristics:
- Inference latency: < 1ms for typical inputs
- Training throughput: ~10,000 examples/second (CPU)
- Memory efficient: Rust memory management with zero-copy where possible
Error Handling
All methods that can fail throw JavaScript Error objects with descriptive messages:
try {
const result = machine.infer([GroundedInput.text('invalid input')]);
} catch (error) {
console.error('Inference failed:', error.message);
}TypeScript Support
Full TypeScript definitions are included. Import types directly:
import type {
NSRConfig,
TrainingStats,
EvaluationResult,
NSRStats
} from '@stateset/nsr';Related Packages
- Rust crate:
stateset-nsr- Core Rust implementation - Python package:
nsr- Python bindings
License
Business Source License 1.1 (BSL-1.1)
The license converts to Apache 2.0 on December 8, 2028.
Links
Contributing
Contributions are welcome! Please see the contributing guidelines for more information.
