feat: migrate embeddings to Candle WASM + remove semantic type inference

Major architectural changes:

1. EMBEDDINGS ENGINE (ONNX → Candle WASM):
   - Replace ONNX Runtime with Rust Candle compiled to WASM
   - Embedded model in WASM binary (no external downloads)
   - Quantized Q8 precision with <50MB memory footprint
   - Zero-download, offline-first operation
   - Same embedding quality (all-MiniLM-L6-v2)

2. REMOVE SEMANTIC TYPE INFERENCE:
   - Delete embeddedKeywordEmbeddings.ts (14MB of pre-computed embeddings)
   - Remove typeAwareQueryPlanner.ts and semanticTypeInference.ts
   - Remove VerbExactMatchSignal (uses keyword embeddings)
   - Update SmartRelationshipExtractor to 3 signals (55%/30%/15% weights)

API CHANGES (requires v7.0.0):
- Removed: inferTypes(), inferNouns(), inferVerbs(), inferIntent()
- Removed: getSemanticTypeInference(), SemanticTypeInference class
- Removed: TypeInference, SemanticTypeInferenceOptions types

Users can still use natural language queries in find() - they just
need to specify type explicitly for type-optimized searches.

PACKAGE SIZE IMPACT:
- Compressed: 90.1 MB → 86.2 MB (-4.3%)
- Uncompressed: 114.4 MB → 100.3 MB (-12%)
- ~448K lines of code removed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
David Snelling 2026-01-06 12:52:34 -08:00
parent 81cd16e41b
commit da7d2ed29d
60 changed files with 3887 additions and 448557 deletions

View file

@ -1,118 +0,0 @@
# 🤖 Model Loading Quick Reference
## 🚀 Common Scenarios
### ✅ Development (Zero Config)
```typescript
const brain = new Brainy()
await brain.init() // Downloads automatically (FP32 default)
```
### ⚡ Development (Optimized - v2.8.0+)
```typescript
// 75% smaller models, 99% accuracy
const brain = new Brainy({
embeddingOptions: { dtype: 'q8' }
})
await brain.init()
```
### 🐳 Docker Production
```dockerfile
# Both models (recommended)
RUN npm run download-models
# Or FP32 only (compatibility)
RUN npm run download-models:fp32
# Or Q8 only (space-constrained)
RUN npm run download-models:q8
ENV BRAINY_ALLOW_REMOTE_MODELS=false
```
### ☁️ Serverless/Lambda
```bash
# Build step
npm run download-models
# Runtime
export BRAINY_ALLOW_REMOTE_MODELS=false
```
### 🔒 Air-Gapped/Offline
```bash
# Connected machine
npm run download-models
tar -czf brainy-models.tar.gz ./models
# Offline machine
tar -xzf brainy-models.tar.gz
export BRAINY_ALLOW_REMOTE_MODELS=false
```
### 🌐 Browser/CDN
```html
<!-- Automatic - no setup needed -->
<script type="module">
import { Brainy } from 'brainy'
const brain = new Brainy()
await brain.init() // Works in browser
</script>
```
## 🚨 Troubleshooting
| Error | Solution |
|-------|----------|
| "Failed to load embedding model" | `npm run download-models` |
| "ENOENT: no such file" | Check `BRAINY_MODELS_PATH` |
| "Network timeout" | Set `BRAINY_ALLOW_REMOTE_MODELS=false` |
| "Permission denied" | `chmod 755 ./models` |
| "Out of memory" | Increase container memory limit |
## 🎯 Environment Variables
| Variable | Values | Purpose |
|----------|--------|---------|
| `BRAINY_ALLOW_REMOTE_MODELS` | `true`/`false` | Allow/block downloads |
| `BRAINY_MODELS_PATH` | `./models` | Model storage path |
| `BRAINY_Q8_CONFIRMED` | `true`/`false` | Silence Q8 compatibility warnings |
| `NODE_ENV` | `production` | Environment detection |
## 📦 Model Info
### FP32 (Default)
- **Model**: All-MiniLM-L6-v2
- **Dimensions**: 384 (fixed)
- **Size**: 90MB
- **Accuracy**: 100% (baseline)
- **Location**: `./models/Xenova/all-MiniLM-L6-v2/onnx/model.onnx`
### Q8 (Optional - v2.8.0+)
- **Model**: All-MiniLM-L6-v2 (quantized)
- **Dimensions**: 384 (same)
- **Size**: 23MB (75% smaller!)
- **Accuracy**: ~99% (minimal loss)
- **Location**: `./models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx`
**⚠️ Important**: FP32 and Q8 create different embeddings and are incompatible!
## ✅ Verification Commands
```bash
# Check FP32 model exists
ls ./models/Xenova/all-MiniLM-L6-v2/onnx/model.onnx
# Check Q8 model exists
ls ./models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx
# Test offline mode
BRAINY_ALLOW_REMOTE_MODELS=false npm test
# Download fresh models (both)
rm -rf ./models && npm run download-models
# Download specific model variant
rm -rf ./models && npm run download-models:q8
```

