620 lines
17 KiB
JavaScript
620 lines
17 KiB
JavaScript
const { EventEmitter } = require('events');
|
|
const https = require('https');
|
|
const WebSocket = require('ws');
|
|
|
|
class FlowNexusIntegration extends EventEmitter {
|
|
constructor(options = {}) {
|
|
super();
|
|
|
|
this.config = {
|
|
endpoint: options.endpoint || 'https://api.flow-nexus.ruv.io',
|
|
token: options.token || process.env.FLOW_NEXUS_TOKEN,
|
|
timeout: options.timeout || 30000,
|
|
retryAttempts: options.retryAttempts || 3,
|
|
retryDelay: options.retryDelay || 1000,
|
|
...options
|
|
};
|
|
|
|
this.registered = false;
|
|
this.solverId = null;
|
|
this.capabilities = [];
|
|
this.swarmConnections = new Map();
|
|
this.costUpdateQueue = [];
|
|
|
|
// Connection status
|
|
this.connected = false;
|
|
this.lastHeartbeat = null;
|
|
this.reconnectAttempts = 0;
|
|
}
|
|
|
|
async registerSolver(solverConfig = {}) {
|
|
try {
|
|
const registrationData = {
|
|
type: 'sublinear-time-solver',
|
|
version: '1.0.0',
|
|
capabilities: [
|
|
'streaming',
|
|
'verification',
|
|
'swarm-coordination',
|
|
'cost-propagation',
|
|
'real-time-updates',
|
|
...solverConfig.capabilities || []
|
|
],
|
|
endpoints: {
|
|
solve: '/api/v1/solve-stream',
|
|
verify: '/api/v1/verify',
|
|
status: '/api/v1/status',
|
|
swarm: '/api/v1/swarm'
|
|
},
|
|
performance: {
|
|
max_matrix_size: 1000000,
|
|
target_latency_ms: 1,
|
|
throughput_ops_per_sec: 10000
|
|
},
|
|
metadata: {
|
|
description: 'Advanced sublinear time sparse linear system solver',
|
|
algorithms: ['jacobi', 'gauss-seidel', 'conjugate-gradient', 'hybrid'],
|
|
formats: ['coo', 'csr', 'dense', 'matrix-market'],
|
|
verification: 'random-probe',
|
|
...solverConfig.metadata
|
|
}
|
|
};
|
|
|
|
const response = await this.makeRequest('POST', '/v1/solvers/register', registrationData);
|
|
|
|
this.solverId = response.solver_id;
|
|
this.registered = true;
|
|
this.capabilities = registrationData.capabilities;
|
|
|
|
console.log(`✓ Registered with Flow-Nexus as solver: ${this.solverId}`);
|
|
|
|
// Start heartbeat
|
|
this.startHeartbeat();
|
|
|
|
return {
|
|
solver_id: this.solverId,
|
|
status: 'registered',
|
|
capabilities: this.capabilities
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Flow-Nexus registration failed:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async joinSwarm(swarmId, nodeConfig = {}) {
|
|
if (!this.registered) {
|
|
throw new Error('Must register solver before joining swarm');
|
|
}
|
|
|
|
try {
|
|
const joinData = {
|
|
solver_id: this.solverId,
|
|
node_id: nodeConfig.nodeId || `node-${this.solverId}`,
|
|
capabilities: nodeConfig.capabilities || this.capabilities,
|
|
topology_preference: nodeConfig.topology || 'mesh',
|
|
coordination_enabled: true,
|
|
cost_propagation: true
|
|
};
|
|
|
|
const response = await this.makeRequest('POST', `/v1/swarms/${swarmId}/join`, joinData);
|
|
|
|
// Establish WebSocket connection for real-time coordination
|
|
await this.connectToSwarm(swarmId, response.websocket_endpoint);
|
|
|
|
this.swarmConnections.set(swarmId, {
|
|
status: 'connected',
|
|
nodeId: joinData.node_id,
|
|
connectedAt: new Date().toISOString(),
|
|
lastActivity: new Date().toISOString()
|
|
});
|
|
|
|
console.log(`✓ Joined swarm: ${swarmId} as node: ${joinData.node_id}`);
|
|
|
|
return {
|
|
swarm_id: swarmId,
|
|
node_id: joinData.node_id,
|
|
status: 'joined'
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error(`Failed to join swarm ${swarmId}:`, error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async connectToSwarm(swarmId, wsEndpoint) {
|
|
return new Promise((resolve, reject) => {
|
|
const ws = new WebSocket(wsEndpoint, {
|
|
headers: {
|
|
'Authorization': `Bearer ${this.config.token}`,
|
|
'X-Solver-ID': this.solverId
|
|
}
|
|
});
|
|
|
|
ws.on('open', () => {
|
|
console.log(`WebSocket connected to swarm: ${swarmId}`);
|
|
this.connected = true;
|
|
resolve();
|
|
});
|
|
|
|
ws.on('message', (data) => {
|
|
this.handleSwarmMessage(swarmId, JSON.parse(data.toString()));
|
|
});
|
|
|
|
ws.on('close', () => {
|
|
console.log(`WebSocket disconnected from swarm: ${swarmId}`);
|
|
this.connected = false;
|
|
this.scheduleReconnect(swarmId, wsEndpoint);
|
|
});
|
|
|
|
ws.on('error', (error) => {
|
|
console.error(`WebSocket error for swarm ${swarmId}:`, error);
|
|
reject(error);
|
|
});
|
|
|
|
// Store WebSocket connection
|
|
const swarmConnection = this.swarmConnections.get(swarmId);
|
|
if (swarmConnection) {
|
|
swarmConnection.ws = ws;
|
|
}
|
|
});
|
|
}
|
|
|
|
handleSwarmMessage(swarmId, message) {
|
|
switch (message.type) {
|
|
case 'cost_update':
|
|
this.handleCostUpdate(swarmId, message);
|
|
break;
|
|
|
|
case 'verification_request':
|
|
this.handleVerificationRequest(swarmId, message);
|
|
break;
|
|
|
|
case 'consensus_vote':
|
|
this.handleConsensusVote(swarmId, message);
|
|
break;
|
|
|
|
case 'heartbeat':
|
|
this.handleHeartbeat(swarmId, message);
|
|
break;
|
|
|
|
default:
|
|
console.warn(`Unknown swarm message type: ${message.type}`);
|
|
}
|
|
}
|
|
|
|
async handleCostUpdate(swarmId, message) {
|
|
try {
|
|
// Propagate cost update to local solver sessions
|
|
const costUpdate = {
|
|
type: 'cost_update',
|
|
session_id: message.session_id,
|
|
delta_costs: message.delta_costs,
|
|
matrix_updates: message.matrix_updates,
|
|
source_node: message.source_node,
|
|
propagation_depth: (message.propagation_depth || 0) + 1,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
this.emit('cost_update', costUpdate);
|
|
|
|
// Queue for batch processing
|
|
this.costUpdateQueue.push(costUpdate);
|
|
|
|
// Process queue if it gets large
|
|
if (this.costUpdateQueue.length > 100) {
|
|
await this.processCostUpdateQueue();
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error handling cost update:', error);
|
|
}
|
|
}
|
|
|
|
async handleVerificationRequest(swarmId, message) {
|
|
try {
|
|
// Respond to verification request
|
|
const verificationResult = await this.performVerification(message);
|
|
|
|
this.sendSwarmMessage(swarmId, {
|
|
type: 'verification_response',
|
|
request_id: message.request_id,
|
|
session_id: message.session_id,
|
|
verified: verificationResult.verified,
|
|
max_error: verificationResult.maxError,
|
|
node_id: this.solverId
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error handling verification request:', error);
|
|
}
|
|
}
|
|
|
|
async performVerification(request) {
|
|
// Implement verification logic
|
|
// This would integrate with the local solver's verification system
|
|
return {
|
|
verified: true,
|
|
maxError: 1e-10,
|
|
probeCount: request.probe_count || 10
|
|
};
|
|
}
|
|
|
|
handleConsensusVote(swarmId, message) {
|
|
// Handle consensus voting for distributed decisions
|
|
this.emit('consensus_vote', {
|
|
swarmId,
|
|
voteId: message.vote_id,
|
|
proposal: message.proposal,
|
|
nodeId: message.node_id
|
|
});
|
|
}
|
|
|
|
handleHeartbeat(swarmId, message) {
|
|
this.lastHeartbeat = Date.now();
|
|
|
|
// Update swarm connection activity
|
|
const connection = this.swarmConnections.get(swarmId);
|
|
if (connection) {
|
|
connection.lastActivity = new Date().toISOString();
|
|
}
|
|
}
|
|
|
|
sendSwarmMessage(swarmId, message) {
|
|
const connection = this.swarmConnections.get(swarmId);
|
|
if (connection && connection.ws && connection.ws.readyState === WebSocket.OPEN) {
|
|
connection.ws.send(JSON.stringify(message));
|
|
}
|
|
}
|
|
|
|
async broadcastCostUpdate(costUpdate) {
|
|
for (const [swarmId, connection] of this.swarmConnections) {
|
|
if (connection.ws && connection.ws.readyState === WebSocket.OPEN) {
|
|
this.sendSwarmMessage(swarmId, {
|
|
type: 'cost_update',
|
|
...costUpdate,
|
|
source_node: this.solverId,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
async processCostUpdateQueue() {
|
|
if (this.costUpdateQueue.length === 0) return;
|
|
|
|
// Batch process cost updates
|
|
const updates = this.costUpdateQueue.splice(0);
|
|
|
|
try {
|
|
// Aggregate updates by session
|
|
const sessionUpdates = new Map();
|
|
|
|
for (const update of updates) {
|
|
if (!sessionUpdates.has(update.session_id)) {
|
|
sessionUpdates.set(update.session_id, []);
|
|
}
|
|
sessionUpdates.get(update.session_id).push(update);
|
|
}
|
|
|
|
// Apply aggregated updates
|
|
for (const [sessionId, updates] of sessionUpdates) {
|
|
await this.applyAggregatedUpdates(sessionId, updates);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error processing cost update queue:', error);
|
|
}
|
|
}
|
|
|
|
async applyAggregatedUpdates(sessionId, updates) {
|
|
// Aggregate delta costs
|
|
const aggregatedDeltas = new Map();
|
|
|
|
for (const update of updates) {
|
|
if (update.delta_costs && update.delta_costs.indices) {
|
|
for (let i = 0; i < update.delta_costs.indices.length; i++) {
|
|
const idx = update.delta_costs.indices[i];
|
|
const value = update.delta_costs.values[i];
|
|
|
|
aggregatedDeltas.set(idx, (aggregatedDeltas.get(idx) || 0) + value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit aggregated update
|
|
this.emit('aggregated_cost_update', {
|
|
session_id: sessionId,
|
|
delta_costs: {
|
|
indices: Array.from(aggregatedDeltas.keys()),
|
|
values: Array.from(aggregatedDeltas.values())
|
|
},
|
|
update_count: updates.length,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
|
|
startHeartbeat() {
|
|
if (this.heartbeatInterval) {
|
|
clearInterval(this.heartbeatInterval);
|
|
}
|
|
|
|
this.heartbeatInterval = setInterval(async () => {
|
|
try {
|
|
await this.sendHeartbeat();
|
|
} catch (error) {
|
|
console.error('Heartbeat failed:', error.message);
|
|
}
|
|
}, 30000); // 30 second heartbeat
|
|
}
|
|
|
|
async sendHeartbeat() {
|
|
if (!this.registered) return;
|
|
|
|
const heartbeatData = {
|
|
solver_id: this.solverId,
|
|
timestamp: new Date().toISOString(),
|
|
status: 'active',
|
|
stats: this.getPerformanceStats()
|
|
};
|
|
|
|
await this.makeRequest('POST', `/v1/solvers/${this.solverId}/heartbeat`, heartbeatData);
|
|
|
|
// Send heartbeat to all swarm connections
|
|
for (const [swarmId, connection] of this.swarmConnections) {
|
|
this.sendSwarmMessage(swarmId, {
|
|
type: 'heartbeat',
|
|
node_id: this.solverId,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
}
|
|
|
|
getPerformanceStats() {
|
|
return {
|
|
memory_usage: process.memoryUsage(),
|
|
cpu_usage: process.cpuUsage(),
|
|
uptime: process.uptime(),
|
|
active_connections: this.swarmConnections.size,
|
|
queue_size: this.costUpdateQueue.length
|
|
};
|
|
}
|
|
|
|
scheduleReconnect(swarmId, wsEndpoint) {
|
|
this.reconnectAttempts++;
|
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
|
|
setTimeout(async () => {
|
|
try {
|
|
console.log(`Attempting to reconnect to swarm ${swarmId}...`);
|
|
await this.connectToSwarm(swarmId, wsEndpoint);
|
|
this.reconnectAttempts = 0;
|
|
} catch (error) {
|
|
console.error(`Reconnection failed for swarm ${swarmId}:`, error.message);
|
|
|
|
if (this.reconnectAttempts < 10) {
|
|
this.scheduleReconnect(swarmId, wsEndpoint);
|
|
} else {
|
|
console.error(`Max reconnection attempts reached for swarm ${swarmId}`);
|
|
}
|
|
}
|
|
}, delay);
|
|
}
|
|
|
|
async makeRequest(method, path, data = null) {
|
|
return new Promise((resolve, reject) => {
|
|
const url = new URL(path, this.config.endpoint);
|
|
|
|
const options = {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'User-Agent': 'SublinearSolver/1.0.0'
|
|
},
|
|
timeout: this.config.timeout
|
|
};
|
|
|
|
if (this.config.token) {
|
|
options.headers['Authorization'] = `Bearer ${this.config.token}`;
|
|
}
|
|
|
|
const req = https.request(url, options, (res) => {
|
|
let body = '';
|
|
|
|
res.on('data', (chunk) => {
|
|
body += chunk;
|
|
});
|
|
|
|
res.on('end', () => {
|
|
try {
|
|
const response = JSON.parse(body);
|
|
|
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
resolve(response);
|
|
} else {
|
|
reject(new Error(`HTTP ${res.statusCode}: ${response.error || body}`));
|
|
}
|
|
} catch (error) {
|
|
reject(new Error(`Invalid JSON response: ${body}`));
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', reject);
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
reject(new Error('Request timeout'));
|
|
});
|
|
|
|
if (data) {
|
|
req.write(JSON.stringify(data));
|
|
}
|
|
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
async getStatus() {
|
|
return {
|
|
registered: this.registered,
|
|
solver_id: this.solverId,
|
|
connected: this.connected,
|
|
swarm_connections: this.swarmConnections.size,
|
|
capabilities: this.capabilities,
|
|
last_heartbeat: this.lastHeartbeat,
|
|
queue_size: this.costUpdateQueue.length
|
|
};
|
|
}
|
|
|
|
async disconnect() {
|
|
// Clean shutdown
|
|
if (this.heartbeatInterval) {
|
|
clearInterval(this.heartbeatInterval);
|
|
}
|
|
|
|
// Close all swarm connections
|
|
for (const [swarmId, connection] of this.swarmConnections) {
|
|
if (connection.ws) {
|
|
connection.ws.close();
|
|
}
|
|
}
|
|
|
|
this.swarmConnections.clear();
|
|
this.connected = false;
|
|
|
|
// Unregister from Flow-Nexus
|
|
if (this.registered && this.solverId) {
|
|
try {
|
|
await this.makeRequest('DELETE', `/v1/solvers/${this.solverId}`);
|
|
console.log('✓ Unregistered from Flow-Nexus');
|
|
} catch (error) {
|
|
console.error('Error unregistering from Flow-Nexus:', error.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tool registration for Flow-Nexus MCP integration
|
|
class FlowNexusMCPTools {
|
|
constructor(integration) {
|
|
this.integration = integration;
|
|
}
|
|
|
|
getToolDefinitions() {
|
|
return [
|
|
{
|
|
name: 'sublinear_solver_stream',
|
|
description: 'Stream-based sublinear time matrix solver with real-time updates',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
matrix: {
|
|
type: 'object',
|
|
description: 'Sparse matrix in COO or CSR format'
|
|
},
|
|
vector: {
|
|
type: 'array',
|
|
items: { type: 'number' },
|
|
description: 'Right-hand side vector'
|
|
},
|
|
method: {
|
|
enum: ['jacobi', 'gauss_seidel', 'cg', 'hybrid'],
|
|
default: 'adaptive',
|
|
description: 'Solver method'
|
|
},
|
|
stream_options: {
|
|
type: 'object',
|
|
properties: {
|
|
real_time: { type: 'boolean', default: true },
|
|
verification_enabled: { type: 'boolean', default: true },
|
|
swarm_coordination: { type: 'boolean', default: false }
|
|
}
|
|
}
|
|
},
|
|
required: ['matrix', 'vector']
|
|
}
|
|
},
|
|
{
|
|
name: 'solver_verification',
|
|
description: 'Verify solution accuracy with random probes',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
session_id: { type: 'string' },
|
|
probe_count: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
|
tolerance: { type: 'number', default: 1e-8 }
|
|
},
|
|
required: ['session_id']
|
|
}
|
|
},
|
|
{
|
|
name: 'swarm_cost_propagation',
|
|
description: 'Propagate cost updates across swarm network',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
session_id: { type: 'string' },
|
|
delta_costs: {
|
|
type: 'object',
|
|
properties: {
|
|
indices: { type: 'array', items: { type: 'integer' } },
|
|
values: { type: 'array', items: { type: 'number' } }
|
|
}
|
|
},
|
|
swarm_id: { type: 'string' }
|
|
},
|
|
required: ['session_id', 'delta_costs']
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
async handleToolCall(toolName, parameters) {
|
|
switch (toolName) {
|
|
case 'sublinear_solver_stream':
|
|
return await this.handleSolverStream(parameters);
|
|
|
|
case 'solver_verification':
|
|
return await this.handleVerification(parameters);
|
|
|
|
case 'swarm_cost_propagation':
|
|
return await this.handleCostPropagation(parameters);
|
|
|
|
default:
|
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
}
|
|
}
|
|
|
|
async handleSolverStream(params) {
|
|
// Implementation would integrate with local solver
|
|
return {
|
|
session_id: 'session-' + Date.now(),
|
|
status: 'started',
|
|
stream_endpoint: '/api/v1/solve-stream'
|
|
};
|
|
}
|
|
|
|
async handleVerification(params) {
|
|
const result = await this.integration.performVerification(params);
|
|
return {
|
|
session_id: params.session_id,
|
|
verified: result.verified,
|
|
max_error: result.maxError
|
|
};
|
|
}
|
|
|
|
async handleCostPropagation(params) {
|
|
await this.integration.broadcastCostUpdate(params);
|
|
return {
|
|
status: 'propagated',
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
FlowNexusIntegration,
|
|
FlowNexusMCPTools
|
|
}; |