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:
ruv
2026-03-02 23:32:45 -05:00
parent 14902e6b4e
commit e91bb8a1d5
1600 changed files with 1852646 additions and 0 deletions
+629
View File
@@ -0,0 +1,629 @@
const express = require('express');
const cors = require('cors');
const compression = require('compression');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const { WebSocketServer } = require('ws');
const { EventEmitter } = require('events');
const { v4: uuidv4 } = require('uuid');
const { StreamingManager } = require('./streaming');
const { SessionManager } = require('./session-manager');
const { FlowNexusIntegration } = require('../integrations/flow-nexus');
class SolverServer extends EventEmitter {
constructor(options = {}) {
super();
this.config = {
port: options.port || 3000,
cors: options.cors || false,
workers: options.workers || 1,
maxSessions: options.maxSessions || 100,
authToken: options.authToken,
flowNexusEnabled: options.flowNexusEnabled || false,
...options
};
this.app = express();
this.server = null;
this.wss = null;
this.sessions = new SessionManager(this.config);
this.streaming = new StreamingManager(this.config);
this.flowNexus = this.config.flowNexusEnabled ? new FlowNexusIntegration() : null;
this.setupMiddleware();
this.setupRoutes();
this.setupWebSocket();
}
setupMiddleware() {
// Security middleware
this.app.use(helmet({
contentSecurityPolicy: false, // Allow WebSocket connections
crossOriginEmbedderPolicy: false
}));
// CORS
if (this.config.cors) {
this.app.use(cors({
origin: true,
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Session-ID']
}));
}
// Compression
this.app.use(compression());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000, // Limit each IP to 1000 requests per windowMs
message: {
error: 'Too many requests',
retryAfter: '15 minutes'
}
});
this.app.use('/api', limiter);
// Body parsing
this.app.use(express.json({ limit: '50mb' }));
this.app.use(express.urlencoded({ extended: true, limit: '50mb' }));
// Request logging
this.app.use((req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.path}`);
next();
});
// Authentication middleware
this.app.use('/api/protected', this.authenticateToken.bind(this));
}
setupRoutes() {
// Health check
this.app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
sessions: this.sessions.getStats()
});
});
// API routes
this.app.use('/api/v1', this.createAPIRoutes());
// Static files for documentation
this.app.use('/docs', express.static('docs'));
// Root endpoint
this.app.get('/', (req, res) => {
res.json({
name: 'Sublinear Time Solver API',
version: '1.0.0',
endpoints: {
health: '/health',
api: '/api/v1',
docs: '/docs',
websocket: '/ws'
}
});
});
}
createAPIRoutes() {
const router = express.Router();
// Start streaming solve session
router.post('/solve-stream', async (req, res) => {
try {
const {
matrix,
vector,
method = 'adaptive',
options = {},
flow_nexus = {}
} = req.body;
if (!matrix || !vector) {
return res.status(400).json({
error: 'Matrix and vector are required',
code: 'MISSING_INPUT'
});
}
const sessionId = uuidv4();
const session = await this.sessions.createSession(sessionId, {
matrix,
vector,
method,
options,
flowNexus: flow_nexus
});
res.writeHead(200, {
'Content-Type': 'application/x-ndjson',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Session-ID': sessionId
});
// Start streaming solve
const stream = await this.streaming.startSolve(session);
for await (const update of stream) {
const data = JSON.stringify({
type: 'iteration_update',
session_id: sessionId,
timestamp: new Date().toISOString(),
...update
}) + '\n';
if (!res.write(data)) {
// Backpressure handling
await new Promise(resolve => res.once('drain', resolve));
}
if (update.converged || update.error) {
break;
}
}
res.end();
} catch (error) {
console.error('Solve stream error:', error);
res.status(500).json({
error: error.message,
code: 'SOLVE_ERROR'
});
}
});
// Submit solve job
router.post('/solve', async (req, res) => {
try {
const { matrix, vector, method, options } = req.body;
if (!matrix || !vector) {
return res.status(400).json({
error: 'Matrix and vector are required'
});
}
const jobId = await this.sessions.submitJob({
matrix,
vector,
method,
options
});
res.json({
job_id: jobId,
status: 'submitted',
endpoints: {
status: `/api/v1/jobs/${jobId}`,
stream: `/api/v1/jobs/${jobId}/stream`
}
});
} catch (error) {
console.error('Submit job error:', error);
res.status(500).json({
error: error.message,
code: 'SUBMIT_ERROR'
});
}
});
// Get job status
router.get('/jobs/:jobId', async (req, res) => {
try {
const status = await this.sessions.getJobStatus(req.params.jobId);
if (!status) {
return res.status(404).json({
error: 'Job not found',
code: 'JOB_NOT_FOUND'
});
}
res.json(status);
} catch (error) {
console.error('Get job status error:', error);
res.status(500).json({
error: error.message,
code: 'STATUS_ERROR'
});
}
});
// Stream job updates
router.get('/jobs/:jobId/stream', async (req, res) => {
try {
const stream = await this.sessions.getJobStream(req.params.jobId);
if (!stream) {
return res.status(404).json({
error: 'Job not found or not streaming'
});
}
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
for await (const update of stream) {
res.write(`data: ${JSON.stringify(update)}\n\n`);
}
res.end();
} catch (error) {
console.error('Job stream error:', error);
res.status(500).json({
error: error.message,
code: 'STREAM_ERROR'
});
}
});
// Verification endpoint
router.post('/verify', async (req, res) => {
try {
const {
session_id,
probe_count = 10,
tolerance = 1e-8
} = req.body;
if (!session_id) {
return res.status(400).json({
error: 'Session ID is required'
});
}
const result = await this.sessions.verifySession(session_id, {
probeCount: probe_count,
tolerance
});
res.json({
type: 'verification_result',
session_id,
timestamp: new Date().toISOString(),
...result
});
} catch (error) {
console.error('Verification error:', error);
res.status(500).json({
error: error.message,
code: 'VERIFICATION_ERROR'
});
}
});
// Session status
router.get('/sessions/:sessionId', async (req, res) => {
try {
const session = await this.sessions.getSession(req.params.sessionId);
if (!session) {
return res.status(404).json({
error: 'Session not found'
});
}
res.json({
session_id: req.params.sessionId,
status: session.status,
created_at: session.createdAt,
last_activity: session.lastActivity,
metrics: session.metrics
});
} catch (error) {
console.error('Session status error:', error);
res.status(500).json({
error: error.message,
code: 'SESSION_ERROR'
});
}
});
// Cost update endpoint for swarm coordination
router.post('/swarm/costs', async (req, res) => {
try {
const {
session_id,
delta_costs,
matrix_updates,
source_node
} = req.body;
if (!session_id || !delta_costs) {
return res.status(400).json({
error: 'Session ID and delta costs are required'
});
}
await this.sessions.updateCosts(session_id, {
deltaCosts: delta_costs,
matrixUpdates: matrix_updates,
sourceNode: source_node
});
res.json({
status: 'updated',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Cost update error:', error);
res.status(500).json({
error: error.message,
code: 'COST_UPDATE_ERROR'
});
}
});
// Swarm join endpoint
router.post('/swarm/join', async (req, res) => {
try {
const {
session_id,
node_id,
capabilities = []
} = req.body;
if (!session_id || !node_id) {
return res.status(400).json({
error: 'Session ID and node ID are required'
});
}
await this.sessions.joinSwarm(session_id, {
nodeId: node_id,
capabilities
});
res.json({
status: 'joined',
node_id,
session_id
});
} catch (error) {
console.error('Swarm join error:', error);
res.status(500).json({
error: error.message,
code: 'SWARM_JOIN_ERROR'
});
}
});
// Flow-Nexus integration endpoints
if (this.flowNexus) {
router.post('/flow-nexus/register', async (req, res) => {
try {
const result = await this.flowNexus.registerSolver({
endpoint: `http://localhost:${this.config.port}`,
capabilities: ['streaming', 'verification', 'swarm']
});
res.json(result);
} catch (error) {
console.error('Flow-Nexus registration error:', error);
res.status(500).json({
error: error.message,
code: 'FLOW_NEXUS_ERROR'
});
}
});
router.get('/flow-nexus/status', async (req, res) => {
try {
const status = await this.flowNexus.getStatus();
res.json(status);
} catch (error) {
console.error('Flow-Nexus status error:', error);
res.status(500).json({
error: error.message,
code: 'FLOW_NEXUS_ERROR'
});
}
});
}
return router;
}
setupWebSocket() {
// WebSocket setup will be done when server starts
}
authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token && this.config.authToken) {
return res.status(401).json({
error: 'Authentication token required',
code: 'AUTH_REQUIRED'
});
}
if (this.config.authToken && token !== this.config.authToken) {
return res.status(403).json({
error: 'Invalid authentication token',
code: 'AUTH_INVALID'
});
}
next();
}
async start() {
return new Promise((resolve, reject) => {
this.server = this.app.listen(this.config.port, (error) => {
if (error) {
reject(error);
return;
}
// Setup WebSocket server
this.wss = new WebSocketServer({
server: this.server,
path: '/ws'
});
this.setupWebSocketHandlers();
console.log(`🚀 Solver server started on port ${this.config.port}`);
this.emit('started');
resolve();
});
});
}
setupWebSocketHandlers() {
this.wss.on('connection', (ws, req) => {
console.log('WebSocket connection established');
ws.on('message', async (data) => {
try {
const message = JSON.parse(data.toString());
await this.handleWebSocketMessage(ws, message);
} catch (error) {
console.error('WebSocket message error:', error);
ws.send(JSON.stringify({
type: 'error',
error: error.message
}));
}
});
ws.on('close', () => {
console.log('WebSocket connection closed');
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
// Send welcome message
ws.send(JSON.stringify({
type: 'welcome',
timestamp: new Date().toISOString()
}));
});
}
async handleWebSocketMessage(ws, message) {
switch (message.type) {
case 'subscribe':
if (message.session_id) {
await this.subscribeToSession(ws, message.session_id);
}
break;
case 'solve':
const sessionId = await this.startWebSocketSolve(ws, message);
ws.send(JSON.stringify({
type: 'solve_started',
session_id: sessionId
}));
break;
case 'ping':
ws.send(JSON.stringify({
type: 'pong',
timestamp: new Date().toISOString()
}));
break;
default:
ws.send(JSON.stringify({
type: 'error',
error: `Unknown message type: ${message.type}`
}));
}
}
async subscribeToSession(ws, sessionId) {
const stream = await this.sessions.getJobStream(sessionId);
if (!stream) {
ws.send(JSON.stringify({
type: 'error',
error: 'Session not found or not streaming'
}));
return;
}
for await (const update of stream) {
if (ws.readyState === ws.OPEN) {
ws.send(JSON.stringify({
type: 'session_update',
session_id: sessionId,
...update
}));
} else {
break;
}
}
}
async startWebSocketSolve(ws, message) {
const sessionId = uuidv4();
const session = await this.sessions.createSession(sessionId, {
matrix: message.matrix,
vector: message.vector,
method: message.method || 'adaptive',
options: message.options || {}
});
// Start streaming to WebSocket
this.subscribeToSession(ws, sessionId);
return sessionId;
}
async stop() {
return new Promise((resolve) => {
if (this.wss) {
this.wss.close();
}
if (this.server) {
this.server.close(() => {
console.log('Server stopped');
this.emit('stopped');
resolve();
});
} else {
resolve();
}
});
}
getStats() {
return {
sessions: this.sessions.getStats(),
server: {
uptime: process.uptime(),
memory: process.memoryUsage(),
connections: this.wss ? this.wss.clients.size : 0
}
};
}
}
module.exports = { SolverServer };
+440
View File
@@ -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
};
+186
View File
@@ -0,0 +1,186 @@
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (!isMainThread) {
// Worker thread code
const { createSolver } = require('../src/solver');
let currentSolver = null;
let currentSession = null;
let solving = false;
parentPort.on('message', async (message) => {
try {
switch (message.type) {
case 'solve':
await handleSolve(message);
break;
case 'stop':
await handleStop(message);
break;
case 'status':
handleStatus(message);
break;
default:
parentPort.postMessage({
type: 'error',
sessionId: message.sessionId,
error: `Unknown message type: ${message.type}`
});
}
} catch (error) {
parentPort.postMessage({
type: 'error',
sessionId: message.sessionId,
error: error.message,
stack: error.stack
});
}
});
async function handleSolve(message) {
if (solving) {
throw new Error('Worker is already solving a problem');
}
solving = true;
currentSession = message.sessionId;
try {
// Create solver with provided configuration
currentSolver = await createSolver({
matrix: message.matrix,
method: message.method || 'adaptive',
tolerance: message.options?.tolerance || 1e-10,
maxIterations: message.options?.maxIterations || 1000,
enableVerification: message.options?.enableVerification || false
});
// Start streaming solve
const startTime = Date.now();
let lastMemoryCheck = startTime;
for await (const update of currentSolver.streamSolve(message.vector)) {
// Check if we should stop
if (!solving || currentSession !== message.sessionId) {
break;
}
// Memory usage tracking
const now = Date.now();
let memoryUsage = 0;
if (now - lastMemoryCheck > 5000) { // Check every 5 seconds
const memInfo = process.memoryUsage();
memoryUsage = Math.round(memInfo.heapUsed / 1024 / 1024); // MB
lastMemoryCheck = now;
}
// Send iteration update
parentPort.postMessage({
type: 'iteration',
sessionId: message.sessionId,
iteration: update.iteration,
residual: update.residual,
convergenceRate: update.convergenceRate,
memoryUsage,
verified: update.verified || false,
timestamp: new Date().toISOString()
});
// Check for convergence
if (update.converged) {
parentPort.postMessage({
type: 'converged',
sessionId: message.sessionId,
solution: update.solution,
stats: {
iterations: update.iteration,
residual: update.residual,
solveTime: now - startTime,
memoryUsage,
converged: true
}
});
break;
}
}
} catch (error) {
parentPort.postMessage({
type: 'error',
sessionId: message.sessionId,
error: error.message
});
} finally {
solving = false;
currentSolver = null;
currentSession = null;
}
}
async function handleStop(message) {
if (currentSession === message.sessionId) {
solving = false;
if (currentSolver && typeof currentSolver.stop === 'function') {
await currentSolver.stop();
}
parentPort.postMessage({
type: 'stopped',
sessionId: message.sessionId
});
}
}
function handleStatus(message) {
parentPort.postMessage({
type: 'status',
sessionId: message.sessionId,
solving,
currentSession,
memory: process.memoryUsage(),
uptime: process.uptime()
});
}
// Handle worker errors
process.on('uncaughtException', (error) => {
parentPort.postMessage({
type: 'error',
sessionId: currentSession,
error: error.message,
fatal: true
});
process.exit(1);
});
process.on('unhandledRejection', (error) => {
parentPort.postMessage({
type: 'error',
sessionId: currentSession,
error: error.message,
fatal: true
});
process.exit(1);
});
// Send ready signal
parentPort.postMessage({
type: 'ready',
workerId: process.pid
});
} else {
// Main thread code - export worker creation utility
const path = require('path');
function createSolverWorker() {
return new Worker(__filename);
}
module.exports = { createSolverWorker };
}
+520
View File
@@ -0,0 +1,520 @@
const { EventEmitter } = require('events');
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
const { v4: uuidv4 } = require('uuid');
const path = require('path');
class StreamingManager extends EventEmitter {
constructor(config = {}) {
super();
this.config = {
maxConcurrentStreams: config.maxConcurrentStreams || 100,
workerPoolSize: config.workers || 1,
streamTimeout: config.streamTimeout || 300000, // 5 minutes
heartbeatInterval: config.heartbeatInterval || 15000, // 15 seconds
...config
};
this.activeStreams = new Map();
this.workerPool = [];
this.availableWorkers = [];
this.jobQueue = [];
this.initializeWorkerPool();
}
initializeWorkerPool() {
for (let i = 0; i < this.config.workerPoolSize; i++) {
this.createWorker();
}
}
createWorker() {
const worker = new Worker(path.join(__dirname, 'solver-worker.js'));
worker.on('message', (message) => {
this.handleWorkerMessage(worker, message);
});
worker.on('error', (error) => {
console.error('Worker error:', error);
this.replaceWorker(worker);
});
worker.on('exit', (code) => {
if (code !== 0) {
console.error(`Worker stopped with exit code ${code}`);
this.replaceWorker(worker);
}
});
worker.id = uuidv4();
this.workerPool.push(worker);
this.availableWorkers.push(worker);
return worker;
}
replaceWorker(deadWorker) {
// Remove dead worker
this.workerPool = this.workerPool.filter(w => w.id !== deadWorker.id);
this.availableWorkers = this.availableWorkers.filter(w => w.id !== deadWorker.id);
// Create replacement
this.createWorker();
}
async startSolve(session) {
if (this.activeStreams.size >= this.config.maxConcurrentStreams) {
throw new Error('Maximum concurrent streams reached');
}
const stream = new SolverStream(session, this);
this.activeStreams.set(session.id, stream);
// Set up cleanup timeout
setTimeout(() => {
if (this.activeStreams.has(session.id)) {
this.activeStreams.delete(session.id);
stream.destroy();
}
}, this.config.streamTimeout);
return stream.start();
}
getWorker() {
if (this.availableWorkers.length === 0) {
return null;
}
return this.availableWorkers.shift();
}
releaseWorker(worker) {
if (this.workerPool.includes(worker)) {
this.availableWorkers.push(worker);
}
}
handleWorkerMessage(worker, message) {
const stream = this.activeStreams.get(message.sessionId);
if (stream) {
stream.handleWorkerMessage(message);
}
}
stopStream(sessionId) {
const stream = this.activeStreams.get(sessionId);
if (stream) {
stream.stop();
this.activeStreams.delete(sessionId);
}
}
getStreamStatus(sessionId) {
const stream = this.activeStreams.get(sessionId);
return stream ? stream.getStatus() : null;
}
getStats() {
return {
activeStreams: this.activeStreams.size,
availableWorkers: this.availableWorkers.length,
totalWorkers: this.workerPool.length,
queuedJobs: this.jobQueue.length
};
}
}
class SolverStream extends EventEmitter {
constructor(session, manager) {
super();
this.session = session;
this.manager = manager;
this.worker = null;
this.status = 'pending';
this.startTime = Date.now();
this.lastUpdate = Date.now();
this.updates = [];
this.currentIteration = 0;
this.currentResidual = Infinity;
this.converged = false;
this.error = null;
// Heartbeat to detect stalled streams
this.heartbeatTimer = setInterval(() => {
this.checkHeartbeat();
}, manager.config.heartbeatInterval);
}
async start() {
try {
this.status = 'starting';
this.worker = this.manager.getWorker();
if (!this.worker) {
throw new Error('No available workers');
}
// Send solve job to worker
this.worker.postMessage({
type: 'solve',
sessionId: this.session.id,
matrix: this.session.matrix,
vector: this.session.vector,
method: this.session.method,
options: this.session.options
});
this.status = 'running';
return this.createAsyncIterator();
} catch (error) {
this.error = error;
this.status = 'error';
throw error;
}
}
async *createAsyncIterator() {
let done = false;
while (!done) {
// Wait for next update
const update = await this.waitForUpdate();
if (update.type === 'iteration') {
this.currentIteration = update.iteration;
this.currentResidual = update.residual;
this.lastUpdate = Date.now();
yield {
iteration: update.iteration,
residual: update.residual,
convergence_rate: update.convergenceRate,
memory_usage: update.memoryUsage,
estimated_time_remaining: this.estimateTimeRemaining(update),
verified: update.verified || false,
timestamp: new Date().toISOString()
};
} else if (update.type === 'converged') {
this.converged = true;
this.status = 'completed';
done = true;
yield {
iteration: this.currentIteration,
residual: this.currentResidual,
converged: true,
solution: update.solution,
stats: update.stats,
timestamp: new Date().toISOString()
};
} else if (update.type === 'error') {
this.error = new Error(update.error);
this.status = 'error';
done = true;
yield {
error: update.error,
iteration: this.currentIteration,
timestamp: new Date().toISOString()
};
}
}
this.cleanup();
}
waitForUpdate() {
return new Promise((resolve) => {
this.once('update', resolve);
});
}
handleWorkerMessage(message) {
this.emit('update', message);
}
estimateTimeRemaining(update) {
if (this.currentIteration === 0) return null;
const elapsed = Date.now() - this.startTime;
const iterationsPerMs = this.currentIteration / elapsed;
if (update.convergenceRate && update.convergenceRate > 0) {
// Estimate based on convergence rate
const iterationsToConverge = Math.log(this.session.options.tolerance || 1e-10) /
Math.log(update.convergenceRate);
const remainingIterations = iterationsToConverge - this.currentIteration;
return Math.max(0, remainingIterations / iterationsPerMs);
}
// Fallback to linear estimation
const maxIterations = this.session.options.maxIterations || 1000;
const remainingIterations = maxIterations - this.currentIteration;
return remainingIterations / iterationsPerMs;
}
checkHeartbeat() {
const timeSinceUpdate = Date.now() - this.lastUpdate;
if (timeSinceUpdate > this.manager.config.heartbeatInterval * 3) {
// Stream appears stalled
console.warn(`Stream ${this.session.id} appears stalled`);
this.stop();
}
}
stop() {
if (this.worker) {
this.worker.postMessage({
type: 'stop',
sessionId: this.session.id
});
}
this.status = 'stopped';
this.cleanup();
}
cleanup() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
if (this.worker) {
this.manager.releaseWorker(this.worker);
this.worker = null;
}
this.removeAllListeners();
}
destroy() {
this.stop();
this.status = 'destroyed';
}
getStatus() {
return {
status: this.status,
sessionId: this.session.id,
currentIteration: this.currentIteration,
currentResidual: this.currentResidual,
converged: this.converged,
error: this.error ? this.error.message : null,
startTime: this.startTime,
lastUpdate: this.lastUpdate,
elapsed: Date.now() - this.startTime
};
}
}
class VerificationLoop {
constructor(options = {}) {
this.enabled = options.enabled || false;
this.interval = options.interval || 100;
this.probeCount = options.probeCount || 10;
this.tolerance = options.tolerance || 1e-8;
this.lastVerification = 0;
this.verificationHistory = [];
}
shouldVerify(iteration) {
if (!this.enabled) return false;
return iteration - this.lastVerification >= this.interval;
}
async verify(matrix, solution, vector) {
this.lastVerification = Date.now();
try {
// Generate random probes
const probes = this.generateProbes(solution.length);
const errors = [];
// Test each probe
for (const probe of probes) {
const computed = this.computeMatrixVectorProbe(matrix, solution, probe.indices);
const expected = probe.indices.map(idx => vector[idx]);
const error = this.computeError(computed, expected);
errors.push(error);
}
const maxError = Math.max(...errors);
const meanError = errors.reduce((a, b) => a + b) / errors.length;
const verified = maxError < this.tolerance;
const result = {
verified,
maxError,
meanError,
probeCount: probes.length,
timestamp: new Date().toISOString()
};
this.verificationHistory.push(result);
// Keep only recent history
if (this.verificationHistory.length > 100) {
this.verificationHistory.shift();
}
return result;
} catch (error) {
console.error('Verification error:', error);
return {
verified: false,
error: error.message,
timestamp: new Date().toISOString()
};
}
}
generateProbes(vectorLength) {
const probes = [];
for (let i = 0; i < this.probeCount; i++) {
const probeSize = Math.min(50, Math.floor(vectorLength * 0.1));
const indices = [];
for (let j = 0; j < probeSize; j++) {
const idx = Math.floor(Math.random() * vectorLength);
if (!indices.includes(idx)) {
indices.push(idx);
}
}
probes.push({ indices: indices.sort() });
}
return probes;
}
computeMatrixVectorProbe(matrix, vector, indices) {
// Simplified matrix-vector multiplication for probe indices
const result = [];
for (const rowIdx of indices) {
let sum = 0;
if (matrix.format === 'coo') {
// Coordinate format
for (let i = 0; i < matrix.data.values.length; i++) {
if (matrix.data.rowIndices[i] === rowIdx) {
const col = matrix.data.colIndices[i];
const val = matrix.data.values[i];
sum += val * vector[col];
}
}
} else if (matrix.format === 'dense') {
// Dense format
for (let col = 0; col < matrix.cols; col++) {
sum += matrix.data[rowIdx][col] * vector[col];
}
}
result.push(sum);
}
return result;
}
computeError(computed, expected) {
let maxError = 0;
for (let i = 0; i < computed.length; i++) {
const error = Math.abs(computed[i] - expected[i]);
maxError = Math.max(maxError, error);
}
return maxError;
}
getVerificationStats() {
if (this.verificationHistory.length === 0) {
return null;
}
const recent = this.verificationHistory.slice(-10);
const verificationRate = recent.filter(v => v.verified).length / recent.length;
const avgError = recent.reduce((sum, v) => sum + (v.meanError || 0), 0) / recent.length;
return {
verificationRate,
avgError,
recentVerifications: recent.length,
totalVerifications: this.verificationHistory.length
};
}
}
// Session management utilities
class SessionManager {
constructor(config = {}) {
this.sessions = new Map();
this.config = config;
}
async createSession(sessionId, sessionData) {
const session = {
id: sessionId,
...sessionData,
status: 'created',
createdAt: new Date().toISOString(),
lastActivity: new Date().toISOString(),
metrics: {
iterations: 0,
residual: Infinity,
memoryUsage: 0
}
};
this.sessions.set(sessionId, session);
return session;
}
getSession(sessionId) {
return this.sessions.get(sessionId);
}
updateSession(sessionId, updates) {
const session = this.sessions.get(sessionId);
if (session) {
Object.assign(session, updates);
session.lastActivity = new Date().toISOString();
}
}
deleteSession(sessionId) {
this.sessions.delete(sessionId);
}
getStats() {
return {
totalSessions: this.sessions.size,
activeSessions: Array.from(this.sessions.values())
.filter(s => s.status === 'running').length
};
}
}
module.exports = {
StreamingManager,
SolverStream,
VerificationLoop,
SessionManager
};