View file

@ -1,6 +1,8 @@
# Production Service Architecture Guide
**How to use Brainy optimally in production Express/Node.js services**
**How to use Brainy optimally in production services (Bun, Node.js, Deno)**
> **Recommended Runtime:** [Bun](https://bun.sh) provides best performance with Brainy's Candle WASM engine. All examples work with both Bun and Node.js.
---
@ -168,7 +170,57 @@ process.on('SIGTERM', async () => {
---
### Pattern 3: Express Middleware
### Pattern 3: Bun Server (Recommended)
```typescript
// server.ts - Clean Bun implementation
import { Brainy } from '@soulcraft/brainy'
let brain: Brainy | null = null
async function getBrain(): Promise<Brainy> {
if (!brain) {
brain = new Brainy({ storage: { path: './brainy-data' } })
await brain.init()
}
return brain
}
// Initialize before server starts
await getBrain()
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url)
if (url.pathname === '/api/entities') {
const b = await getBrain()
const entities = await b.find({})
return Response.json(entities)
}
if (url.pathname === '/api/entity' && req.method === 'POST') {
const b = await getBrain()
const body = await req.json()
const id = await b.add(body)
return Response.json({ id })
}
return new Response('Not Found', { status: 404 })
}
})
console.log('Server running on http://localhost:3000')
```
**Benefits:**
- ✅ Native Bun performance (~2x faster than Node.js)
- ✅ No framework dependencies
- ✅ Works with `bun --compile` for single-binary deployment
- ✅ Built-in TypeScript support
### Pattern 4: Express/Node.js Middleware (Legacy)
```typescript
// middleware/brainy.ts

View file

@ -1373,12 +1373,9 @@ const brain = new Brainy({
typeAware: true // Enable type-aware indexing (v4.0+)
},
// Model configuration
model: {
type: 'transformers', // transformers | custom
name: 'Xenova/all-MiniLM-L6-v2',
device: 'auto' // auto | cpu | gpu
},
// Model configuration (embedded in WASM - zero config needed)
// Model: all-MiniLM-L6-v2 (384 dimensions)
// Device: CPU via WASM (works everywhere)
// Cache configuration
cache: {

View file

@ -206,7 +206,7 @@ if (device === 'webgpu') {
// CUDA detection in Node:
if (device === 'cuda') {
// Requires ONNX Runtime GPU packages
// Future: GPU acceleration support
}
```

View file

@ -80,7 +80,7 @@ const entity = {
- ✅ **Semantic Understanding**: Types have meaning, not just structure
- ✅ **Tool Compatibility**: All augmentations understand core types
- ✅ **Concept Extraction**: NLP can map text to known types
- ✅ **Type Inference**: Automatic type detection via keywords/synonyms
- ✅ **Explicit Types**: Clear type specification in API
- ✅ **Query Optimization**: Type-aware query planning
- ✅ **Flexible Metadata**: Any fields within typed structure
- ✅ **Billion-Scale Ready**: Type tracking scales linearly
@ -121,45 +121,51 @@ class TypeAwareMetadataIndex {
- **After**: PROJECTED 1.2MB memory for same dataset (385x reduction - calculated from Uint32Array size, not measured)
- **Scales to billions**: Memory grows with entity count, not key diversity
### 2. Semantic Type Inference
### 2. Explicit Type System
**The Magic**: Map natural language to structured types:
**The Design**: Specify types clearly in your API calls:
```typescript
import { getSemanticTypeInference } from '@soulcraft/brainy'
import { Brainy, NounType, VerbType } from '@soulcraft/brainy'
const inference = getSemanticTypeInference()
// Add entity with explicit type
await brain.add({
data: { name: 'Alice', role: 'CEO of Acme Corp' },
type: NounType.Person // Explicit type specification
})
// Automatic type detection
await inference.inferNounType('CEO of Acme Corp')
// → 'person'
await inference.inferNounType('San Francisco office building')
// → 'place'
await inference.inferVerbType('Alice manages Bob')
// → 'manages' (relationship type)
// Query with type filtering
await brain.find({
query: 'Alice',
type: NounType.Person // Type-optimized search
})
```
**How It Works**:
1. **Keyword Matching**: "CEO", "manager" → 'person'
2. **Synonym Detection**: "building", "office" → 'place'
3. **Semantic Embeddings**: Vector similarity to type prototypes
4. **Context Analysis**: Surrounding words provide hints
**Why Explicit Types?**:
1. **Deterministic**: You control exactly how entities are classified
2. **Predictable**: No inference surprises or edge cases
3. **Fast**: No neural processing overhead on every add/query
4. **Smaller**: No embedded keyword models needed
**Real-World Use Case**:
```typescript
// Import unstructured data
const text = "Apple announced a new product line in Cupertino"
// Import data with known types
await brain.add({
data: { name: 'Apple Inc.', industry: 'Technology' },
type: NounType.Organization
})
// Brainy automatically infers:
// - "Apple" → noun type: 'organization'
// - "product line" → noun type: 'product'
// - "Cupertino" → noun type: 'place'
// - "announced" → verb type: 'announces'
// - "in" → verb type: 'locatedIn'
await brain.add({
data: { name: 'Cupertino', country: 'USA' },
type: NounType.Location
})
// Creates typed, queryable knowledge graph automatically!
// Create relationship
await brain.relate({
from: appleId,
to: cupertinoId,
type: VerbType.LocatedIn
})
```
### 3. Tool & Augmentation Compatibility
@ -364,42 +370,47 @@ function processNoun(noun: Noun) {
---
## Public API: Semantic Type Inference
## Public API: Type System
The type inference system is **fully public** for augmentation developers and external tools:
The type system is **fully public** for developers and augmentation authors:
```typescript
import {
getSemanticTypeInference,
SemanticTypeInference
NounType,
VerbType,
getNounTypes,
getVerbTypes,
BrainyTypes,
suggestType
} from '@soulcraft/brainy'
// Get singleton instance
const inference = getSemanticTypeInference()
// Get all available noun types
const nounTypes = getNounTypes()
// → ['Person', 'Organization', 'Location', 'Thing', 'Concept', ...]
// Infer noun type from text
const nounType = await inference.inferNounType('Software Engineer')
// → 'person'
// Get all available verb types
const verbTypes = getVerbTypes()
// → ['RelatedTo', 'Contains', 'CreatedBy', 'LocatedIn', ...]
// Infer verb type from relationship text
const verbType = await inference.inferVerbType('works at')
// → 'worksAt'
// Use types directly
await brain.add({
data: { name: 'Alice' },
type: NounType.Person
})
// Get type keywords for reverse lookup
const keywords = inference.getNounTypeKeywords('person')
// → ['person', 'human', 'individual', 'user', 'employee', ...]
// Get type synonyms
const synonyms = inference.getNounTypeSynonyms('organization')
// → ['company', 'corporation', 'business', 'firm', 'enterprise', ...]
// Query by type
await brain.find({
type: NounType.Person,
where: { name: 'Alice' }
})
```
**Use Cases**:
- **Import Tools**: Auto-detect entity types during data import
- **Query Builders**: Suggest types based on user input
- **Type-Safe Code**: Use TypeScript enums for compile-time checking
- **Import Tools**: Specify entity types during data import
- **Query Builders**: Filter by known types
- **Augmentations**: Type-specific processing pipelines
- **Visualization**: Type-appropriate rendering
- **Data Validation**: Ensure correct type assignments
---
@ -415,7 +426,7 @@ const synonyms = inference.getNounTypeSynonyms('organization')
| **Query Planning** | Impossible | Table statistics | Type statistics |
| **Tool Compatibility** | None | SQL only | Full ecosystem |
| **Semantic Understanding** | None | None | Built-in |
| **Concept Extraction** | Manual | Manual | Automatic |
| **Concept Extraction** | Manual | Manual | Via SmartExtractor |
| **Flexibility** | Infinite | Zero | Optimal balance |
---
@ -483,7 +494,7 @@ Brainy's **Finite Noun/Verb Type System** is revolutionary because it achieves t
2. ✅ **Semantic understanding** (NLP integration)
3. ✅ **Tool compatibility** (ecosystem interoperability)
4. ✅ **Query optimization** (type-aware planning)
5. ✅ **Concept extraction** (automatic type inference)
5. ✅ **Concept extraction** (via SmartExtractor for imports)
6. ✅ **Developer experience** (clean architecture)
7. ✅ **Flexibility** (metadata freedom within types)
@ -493,11 +504,10 @@ It's not schemaless chaos. It's not rigid relational constraints. It's **semanti
## Further Reading
- [Type Inference System](../api/type-inference.md) - API reference for semantic type detection
- [Storage Architecture](./storage-architecture.md) - How types enable billion-scale storage
- [Augmentation System](./augmentations.md) - Building type-aware augmentations
- [Concept Extraction](../guides/natural-language.md) - NLP integration with typed entities
- [Query Optimization](../api/query-optimization.md) - Type-aware query planning
- [Import Flow](../guides/import-flow.md) - How types work in the import pipeline
---

View file

@ -25,7 +25,9 @@ Built-in authentication and rate limiting when needed.
The APIServerAugmentation is included in Brainy core. No additional installation required.
For Node.js environments, you may want to install optional dependencies:
**Bun** (recommended): No additional dependencies needed - use `Bun.serve()` directly.
**Node.js**: Install optional Express dependencies:
```bash
npm install express cors ws
```
@ -259,6 +261,30 @@ Authorization: Bearer your-token
## Environment Support
### Bun ✅ (Recommended)
Native performance with Bun.serve() - no Express required. Works with `bun --compile` for single-binary deployment.
```typescript
// Simple Bun server with Brainy
import { Brainy } from '@soulcraft/brainy'
const brain = new Brainy()
await brain.init()
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url)
if (url.pathname === '/api/search') {
const { query } = await req.json()
const results = await brain.search(query)
return Response.json(results)
}
return new Response('Not Found', { status: 404 })
}
})
```
### Node.js ✅
Full support with Express, WebSocket, and all features.

View file

@ -256,9 +256,9 @@ if (device === 'webgpu') {
// Transformer models use WebGPU automatically
}
// CUDA in Node.js (requires ONNX Runtime GPU)
// CUDA in Node.js (future GPU support)
if (device === 'cuda') {
// Automatically uses GPU for embeddings
// Future: GPU acceleration for embeddings
}
```

View file

@ -470,7 +470,7 @@ const verbType = await this.inferRelationship(
**Location**: `src/neural/SmartRelationshipExtractor.ts:100`
The SmartRelationshipExtractor runs **4 signals in parallel** (just like entity extraction):
The SmartRelationshipExtractor runs **3 signals in parallel**:
```
┌──────────────────────────────────────────────────────────┐
@ -480,40 +480,34 @@ The SmartRelationshipExtractor runs **4 signals in parallel** (just like entity
│ Input Context: │
│ "Famous painting created by Leonardo da Vinci" │
│ │
│ 1. VerbExactMatchSignal (40%) │
│ → Searches 334 verb keywords │
│ → Finds phrase: "created by" │
│ → Maps to: VerbType.CreatedBy │
│ → Confidence: 0.95 │
│ │
│ 2. VerbEmbeddingSignal (35%) │
│ 1. VerbEmbeddingSignal (55%) │
│ → Embeds context: [0.23, -0.45, 0.78, ...] │
│ → Compares to 40 verb embeddings │
│ → Closest match: CreatedBy (similarity: 0.89) │
│ → Confidence: 0.89 │
│ │
3. VerbPatternSignal (20%) │
│ 2. VerbPatternSignal (30%) │
│ → Tests 48+ regex patterns │
│ → Matches: /\bcreated?\s+by\b/i │
│ → Maps to: VerbType.CreatedBy │
│ → Confidence: 0.90 │
│ │
4. VerbContextSignal (5%)
3. VerbContextSignal (15%)
│ → Type pair: (Product, Person) │
│ → Hint suggests: CreatedBy │
│ → Confidence: 0.80 │
│ │
│ Ensemble Vote: │
│ CreatedBy: 0.95×0.40 + 0.89×0.35 + 0.90×0.20 + 0.80×0.05
│ = 0.38 + 0.31 + 0.18 + 0.04
│ = 0.91
│ CreatedBy: 0.89×0.55 + 0.90×0.30 + 0.80×0.15
│ = 0.49 + 0.27 + 0.12
│ = 0.88
│ │
│ Agreement Boost: │
│ → 4 signals agree on CreatedBy! │
│ → Boost: +0.05 × (4-1) = +0.15
│ → Final: 0.91 + 0.15 = 1.06 → capped at 0.99
│ → 3 signals agree on CreatedBy! │
│ → Boost: +0.05 × (3-1) = +0.10
│ → Final: 0.88 + 0.10 = 0.98
│ │
│ Winner: CreatedBy (0.99 confidence) 🎯 │
│ Winner: CreatedBy (0.98 confidence) 🎯 │
└──────────────────────────────────────────────────────────┘
```
@ -897,8 +891,8 @@ const vector = await this.embed('Mona Lisa')
```
**Embedding Service**:
- Default: Uses `@xenova/transformers` (local, no API calls!)
- Model: `Xenova/all-MiniLM-L6-v2` (384 dimensions)
- Uses Candle WASM (local, no API calls, no downloads!)
- Model: `all-MiniLM-L6-v2` embedded in WASM (384 dimensions)
- Performance: ~5-15ms per embedding
**Output**:
@ -1868,10 +1862,9 @@ groupBy: 'type'
│ └─ ContextSignal (5%) │
│ │
│ SmartRelationshipExtractor (Verb Types): │
│ ├─ VerbExactMatchSignal (40%) │
│ ├─ VerbEmbeddingSignal (35%) │
│ ├─ VerbPatternSignal (20%) │
│ └─ VerbContextSignal (5%) │
│ ├─ VerbEmbeddingSignal (55%) │
│ ├─ VerbPatternSignal (30%) │
│ └─ VerbContextSignal (15%) │
│ │
│ Result: Intelligent entities + relationships │
└───────────────────────────────────────────────┘

View file

@ -1,358 +1,236 @@
# 🤖 Model Loading Guide
# Model Loading Guide
Brainy uses AI embedding models to understand and process your data. This guide explains how model loading works and how to handle different scenarios.
Brainy uses AI embedding models to understand and process your data. With the Candle WASM engine, the model is **embedded at compile time** - no downloads, no configuration, no external dependencies.
## 🚀 Zero Configuration (Default)
## Zero Configuration (Default)
**For most developers, no configuration is needed:**
**For all developers, no configuration is needed:**
```typescript
const brain = new Brainy()
await brain.init() // Models load automatically
await brain.init() // Model is already embedded - nothing to download!
```
**What happens automatically:**
1. Checks for local models in `./models/`
2. Downloads All-MiniLM-L6-v2 if needed (384 dimensions)
3. Configures optimal settings for your environment
4. Ready to use immediately
1. Candle WASM module loads (~90MB, includes model weights)
2. Model initializes in ~200ms
3. Ready to use immediately
## 📦 Model Loading Cascade
**No downloads. No CDN. No configuration. Just works.**
Brainy tries multiple sources in this order:
## How It Works
The all-MiniLM-L6-v2 model is embedded in the WASM binary using Rust's `include_bytes!` macro:
```
1. LOCAL CACHE (./models/)
↓ (if not found)
2. CDN DOWNLOAD (fast mirrors)
↓ (if fails)
3. GITHUB RELEASES (github.com/xenova/transformers.js)
↓ (if fails)
4. HUGGINGFACE HUB (huggingface.co)
↓ (if fails)
5. FALLBACK STRATEGIES (different model variants)
candle_embeddings_bg.wasm (~90MB)
├── Candle ML Runtime (~3MB)
├── Model Weights (safetensors format, ~87MB)
└── Tokenizer (HuggingFace tokenizers, ~450KB)
```
## 🌍 Environment-Specific Behavior
This single WASM file contains everything needed for sentence embeddings.
## Environments
### Bun (Recommended)
```typescript
// Works with Bun runtime
bun run server.ts
// Works with bun --compile (single binary deployment!)
bun build --compile --target=bun server.ts
./server // Self-contained binary with embedded model
```
### Node.js
```typescript
// Standard Node.js
node dist/server.js
// Runs identically to Bun
```
### Browser
```typescript
// Automatically configured for browsers
const brain = new Brainy() // Works in React, Vue, vanilla JS
await brain.init() // Downloads models via CDN
```
### Node.js Development
```typescript
// Zero config - downloads to ./models/
// Model loads via WASM (single file, no additional assets)
const brain = new Brainy()
await brain.init() // Downloads once, cached forever
```
### Production Server
```typescript
// Preload models during build/deployment
const brain = new Brainy()
await brain.init() // Uses cached local models
await brain.init()
```
### Docker/Kubernetes
```dockerfile
# Dockerfile - preload models
RUN npm run download-models
ENV BRAINY_ALLOW_REMOTE_MODELS=false
```
## 🛠️ Manual Model Management
### Pre-download Models
```bash
# Download models during build/deployment
npm run download-models
# Custom location
BRAINY_MODELS_PATH=./my-models npm run download-models
```
### Verify Models
```bash
# Check if models exist
ls ./models/Xenova/all-MiniLM-L6-v2/
# Should see:
# - config.json
# - tokenizer.json
# - onnx/model.onnx
```
### Custom Model Path
```typescript
const brain = new Brainy({
embedding: {
cacheDir: './custom-models'
}
})
```
## 🔒 Offline & Air-Gapped Environments
### Complete Offline Setup
```bash
# 1. Download models on connected machine
npm run download-models
# 2. Copy models to offline machine
cp -r ./models /path/to/offline/project/
# 3. Force local-only mode
export BRAINY_ALLOW_REMOTE_MODELS=false
```
### Container/Server Deployment
```dockerfile
FROM node:18
FROM oven/bun:1.1
WORKDIR /app
COPY package*.json ./
RUN npm ci
# Download models during build
RUN npm run download-models
# Force local-only in production
ENV BRAINY_ALLOW_REMOTE_MODELS=false
RUN bun install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
CMD ["bun", "run", "server.ts"]
# That's it! No model download step needed.
# Model is embedded in the npm package.
```
## ⚙️ Environment Variables
## Model Information
### BRAINY_ALLOW_REMOTE_MODELS
Controls whether remote model downloads are allowed:
### all-MiniLM-L6-v2 (Embedded)
- **Dimensions**: 384 (fixed)
- **Format**: Safetensors (FP32)
- **Size**: ~87MB (embedded in WASM)
- **Total WASM Size**: ~90MB
- **Language**: English-optimized, works with all languages
- **Inference**: ~2-10ms per embedding
- **Initialization**: ~200ms
```bash
# Allow remote downloads (default in most environments)
export BRAINY_ALLOW_REMOTE_MODELS=true
### Memory Usage
- **Loaded WASM**: ~90MB
- **Inference peak**: ~140MB total
- **Steady state**: ~100MB
# Force local-only (recommended for production)
export BRAINY_ALLOW_REMOTE_MODELS=false
```
## Comparing to Previous Architecture
### BRAINY_MODELS_PATH
Custom model storage location:
| Feature | Before (ONNX) | Now (Candle WASM) |
|---------|--------------|-------------------|
| Model downloads | Required on first use | None - embedded |
| External dependencies | onnxruntime-web | None |
| Model files | model.onnx, tokenizer.json | Embedded in WASM |
| Offline support | Required setup | Works by default |
| Bun compile | Broken | Works |
| Configuration | Environment variables | None needed |
```bash
# Custom model path
export BRAINY_MODELS_PATH=/opt/brainy/models
## Troubleshooting
# Relative path
export BRAINY_MODELS_PATH=./my-custom-models
```
### "Failed to initialize Candle Embedding Engine"
## 🚨 Troubleshooting
### "Failed to load embedding model" Error
**Cause**: Models not found locally and remote download blocked/failed.
**Cause**: WASM loading issue.
**Solutions**:
```bash
# Option 1: Allow remote downloads
export BRAINY_ALLOW_REMOTE_MODELS=true
# Rebuild the WASM
npm run build:candle
# Option 2: Download models manually
npm run download-models
# Option 3: Check internet connectivity
ping huggingface.co
# Option 4: Use custom model path
export BRAINY_MODELS_PATH=/path/to/existing/models
# Verify WASM exists
ls dist/embeddings/wasm/pkg/candle_embeddings_bg.wasm
# Should be ~90MB
```
### Models Download Very Slowly
### Out of Memory
**Cause**: Network issues or regional restrictions.
**Solutions**:
```bash
# Pre-download during build/CI
npm run download-models
# Use faster mirrors (automatic in newer versions)
# No action needed - Brainy tries multiple CDNs
```
### Container Out of Memory During Model Load
**Cause**: Limited container memory during model initialization.
**Cause**: Container/environment has less than 256MB RAM.
**Solutions**:
```dockerfile
# Increase memory limit
docker run -m 2g my-app
# Use quantized models (default)
ENV BRAINY_MODEL_DTYPE=q8
# Pre-load models at build time (recommended)
RUN npm run download-models
# Increase memory limit (recommended: 512MB+)
docker run -m 512m my-app
```
### Permission Denied Creating Model Cache
### Slow Initialization (>500ms)
**Cause**: Write permissions for model cache directory.
**Cause**: Cold start, large WASM parsing.
**Solutions**:
```bash
# Make directory writable
chmod 755 ./models
```typescript
// Initialize once at startup, not per-request
await brain.init() // Do this once
# Use custom writable path
export BRAINY_MODELS_PATH=/tmp/brainy-models
# Or use memory-only storage
const brain = new Brainy({
storage: { forceMemoryStorage: true }
// Then reuse for all requests
app.get('/api', async (req, res) => {
const results = await brain.find(req.query)
res.json(results)
})
```
## 🎯 Best Practices
## Migration from Previous Versions
### From v6.x (ONNX)
No changes needed for most users:
```typescript
// Same API - just upgrade
const brain = new Brainy()
await brain.init()
```
**What's removed:**
- `BRAINY_ALLOW_REMOTE_MODELS` - no downloads
- `BRAINY_MODELS_PATH` - no external model files
- `npm run download-models` - no longer needed
**What's new:**
- Faster initialization
- Works with `bun --compile`
- No network requirements
### From Custom Embedding Functions
If you provided a custom embedding function, it still works:
```typescript
const brain = new Brainy({
embeddingFunction: myCustomEmbedder // Still supported
})
```
## Advanced: Building Custom WASM
For contributors who want to modify the embedding engine:
```bash
# Navigate to Candle WASM source
cd src/embeddings/candle-wasm
# Build with wasm-pack
wasm-pack build --target web --release
# Copy to pkg folder
cp pkg/* ../wasm/pkg/
# Build TypeScript
npm run build
```
## Best Practices
### Development
```typescript
// ✅ Zero config - just works
// Just works - no setup
const brain = new Brainy()
await brain.init()
```
### Production
```dockerfile
# ✅ Pre-download models
RUN npm run download-models
# ✅ Force local-only
ENV BRAINY_ALLOW_REMOTE_MODELS=false
# ✅ Verify models exist
RUN test -f ./models/Xenova/all-MiniLM-L6-v2/onnx/model.onnx
```
### CI/CD Pipeline
```yaml
# .github/workflows/build.yml
- name: Download AI Models
run: npm run download-models
- name: Verify Models
run: |
test -f ./models/Xenova/all-MiniLM-L6-v2/onnx/model.onnx
echo "✅ Models verified"
- name: Test Offline Mode
env:
BRAINY_ALLOW_REMOTE_MODELS: false
run: npm test
```
### Lambda/Serverless
```typescript
// ✅ Models in deployment package
const brain = new Brainy({
embedding: {
localFilesOnly: true, // No downloads in lambda
cacheDir: './models' // Bundled with deployment
}
})
```
## 📊 Model Information
### All-MiniLM-L6-v2 (Default)
- **Dimensions**: 384 (fixed)
- **Size**: ~80MB compressed, ~330MB uncompressed
- **Language**: English (optimized)
- **Speed**: Very fast inference
- **Quality**: High quality for most use cases
### Model Files Structure
```
models/
└── Xenova/
└── all-MiniLM-L6-v2/
├── config.json # Model configuration
├── tokenizer.json # Text tokenizer
├── tokenizer_config.json
└── onnx/
├── model.onnx # Main model file
└── model_quantized.onnx # Optimized version
```
## 🔄 Migration from Other Embedding Solutions
### From OpenAI Embeddings
```typescript
// Before: OpenAI API calls
const response = await openai.embeddings.create({
model: "text-embedding-ada-002",
input: "Your text"
})
// After: Local Brainy embeddings
// Initialize once at startup
const brain = new Brainy()
await brain.init() // One-time setup
const id = await brain.add("Your text", { nounType: 'content' }) // Embedded automatically
```
### From Sentence Transformers
```python
# Before: Python sentence-transformers
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
# After: JavaScript Brainy (same model!)
const brain = new Brainy() // Uses same all-MiniLM-L6-v2
await brain.init()
// Singleton pattern recommended
export { brain }
```
## 🚀 Advanced Configuration
### Deployment
```bash
# Option 1: Bun compile (single binary)
bun build --compile server.ts
./server # Contains everything
### Custom Embedding Options
```typescript
const brain = new Brainy({
embedding: {
model: 'Xenova/all-MiniLM-L6-v2', // Default
dtype: 'q8', // Quantized for speed
device: 'cpu', // CPU inference
localFilesOnly: false, // Allow downloads
verbose: true // Debug logging
}
})
```
### Multiple Model Support (Advanced)
```typescript
// Use custom embedding function
import { createEmbeddingFunction } from 'brainy'
const customEmbedder = createEmbeddingFunction({
model: 'Xenova/all-MiniLM-L12-v2', // Larger model
dtype: 'fp32' // Higher precision
})
const brain = new Brainy({
embeddingFunction: customEmbedder
})
# Option 2: Docker
docker build -t my-app .
docker run -p 3000:3000 my-app
```
---
## 📚 Additional Resources
## Additional Resources
- [Zero Configuration Guide](./zero-config.md)
- [Enterprise Deployment](./enterprise-deployment.md)
- [Production Service Architecture](../PRODUCTION_SERVICE_ARCHITECTURE.md)
- [Zero Configuration Guide](../architecture/zero-config.md)
- [Troubleshooting Guide](../troubleshooting.md)
- [API Reference](../api/README.md)
**Need help?** Check our [troubleshooting guide](../troubleshooting.md) or [open an issue](https://github.com/your-repo/brainy/issues).
**Need help?** [Open an issue](https://github.com/soulcraftlabs/brainy/issues)

View file

@ -49,10 +49,9 @@ else:
System Memory: 2048 MB
OS Reserved (20%): -410 MB
Available: 1638 MB
Model Memory (Q8): -150 MB
├─ Weights: 22 MB
├─ ONNX Runtime: 30 MB
└─ Workspace: 98 MB
Model Memory: -140 MB
├─ WASM + Weights: 90 MB
└─ Workspace: 50 MB
───────────────────────────
Available for Cache: 1488 MB
Dev Allocation (25%): 372 MB UnifiedCache

View file

@ -4,47 +4,48 @@ Common issues and solutions for Brainy.
## 🤖 Model Loading Issues
### "Failed to load embedding model"
### "Failed to initialize Candle Embedding Engine"
**Symptoms**: Error during `brain.init()` with model loading failure.
**Symptoms**: Error during `brain.init()` with WASM loading failure.
**Causes & Solutions**:
1. **No local models + remote downloads blocked**
1. **WASM file missing**
```bash
# Solution: Download models manually
npm run download-models
# Verify WASM exists (~90MB with embedded model)
ls -lh dist/embeddings/wasm/pkg/candle_embeddings_bg.wasm
# Rebuild if missing
npm run build
```
2. **Network connectivity issues**
2. **Memory too low**
```bash
# Solution: Allow remote models
export BRAINY_ALLOW_REMOTE_MODELS=true
# Or pre-download in connected environment
npm run download-models
# Ensure at least 256MB available
# For Docker:
docker run -m 512m my-app
```
3. **Incorrect model path**
3. **Corrupted WASM**
```bash
# Check if models exist
ls ./models/Xenova/all-MiniLM-L6-v2/onnx/model.onnx
# Set correct path
export BRAINY_MODELS_PATH=/correct/path/to/models
# Rebuild the Candle WASM
npm run build:candle
npm run build
```
### Models Download Very Slowly
### Slow Initialization (>500ms)
**Symptoms**: Long wait times during first initialization.
**Symptoms**: Long wait times during first `brain.init()`.
**Cause**: WASM parsing takes ~200ms, which is normal for the 90MB file.
**Solutions**:
```bash
# Pre-download during build/CI
npm run download-models
```typescript
// Initialize once at startup, not per-request
await brain.init() // Do this once
# For Docker - download during image build
RUN npm run download-models
// Reuse for all requests
const results = await brain.find(query)
```
### Container Out of Memory During Model Load
@ -388,8 +389,8 @@ node -e "console.log(process.memoryUsage())"
# Platform info
node -e "console.log(process.platform, process.arch)"
# Brainy models
ls -la ./models/Xenova/all-MiniLM-L6-v2/
# Verify WASM file exists (model embedded inside)
ls -la dist/embeddings/wasm/pkg/candle_embeddings_bg.wasm
```
### Report Issues