mirror of
https://github.com/ruvnet/RuView
synced 2026-08-04 19:31:42 +00:00
feat: vendor midstream and sublinear-time-solver libraries
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { StreamingManager } = require('./streaming');
|
||||
|
||||
class SessionManager extends EventEmitter {
|
||||
constructor(config = {}) {
|
||||
super();
|
||||
|
||||
this.config = {
|
||||
maxSessions: config.maxSessions || 100,
|
||||
sessionTimeout: config.sessionTimeout || 3600000, // 1 hour
|
||||
cleanupInterval: config.cleanupInterval || 300000, // 5 minutes
|
||||
...config
|
||||
};
|
||||
|
||||
this.sessions = new Map();
|
||||
this.jobQueue = [];
|
||||
this.streaming = new StreamingManager(config);
|
||||
|
||||
// Start cleanup timer
|
||||
this.cleanupTimer = setInterval(() => {
|
||||
this.cleanupStaleSessions();
|
||||
}, this.config.cleanupInterval);
|
||||
}
|
||||
|
||||
async createSession(sessionId, sessionData) {
|
||||
if (this.sessions.size >= this.config.maxSessions) {
|
||||
throw new Error('Maximum number of sessions reached');
|
||||
}
|
||||
|
||||
const session = {
|
||||
id: sessionId,
|
||||
...sessionData,
|
||||
status: 'created',
|
||||
createdAt: new Date().toISOString(),
|
||||
lastActivity: new Date().toISOString(),
|
||||
metrics: {
|
||||
iterations: 0,
|
||||
residual: Infinity,
|
||||
memoryUsage: 0,
|
||||
convergenceRate: null
|
||||
},
|
||||
swarmNodes: new Set(),
|
||||
verificationHistory: [],
|
||||
costUpdates: []
|
||||
};
|
||||
|
||||
this.sessions.set(sessionId, session);
|
||||
this.emit('session_created', session);
|
||||
|
||||
console.log(`Session created: ${sessionId}`);
|
||||
return session;
|
||||
}
|
||||
|
||||
getSession(sessionId) {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (session) {
|
||||
session.lastActivity = new Date().toISOString();
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
updateSession(sessionId, updates) {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (session) {
|
||||
Object.assign(session, updates);
|
||||
session.lastActivity = new Date().toISOString();
|
||||
this.emit('session_updated', session);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
deleteSession(sessionId) {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (session) {
|
||||
this.sessions.delete(sessionId);
|
||||
this.streaming.stopStream(sessionId);
|
||||
this.emit('session_deleted', session);
|
||||
console.log(`Session deleted: ${sessionId}`);
|
||||
}
|
||||
}
|
||||
|
||||
async submitJob(jobData) {
|
||||
const jobId = uuidv4();
|
||||
const session = await this.createSession(jobId, {
|
||||
...jobData,
|
||||
type: 'job',
|
||||
status: 'queued'
|
||||
});
|
||||
|
||||
this.jobQueue.push(jobId);
|
||||
this.processJobQueue();
|
||||
|
||||
return jobId;
|
||||
}
|
||||
|
||||
async processJobQueue() {
|
||||
if (this.jobQueue.length === 0) return;
|
||||
|
||||
const jobId = this.jobQueue.shift();
|
||||
const session = this.getSession(jobId);
|
||||
|
||||
if (!session) return;
|
||||
|
||||
try {
|
||||
session.status = 'running';
|
||||
const stream = await this.streaming.startSolve(session);
|
||||
|
||||
// Store stream reference for later access
|
||||
session.stream = stream;
|
||||
|
||||
} catch (error) {
|
||||
session.status = 'error';
|
||||
session.error = error.message;
|
||||
console.error(`Job ${jobId} failed:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async getJobStatus(jobId) {
|
||||
const session = this.getSession(jobId);
|
||||
if (!session) return null;
|
||||
|
||||
const streamStatus = this.streaming.getStreamStatus(jobId);
|
||||
|
||||
return {
|
||||
job_id: jobId,
|
||||
status: session.status,
|
||||
created_at: session.createdAt,
|
||||
last_activity: session.lastActivity,
|
||||
metrics: session.metrics,
|
||||
stream: streamStatus,
|
||||
swarm_nodes: Array.from(session.swarmNodes || []),
|
||||
verification_count: session.verificationHistory?.length || 0
|
||||
};
|
||||
}
|
||||
|
||||
async getJobStream(jobId) {
|
||||
const session = this.getSession(jobId);
|
||||
if (!session) return null;
|
||||
|
||||
if (session.stream) {
|
||||
return session.stream;
|
||||
}
|
||||
|
||||
// If no active stream, try to start one
|
||||
if (session.status === 'created' || session.status === 'queued') {
|
||||
return await this.streaming.startSolve(session);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async verifySession(sessionId, options = {}) {
|
||||
const session = this.getSession(sessionId);
|
||||
if (!session) {
|
||||
throw new Error('Session not found');
|
||||
}
|
||||
|
||||
const { probeCount = 10, tolerance = 1e-8 } = options;
|
||||
|
||||
try {
|
||||
// Implement verification logic
|
||||
const verificationResult = await this.performVerification(session, {
|
||||
probeCount,
|
||||
tolerance
|
||||
});
|
||||
|
||||
// Store verification history
|
||||
session.verificationHistory = session.verificationHistory || [];
|
||||
session.verificationHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
...verificationResult
|
||||
});
|
||||
|
||||
// Keep only recent history
|
||||
if (session.verificationHistory.length > 100) {
|
||||
session.verificationHistory = session.verificationHistory.slice(-100);
|
||||
}
|
||||
|
||||
return verificationResult;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Verification failed for session ${sessionId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async performVerification(session, options) {
|
||||
// This would integrate with the actual solver verification
|
||||
// For now, return a mock result
|
||||
const errors = [];
|
||||
|
||||
for (let i = 0; i < options.probeCount; i++) {
|
||||
// Generate random probe error (mock)
|
||||
const error = Math.random() * 1e-8;
|
||||
errors.push(error);
|
||||
}
|
||||
|
||||
const maxError = Math.max(...errors);
|
||||
const meanError = errors.reduce((a, b) => a + b) / errors.length;
|
||||
|
||||
return {
|
||||
verified: maxError < options.tolerance,
|
||||
max_error: maxError,
|
||||
mean_error: meanError,
|
||||
probe_count: options.probeCount,
|
||||
tolerance: options.tolerance,
|
||||
verification_time_ms: Math.random() * 5 // Mock timing
|
||||
};
|
||||
}
|
||||
|
||||
async updateCosts(sessionId, costUpdate) {
|
||||
const session = this.getSession(sessionId);
|
||||
if (!session) {
|
||||
throw new Error('Session not found');
|
||||
}
|
||||
|
||||
// Store cost update
|
||||
session.costUpdates = session.costUpdates || [];
|
||||
session.costUpdates.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
...costUpdate
|
||||
});
|
||||
|
||||
// Apply cost update to solver (if running)
|
||||
if (session.stream && session.status === 'running') {
|
||||
// This would integrate with the actual solver cost update mechanism
|
||||
console.log(`Cost update applied to session ${sessionId}`);
|
||||
}
|
||||
|
||||
this.emit('cost_update', { sessionId, costUpdate });
|
||||
}
|
||||
|
||||
async joinSwarm(sessionId, nodeData) {
|
||||
const session = this.getSession(sessionId);
|
||||
if (!session) {
|
||||
throw new Error('Session not found');
|
||||
}
|
||||
|
||||
session.swarmNodes = session.swarmNodes || new Set();
|
||||
session.swarmNodes.add(nodeData.nodeId);
|
||||
|
||||
console.log(`Node ${nodeData.nodeId} joined swarm for session ${sessionId}`);
|
||||
this.emit('swarm_join', { sessionId, nodeData });
|
||||
}
|
||||
|
||||
cleanupStaleSession(sessionId) {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (!session) return;
|
||||
|
||||
const now = Date.now();
|
||||
const lastActivity = new Date(session.lastActivity).getTime();
|
||||
const age = now - lastActivity;
|
||||
|
||||
if (age > this.config.sessionTimeout) {
|
||||
console.log(`Cleaning up stale session: ${sessionId} (age: ${Math.round(age / 1000)}s)`);
|
||||
this.deleteSession(sessionId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
cleanupStaleSession() {
|
||||
const staleSessions = [];
|
||||
|
||||
for (const [sessionId, session] of this.sessions) {
|
||||
if (this.cleanupStaleSession(sessionId)) {
|
||||
staleSessions.push(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
if (staleSessions.length > 0) {
|
||||
console.log(`Cleaned up ${staleSessions.length} stale sessions`);
|
||||
}
|
||||
}
|
||||
|
||||
getStats() {
|
||||
const sessions = Array.from(this.sessions.values());
|
||||
|
||||
return {
|
||||
total_sessions: this.sessions.size,
|
||||
active_sessions: sessions.filter(s => s.status === 'running').length,
|
||||
queued_jobs: this.jobQueue.length,
|
||||
sessions_by_status: {
|
||||
created: sessions.filter(s => s.status === 'created').length,
|
||||
queued: sessions.filter(s => s.status === 'queued').length,
|
||||
running: sessions.filter(s => s.status === 'running').length,
|
||||
completed: sessions.filter(s => s.status === 'completed').length,
|
||||
error: sessions.filter(s => s.status === 'error').length
|
||||
},
|
||||
streaming_stats: this.streaming.getStats(),
|
||||
memory_usage: process.memoryUsage()
|
||||
};
|
||||
}
|
||||
|
||||
async shutdown() {
|
||||
console.log('Shutting down session manager...');
|
||||
|
||||
// Clear cleanup timer
|
||||
if (this.cleanupTimer) {
|
||||
clearInterval(this.cleanupTimer);
|
||||
}
|
||||
|
||||
// Stop all active streams
|
||||
for (const sessionId of this.sessions.keys()) {
|
||||
this.streaming.stopStream(sessionId);
|
||||
}
|
||||
|
||||
// Clear all sessions
|
||||
this.sessions.clear();
|
||||
this.jobQueue.length = 0;
|
||||
|
||||
this.emit('shutdown');
|
||||
console.log('Session manager shutdown complete');
|
||||
}
|
||||
}
|
||||
|
||||
// Session data structure
|
||||
class SolverSession {
|
||||
constructor(sessionId, config) {
|
||||
this.id = sessionId;
|
||||
this.matrix = config.matrix;
|
||||
this.vector = config.vector;
|
||||
this.method = config.method || 'adaptive';
|
||||
this.options = config.options || {};
|
||||
|
||||
this.status = 'created';
|
||||
this.createdAt = new Date().toISOString();
|
||||
this.lastActivity = new Date().toISOString();
|
||||
|
||||
this.metrics = {
|
||||
iterations: 0,
|
||||
residual: Infinity,
|
||||
convergenceRate: null,
|
||||
memoryUsage: 0
|
||||
};
|
||||
|
||||
this.swarmNodes = new Set();
|
||||
this.verificationHistory = [];
|
||||
this.costUpdates = [];
|
||||
|
||||
// Flow-Nexus specific data
|
||||
this.flowNexus = config.flowNexus || {};
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return {
|
||||
id: this.id,
|
||||
matrix: this.matrix,
|
||||
vector: this.vector,
|
||||
method: this.method,
|
||||
options: this.options,
|
||||
status: this.status,
|
||||
createdAt: this.createdAt,
|
||||
lastActivity: this.lastActivity,
|
||||
metrics: this.metrics,
|
||||
swarmNodes: Array.from(this.swarmNodes),
|
||||
verificationHistory: this.verificationHistory,
|
||||
costUpdates: this.costUpdates,
|
||||
flowNexus: this.flowNexus
|
||||
};
|
||||
}
|
||||
|
||||
static deserialize(data) {
|
||||
const session = new SolverSession(data.id, {
|
||||
matrix: data.matrix,
|
||||
vector: data.vector,
|
||||
method: data.method,
|
||||
options: data.options,
|
||||
flowNexus: data.flowNexus
|
||||
});
|
||||
|
||||
session.status = data.status;
|
||||
session.createdAt = data.createdAt;
|
||||
session.lastActivity = data.lastActivity;
|
||||
session.metrics = data.metrics;
|
||||
session.swarmNodes = new Set(data.swarmNodes || []);
|
||||
session.verificationHistory = data.verificationHistory || [];
|
||||
session.costUpdates = data.costUpdates || [];
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
updateMetrics(metrics) {
|
||||
Object.assign(this.metrics, metrics);
|
||||
this.lastActivity = new Date().toISOString();
|
||||
}
|
||||
|
||||
addSwarmNode(nodeId) {
|
||||
this.swarmNodes.add(nodeId);
|
||||
this.lastActivity = new Date().toISOString();
|
||||
}
|
||||
|
||||
removeSwarmNode(nodeId) {
|
||||
this.swarmNodes.delete(nodeId);
|
||||
this.lastActivity = new Date().toISOString();
|
||||
}
|
||||
|
||||
addVerificationResult(result) {
|
||||
this.verificationHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
...result
|
||||
});
|
||||
|
||||
// Keep only recent history
|
||||
if (this.verificationHistory.length > 100) {
|
||||
this.verificationHistory.shift();
|
||||
}
|
||||
|
||||
this.lastActivity = new Date().toISOString();
|
||||
}
|
||||
|
||||
addCostUpdate(update) {
|
||||
this.costUpdates.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
...update
|
||||
});
|
||||
|
||||
// Keep only recent updates
|
||||
if (this.costUpdates.length > 1000) {
|
||||
this.costUpdates = this.costUpdates.slice(-1000);
|
||||
}
|
||||
|
||||
this.lastActivity = new Date().toISOString();
|
||||
}
|
||||
|
||||
getAge() {
|
||||
return Date.now() - new Date(this.createdAt).getTime();
|
||||
}
|
||||
|
||||
getInactiveTime() {
|
||||
return Date.now() - new Date(this.lastActivity).getTime();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SessionManager,
|
||||
SolverSession
|
||||
};
|
||||
Reference in New Issue
Block a user