Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
321
vendor/ruvector/npm/packages/ruvllm/src/engine.js
vendored
Normal file
321
vendor/ruvector/npm/packages/ruvllm/src/engine.js
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
"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
|
||||
Reference in New Issue
Block a user