mirror of
https://github.com/ruvnet/RuView
synced 2026-06-09 10:13:17 +00:00
4b1005524e
- Add 154 missing vendor files (gitignore was filtering them) - vendor/midstream: 564 files (was 561) - vendor/sublinear-time-solver: 1190 files (was 1039) - Add ESP32 edge processing (ADR-039): presence, vitals, fall detection - Add WASM programmable sensing (ADR-040/041) with wasm3 runtime - Add firmware CI workflow (.github/workflows/firmware-ci.yml) - Add wifi-densepose-wasm-edge crate for edge WASM modules - Update sensing server, provision.py, UI components Co-Authored-By: claude-flow <ruv@ruv.net>
354 lines
13 KiB
JavaScript
354 lines
13 KiB
JavaScript
/**
|
|
* Persistent Learning System
|
|
* Enables cross-session learning and knowledge accumulation
|
|
*/
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
export class PersistentLearningSystem {
|
|
knowledgeBase = new Map();
|
|
sessionMemory = new Map();
|
|
currentSessionId;
|
|
learningRate = 0.1;
|
|
forgettingRate = 0.01;
|
|
storagePath;
|
|
constructor(storagePath = './data/learning') {
|
|
this.storagePath = storagePath;
|
|
this.currentSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
this.initializeSession();
|
|
}
|
|
/**
|
|
* Initialize new learning session
|
|
*/
|
|
async initializeSession() {
|
|
await this.loadPersistedKnowledge();
|
|
this.sessionMemory.set(this.currentSessionId, {
|
|
sessionId: this.currentSessionId,
|
|
startTime: Date.now(),
|
|
interactions: [],
|
|
discoveries: [],
|
|
performanceMetrics: {}
|
|
});
|
|
}
|
|
/**
|
|
* Learn from interaction results
|
|
*/
|
|
async learnFromInteraction(interaction) {
|
|
// Add to current session memory
|
|
const session = this.sessionMemory.get(this.currentSessionId);
|
|
if (session) {
|
|
session.interactions.push(interaction);
|
|
}
|
|
// Extract learning triples from successful interactions
|
|
if (interaction.success) {
|
|
const newTriples = this.extractLearningTriples(interaction);
|
|
for (const triple of newTriples) {
|
|
await this.addKnowledge(triple);
|
|
}
|
|
// Look for patterns across interactions
|
|
const patterns = this.detectPatterns(session?.interactions || []);
|
|
for (const pattern of patterns) {
|
|
await this.recordDiscovery({
|
|
timestamp: Date.now(),
|
|
type: 'pattern',
|
|
content: pattern,
|
|
novelty: this.calculateNovelty(pattern),
|
|
utility: this.calculateUtility(pattern)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Add knowledge triple with reinforcement learning
|
|
*/
|
|
async addKnowledge(triple) {
|
|
const key = `${triple.subject}:${triple.predicate}:${triple.object}`;
|
|
const existing = this.knowledgeBase.get(key);
|
|
if (existing) {
|
|
// Reinforce existing knowledge
|
|
existing.confidence = Math.min(1.0, existing.confidence + this.learningRate * (1 - existing.confidence));
|
|
existing.timestamp = Date.now();
|
|
existing.sources.push(triple.sessionId);
|
|
}
|
|
else {
|
|
// Add new knowledge
|
|
this.knowledgeBase.set(key, triple);
|
|
}
|
|
// Persist the update
|
|
await this.persistKnowledge();
|
|
}
|
|
/**
|
|
* Query learned knowledge with confidence scores
|
|
*/
|
|
queryKnowledge(subject, predicate, object) {
|
|
const results = [];
|
|
for (const [key, triple] of this.knowledgeBase) {
|
|
let matches = true;
|
|
if (subject && triple.subject !== subject)
|
|
matches = false;
|
|
if (predicate && triple.predicate !== predicate)
|
|
matches = false;
|
|
if (object && triple.object !== object)
|
|
matches = false;
|
|
if (matches) {
|
|
results.push(triple);
|
|
}
|
|
}
|
|
// Sort by confidence and recency
|
|
return results.sort((a, b) => (b.confidence * 0.7 + (b.timestamp / Date.now()) * 0.3) -
|
|
(a.confidence * 0.7 + (a.timestamp / Date.now()) * 0.3));
|
|
}
|
|
/**
|
|
* Learn from cross-session patterns
|
|
*/
|
|
async analyzeHistoricalPatterns() {
|
|
const allSessions = Array.from(this.sessionMemory.values());
|
|
const discoveries = [];
|
|
// Analyze success patterns across sessions
|
|
const successPatterns = this.findSuccessPatterns(allSessions);
|
|
discoveries.push(...successPatterns.map(pattern => ({
|
|
timestamp: Date.now(),
|
|
type: 'pattern',
|
|
content: pattern,
|
|
novelty: this.calculateNovelty(pattern),
|
|
utility: this.calculateUtility(pattern)
|
|
})));
|
|
// Find tool combination effectiveness
|
|
const toolEffectiveness = this.analyzeToolEffectiveness(allSessions);
|
|
discoveries.push({
|
|
timestamp: Date.now(),
|
|
type: 'optimization',
|
|
content: { toolRankings: toolEffectiveness },
|
|
novelty: 0.5,
|
|
utility: 0.8
|
|
});
|
|
// Store discoveries
|
|
for (const discovery of discoveries) {
|
|
await this.recordDiscovery(discovery);
|
|
}
|
|
return discoveries;
|
|
}
|
|
/**
|
|
* Get learning recommendations based on historical data
|
|
*/
|
|
getLearningRecommendations() {
|
|
const recommendations = [];
|
|
// Recommend exploring under-utilized tool combinations
|
|
const underutilized = this.findUnderutilizedCombinations();
|
|
recommendations.push({
|
|
type: 'exploration',
|
|
suggestion: 'Try under-utilized tool combinations',
|
|
combinations: underutilized,
|
|
priority: 0.7
|
|
});
|
|
// Recommend reinforcing successful patterns
|
|
const successfulPatterns = this.getSuccessfulPatterns();
|
|
recommendations.push({
|
|
type: 'reinforcement',
|
|
suggestion: 'Strengthen successful reasoning patterns',
|
|
patterns: successfulPatterns,
|
|
priority: 0.8
|
|
});
|
|
// Recommend areas needing improvement
|
|
const weakAreas = this.identifyWeakAreas();
|
|
recommendations.push({
|
|
type: 'improvement',
|
|
suggestion: 'Focus learning on weak performance areas',
|
|
areas: weakAreas,
|
|
priority: 0.9
|
|
});
|
|
return recommendations.sort((a, b) => b.priority - a.priority);
|
|
}
|
|
/**
|
|
* Apply forgetting to old, unused knowledge
|
|
*/
|
|
async applyForgetting() {
|
|
const now = Date.now();
|
|
const oneDay = 24 * 60 * 60 * 1000;
|
|
for (const [key, triple] of this.knowledgeBase) {
|
|
const age = now - triple.timestamp;
|
|
const ageDays = age / oneDay;
|
|
// Apply forgetting curve
|
|
const forgettingFactor = Math.exp(-this.forgettingRate * ageDays);
|
|
triple.confidence *= forgettingFactor;
|
|
// Remove very low confidence knowledge
|
|
if (triple.confidence < 0.01) {
|
|
this.knowledgeBase.delete(key);
|
|
}
|
|
}
|
|
await this.persistKnowledge();
|
|
}
|
|
/**
|
|
* Extract learning triples from interactions
|
|
*/
|
|
extractLearningTriples(interaction) {
|
|
const triples = [];
|
|
// Extract tool effectiveness patterns
|
|
if (interaction.success && interaction.tools.length > 0) {
|
|
triples.push({
|
|
subject: interaction.tools.join('+'),
|
|
predicate: 'effective_for',
|
|
object: interaction.type,
|
|
confidence: 0.5,
|
|
timestamp: Date.now(),
|
|
sessionId: this.currentSessionId,
|
|
sources: [this.currentSessionId]
|
|
});
|
|
}
|
|
// Extract input-output patterns
|
|
if (interaction.input && interaction.output) {
|
|
const inputPattern = this.extractPattern(interaction.input);
|
|
const outputPattern = this.extractPattern(interaction.output);
|
|
if (inputPattern && outputPattern) {
|
|
triples.push({
|
|
subject: inputPattern,
|
|
predicate: 'transforms_to',
|
|
object: outputPattern,
|
|
confidence: 0.6,
|
|
timestamp: Date.now(),
|
|
sessionId: this.currentSessionId,
|
|
sources: [this.currentSessionId]
|
|
});
|
|
}
|
|
}
|
|
return triples;
|
|
}
|
|
extractPattern(data) {
|
|
if (typeof data === 'string')
|
|
return data.substring(0, 50);
|
|
if (typeof data === 'object')
|
|
return JSON.stringify(data).substring(0, 50);
|
|
return null;
|
|
}
|
|
detectPatterns(interactions) {
|
|
const patterns = [];
|
|
// Find temporal patterns
|
|
const temporalPatterns = this.findTemporalPatterns(interactions);
|
|
patterns.push(...temporalPatterns);
|
|
// Find tool usage patterns
|
|
const toolPatterns = this.findToolPatterns(interactions);
|
|
patterns.push(...toolPatterns);
|
|
return patterns;
|
|
}
|
|
findTemporalPatterns(interactions) {
|
|
// Implementation for finding temporal patterns
|
|
return [];
|
|
}
|
|
findToolPatterns(interactions) {
|
|
// Implementation for finding tool usage patterns
|
|
return [];
|
|
}
|
|
findSuccessPatterns(sessions) {
|
|
// Implementation for finding success patterns across sessions
|
|
return [];
|
|
}
|
|
analyzeToolEffectiveness(sessions) {
|
|
// Implementation for analyzing tool effectiveness
|
|
return {};
|
|
}
|
|
findUnderutilizedCombinations() {
|
|
// Implementation for finding under-utilized combinations
|
|
return [];
|
|
}
|
|
getSuccessfulPatterns() {
|
|
// Implementation for getting successful patterns
|
|
return [];
|
|
}
|
|
identifyWeakAreas() {
|
|
// Implementation for identifying weak areas
|
|
return [];
|
|
}
|
|
calculateNovelty(pattern) {
|
|
// Calculate how novel this pattern is
|
|
return Math.random() * 0.5 + 0.5; // Placeholder
|
|
}
|
|
calculateUtility(pattern) {
|
|
// Calculate how useful this pattern is
|
|
return Math.random() * 0.5 + 0.5; // Placeholder
|
|
}
|
|
async recordDiscovery(discovery) {
|
|
const session = this.sessionMemory.get(this.currentSessionId);
|
|
if (session) {
|
|
session.discoveries.push(discovery);
|
|
}
|
|
}
|
|
/**
|
|
* Persist knowledge to disk
|
|
*/
|
|
async persistKnowledge() {
|
|
try {
|
|
await fs.mkdir(this.storagePath, { recursive: true });
|
|
const knowledgeArray = Array.from(this.knowledgeBase.values());
|
|
await fs.writeFile(path.join(this.storagePath, 'knowledge_base.json'), JSON.stringify(knowledgeArray, null, 2));
|
|
const sessionArray = Array.from(this.sessionMemory.values());
|
|
await fs.writeFile(path.join(this.storagePath, 'session_memory.json'), JSON.stringify(sessionArray, null, 2));
|
|
}
|
|
catch (error) {
|
|
console.error('Failed to persist knowledge:', error);
|
|
}
|
|
}
|
|
/**
|
|
* Load persisted knowledge from disk
|
|
*/
|
|
async loadPersistedKnowledge() {
|
|
try {
|
|
const knowledgePath = path.join(this.storagePath, 'knowledge_base.json');
|
|
const sessionPath = path.join(this.storagePath, 'session_memory.json');
|
|
// Load knowledge base
|
|
try {
|
|
const knowledgeData = await fs.readFile(knowledgePath, 'utf-8');
|
|
const knowledgeArray = JSON.parse(knowledgeData);
|
|
this.knowledgeBase.clear();
|
|
for (const triple of knowledgeArray) {
|
|
const key = `${triple.subject}:${triple.predicate}:${triple.object}`;
|
|
this.knowledgeBase.set(key, triple);
|
|
}
|
|
}
|
|
catch (error) {
|
|
// No existing knowledge base
|
|
}
|
|
// Load session memory
|
|
try {
|
|
const sessionData = await fs.readFile(sessionPath, 'utf-8');
|
|
const sessionArray = JSON.parse(sessionData);
|
|
this.sessionMemory.clear();
|
|
for (const session of sessionArray) {
|
|
this.sessionMemory.set(session.sessionId, session);
|
|
}
|
|
}
|
|
catch (error) {
|
|
// No existing session memory
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.error('Failed to load persisted knowledge:', error);
|
|
}
|
|
}
|
|
/**
|
|
* Get learning statistics
|
|
*/
|
|
getLearningStats() {
|
|
return {
|
|
totalTriples: this.knowledgeBase.size,
|
|
currentSession: this.currentSessionId,
|
|
totalSessions: this.sessionMemory.size,
|
|
avgConfidence: this.calculateAverageConfidence(),
|
|
lastUpdate: this.getLastUpdateTime(),
|
|
learningRate: this.learningRate,
|
|
forgettingRate: this.forgettingRate
|
|
};
|
|
}
|
|
calculateAverageConfidence() {
|
|
const triples = Array.from(this.knowledgeBase.values());
|
|
if (triples.length === 0)
|
|
return 0;
|
|
const sum = triples.reduce((acc, triple) => acc + triple.confidence, 0);
|
|
return sum / triples.length;
|
|
}
|
|
getLastUpdateTime() {
|
|
const triples = Array.from(this.knowledgeBase.values());
|
|
if (triples.length === 0)
|
|
return 0;
|
|
return Math.max(...triples.map(triple => triple.timestamp));
|
|
}
|
|
}
|