321 lines
9.4 KiB
JavaScript
321 lines
9.4 KiB
JavaScript
"use strict";
|
|
/**
|
|
* RuvLLM Engine - Main orchestrator for self-learning LLM
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.RuvLLM = void 0;
|
|
const native_1 = require("./native");
|
|
/**
|
|
* Convert JS config to native config format
|
|
*/
|
|
function toNativeConfig(config) {
|
|
if (!config)
|
|
return undefined;
|
|
return {
|
|
embedding_dim: config.embeddingDim,
|
|
router_hidden_dim: config.routerHiddenDim,
|
|
hnsw_m: config.hnswM,
|
|
hnsw_ef_construction: config.hnswEfConstruction,
|
|
hnsw_ef_search: config.hnswEfSearch,
|
|
learning_enabled: config.learningEnabled,
|
|
quality_threshold: config.qualityThreshold,
|
|
ewc_lambda: config.ewcLambda,
|
|
};
|
|
}
|
|
/**
|
|
* Convert JS generation config to native format
|
|
*/
|
|
function toNativeGenConfig(config) {
|
|
if (!config)
|
|
return undefined;
|
|
return {
|
|
max_tokens: config.maxTokens,
|
|
temperature: config.temperature,
|
|
top_p: config.topP,
|
|
top_k: config.topK,
|
|
repetition_penalty: config.repetitionPenalty,
|
|
};
|
|
}
|
|
/**
|
|
* RuvLLM - Self-learning LLM orchestrator
|
|
*
|
|
* Combines SONA adaptive learning with HNSW memory,
|
|
* FastGRNN routing, and SIMD-optimized inference.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { RuvLLM } from '@ruvector/ruvllm';
|
|
*
|
|
* const llm = new RuvLLM({ embeddingDim: 768 });
|
|
*
|
|
* // Query with automatic routing
|
|
* const response = await llm.query('What is machine learning?');
|
|
* console.log(response.text);
|
|
*
|
|
* // Provide feedback for learning
|
|
* llm.feedback({ requestId: response.requestId, rating: 5 });
|
|
* ```
|
|
*/
|
|
class RuvLLM {
|
|
/**
|
|
* Create a new RuvLLM instance
|
|
*/
|
|
constructor(config) {
|
|
this.native = null;
|
|
// Fallback state for when native module is not available
|
|
this.fallbackState = {
|
|
memory: new Map(),
|
|
nextId: 1,
|
|
queryCount: 0,
|
|
};
|
|
this.config = config ?? {};
|
|
const mod = (0, native_1.getNativeModule)();
|
|
if (mod) {
|
|
try {
|
|
this.native = new mod.RuvLLMEngine(toNativeConfig(config));
|
|
}
|
|
catch {
|
|
// Silently fall back to JS implementation
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Query the LLM with automatic routing
|
|
*/
|
|
query(text, config) {
|
|
if (this.native) {
|
|
const result = this.native.query(text, toNativeGenConfig(config));
|
|
return {
|
|
text: result.text,
|
|
confidence: result.confidence,
|
|
model: result.model,
|
|
contextSize: result.context_size,
|
|
latencyMs: result.latency_ms,
|
|
requestId: result.request_id,
|
|
};
|
|
}
|
|
// Fallback implementation
|
|
this.fallbackState.queryCount++;
|
|
return {
|
|
text: `[Fallback] Response to: ${text.slice(0, 50)}...`,
|
|
confidence: 0.5,
|
|
model: 'fallback',
|
|
contextSize: 512,
|
|
latencyMs: 1.0,
|
|
requestId: `fb-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
};
|
|
}
|
|
/**
|
|
* Generate text with SIMD-optimized inference
|
|
*
|
|
* Note: If no trained model is loaded (demo mode), returns an informational
|
|
* message instead of garbled output.
|
|
*/
|
|
generate(prompt, config) {
|
|
if (this.native) {
|
|
return this.native.generate(prompt, toNativeGenConfig(config));
|
|
}
|
|
// Fallback - provide helpful message instead of garbled output
|
|
const maxTokens = config?.maxTokens ?? 256;
|
|
const temp = config?.temperature ?? 0.7;
|
|
const topP = config?.topP ?? 0.9;
|
|
return `[RuvLLM JavaScript Fallback Mode]
|
|
No native SIMD module loaded. Running in JavaScript fallback mode.
|
|
|
|
Your prompt: "${prompt.slice(0, 100)}${prompt.length > 100 ? '...' : ''}"
|
|
|
|
To enable native SIMD inference:
|
|
1. Install the native bindings: npm install @ruvector/ruvllm-${process.platform}-${process.arch}
|
|
2. Or load a GGUF model file
|
|
3. Or connect to an external LLM API
|
|
|
|
Config: temp=${temp.toFixed(2)}, top_p=${topP.toFixed(2)}, max_tokens=${maxTokens}
|
|
|
|
This fallback provides routing, memory, and embedding features but not full text generation.`;
|
|
}
|
|
/**
|
|
* Get routing decision for a query
|
|
*/
|
|
route(text) {
|
|
if (this.native) {
|
|
const result = this.native.route(text);
|
|
return {
|
|
model: result.model,
|
|
contextSize: result.context_size,
|
|
temperature: result.temperature,
|
|
topP: result.top_p,
|
|
confidence: result.confidence,
|
|
};
|
|
}
|
|
// Fallback
|
|
return {
|
|
model: 'M700',
|
|
contextSize: 512,
|
|
temperature: 0.7,
|
|
topP: 0.9,
|
|
confidence: 0.5,
|
|
};
|
|
}
|
|
/**
|
|
* Search memory for similar content
|
|
*/
|
|
searchMemory(text, k = 10) {
|
|
if (this.native) {
|
|
const results = this.native.searchMemory(text, k);
|
|
return results.map(r => ({
|
|
id: r.id,
|
|
score: r.score,
|
|
content: r.content,
|
|
metadata: JSON.parse(r.metadata || '{}'),
|
|
}));
|
|
}
|
|
// Fallback - simple search
|
|
return Array.from(this.fallbackState.memory.entries())
|
|
.slice(0, k)
|
|
.map(([id, data]) => ({
|
|
id,
|
|
score: 0.5,
|
|
content: data.content,
|
|
metadata: data.metadata,
|
|
}));
|
|
}
|
|
/**
|
|
* Add content to memory
|
|
*/
|
|
addMemory(content, metadata) {
|
|
if (this.native) {
|
|
return this.native.addMemory(content, metadata ? JSON.stringify(metadata) : undefined);
|
|
}
|
|
// Fallback
|
|
const id = this.fallbackState.nextId++;
|
|
this.fallbackState.memory.set(id, {
|
|
content,
|
|
embedding: this.embed(content),
|
|
metadata: metadata ?? {},
|
|
});
|
|
return id;
|
|
}
|
|
/**
|
|
* Provide feedback for learning
|
|
*/
|
|
feedback(fb) {
|
|
if (this.native) {
|
|
return this.native.feedback(fb.requestId, fb.rating, fb.correction);
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Get engine statistics
|
|
*/
|
|
stats() {
|
|
if (this.native) {
|
|
const s = this.native.stats();
|
|
// Map native stats (snake_case) to TypeScript interface (camelCase)
|
|
// Handle both old and new field names for backward compatibility
|
|
return {
|
|
totalQueries: s.total_queries ?? 0,
|
|
memoryNodes: s.memory_nodes ?? 0,
|
|
patternsLearned: s.patterns_learned ?? s.training_steps ?? 0,
|
|
avgLatencyMs: s.avg_latency_ms ?? 0,
|
|
cacheHitRate: s.cache_hit_rate ?? 0,
|
|
routerAccuracy: s.router_accuracy ?? 0.5,
|
|
};
|
|
}
|
|
// Fallback
|
|
return {
|
|
totalQueries: this.fallbackState.queryCount,
|
|
memoryNodes: this.fallbackState.memory.size,
|
|
patternsLearned: 0,
|
|
avgLatencyMs: 1.0,
|
|
cacheHitRate: 0.0,
|
|
routerAccuracy: 0.5,
|
|
};
|
|
}
|
|
/**
|
|
* Force router learning cycle
|
|
*/
|
|
forceLearn() {
|
|
if (this.native) {
|
|
return this.native.forceLearn();
|
|
}
|
|
return 'Learning not available in fallback mode';
|
|
}
|
|
/**
|
|
* Get embedding for text
|
|
*/
|
|
embed(text) {
|
|
if (this.native) {
|
|
return this.native.embed(text);
|
|
}
|
|
// Fallback - simple hash-based embedding
|
|
const dim = this.config.embeddingDim ?? 768;
|
|
const embedding = new Array(dim).fill(0);
|
|
for (let i = 0; i < text.length; i++) {
|
|
const idx = (text.charCodeAt(i) * (i + 1)) % dim;
|
|
embedding[idx] += 0.1;
|
|
}
|
|
// Normalize
|
|
const norm = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0)) || 1;
|
|
return embedding.map(x => x / norm);
|
|
}
|
|
/**
|
|
* Compute similarity between two texts
|
|
*/
|
|
similarity(text1, text2) {
|
|
if (this.native) {
|
|
return this.native.similarity(text1, text2);
|
|
}
|
|
// Fallback - cosine similarity
|
|
const emb1 = this.embed(text1);
|
|
const emb2 = this.embed(text2);
|
|
let dot = 0;
|
|
let norm1 = 0;
|
|
let norm2 = 0;
|
|
for (let i = 0; i < emb1.length; i++) {
|
|
dot += emb1[i] * emb2[i];
|
|
norm1 += emb1[i] * emb1[i];
|
|
norm2 += emb2[i] * emb2[i];
|
|
}
|
|
const denom = Math.sqrt(norm1) * Math.sqrt(norm2);
|
|
const similarity = denom > 0 ? dot / denom : 0;
|
|
// Clamp to [0, 1] to handle floating point errors
|
|
return Math.max(0, Math.min(1, similarity));
|
|
}
|
|
/**
|
|
* Check if SIMD is available
|
|
*/
|
|
hasSimd() {
|
|
if (this.native) {
|
|
return this.native.hasSimd();
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Get SIMD capabilities
|
|
*/
|
|
simdCapabilities() {
|
|
if (this.native) {
|
|
return this.native.simdCapabilities();
|
|
}
|
|
return ['Scalar (fallback)'];
|
|
}
|
|
/**
|
|
* Batch query multiple prompts
|
|
*/
|
|
batchQuery(request) {
|
|
const start = Date.now();
|
|
const responses = request.queries.map(q => this.query(q, request.config));
|
|
return {
|
|
responses,
|
|
totalLatencyMs: Date.now() - start,
|
|
};
|
|
}
|
|
/**
|
|
* Check if native module is loaded
|
|
*/
|
|
isNativeLoaded() {
|
|
return this.native !== null;
|
|
}
|
|
}
|
|
exports.RuvLLM = RuvLLM;
|
|
//# sourceMappingURL=engine.js.map
|