Files
wifi-densepose/vendor/ruvector/npm/packages/agentic-integration/regional-agent.js

456 lines
16 KiB
JavaScript

"use strict";
/**
* Regional Agent - Per-region agent implementation for distributed processing
*
* Handles:
* - Region-specific initialization
* - Local query processing
* - Cross-region communication
* - State synchronization
* - Metrics reporting
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegionalAgent = void 0;
const events_1 = require("events");
const child_process_1 = require("child_process");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class RegionalAgent extends events_1.EventEmitter {
constructor(config) {
super();
this.config = config;
this.activeStreams = 0;
this.totalQueries = 0;
this.totalLatency = 0;
this.localIndex = new Map();
this.syncQueue = [];
this.rateLimiter = new RateLimiter({
maxRequests: config.maxConcurrentStreams,
windowMs: 1000,
});
this.initialize();
}
/**
* Initialize regional agent
*/
async initialize() {
console.log(`[RegionalAgent:${this.config.region}] Initializing agent ${this.config.agentId}...`);
if (this.config.enableClaudeFlowHooks) {
try {
// Pre-task hook for agent initialization
await execAsync(`npx claude-flow@alpha hooks pre-task --description "Initialize regional agent ${this.config.agentId} in ${this.config.region}"`);
// Restore session if available
await execAsync(`npx claude-flow@alpha hooks session-restore --session-id "agent-${this.config.agentId}"`);
console.log(`[RegionalAgent:${this.config.region}] Claude-flow hooks initialized`);
}
catch (error) {
console.warn(`[RegionalAgent:${this.config.region}] Claude-flow hooks not available:`, error);
}
}
// Load local index from storage
await this.loadLocalIndex();
// Start metrics reporting
this.startMetricsReporting();
// Start sync process
this.startSyncProcess();
// Register with coordinator
await this.registerWithCoordinator();
this.emit('agent:initialized', {
agentId: this.config.agentId,
region: this.config.region,
});
console.log(`[RegionalAgent:${this.config.region}] Agent ${this.config.agentId} initialized successfully`);
}
/**
* Load local index from persistent storage
*/
async loadLocalIndex() {
try {
// Placeholder for actual storage loading
// In production, this would load from disk/database
console.log(`[RegionalAgent:${this.config.region}] Loading local index from ${this.config.localStoragePath}`);
// Simulate loading
this.localIndex.clear();
console.log(`[RegionalAgent:${this.config.region}] Local index loaded: ${this.localIndex.size} vectors`);
}
catch (error) {
console.error(`[RegionalAgent:${this.config.region}] Error loading local index:`, error);
throw error;
}
}
/**
* Register with coordinator
*/
async registerWithCoordinator() {
try {
console.log(`[RegionalAgent:${this.config.region}] Registering with coordinator at ${this.config.coordinatorEndpoint}`);
// In production, this would be an HTTP/gRPC call
// For now, emit event
this.emit('coordinator:register', {
agentId: this.config.agentId,
region: this.config.region,
endpoint: `https://${this.config.region}.ruvector.io/agent/${this.config.agentId}`,
capabilities: this.config.capabilities,
capacity: this.config.maxConcurrentStreams,
registeredAt: Date.now(),
});
console.log(`[RegionalAgent:${this.config.region}] Successfully registered with coordinator`);
}
catch (error) {
console.error(`[RegionalAgent:${this.config.region}] Failed to register with coordinator:`, error);
throw error;
}
}
/**
* Process query request locally
*/
async processQuery(request) {
const startTime = Date.now();
// Check rate limit
if (!this.rateLimiter.tryAcquire()) {
throw new Error('Rate limit exceeded');
}
this.activeStreams++;
this.totalQueries++;
try {
console.log(`[RegionalAgent:${this.config.region}] Processing query ${request.id}`);
// Validate query
this.validateQuery(request);
// Execute vector search
const matches = await this.searchVectors(request);
const latency = Date.now() - startTime;
this.totalLatency += latency;
const result = {
id: request.id,
matches,
latency,
region: this.config.region,
};
this.emit('query:completed', {
queryId: request.id,
latency,
matchCount: matches.length,
});
if (this.config.enableClaudeFlowHooks) {
try {
// Notify about query completion
await execAsync(`npx claude-flow@alpha hooks notify --message "Query ${request.id} completed in ${latency}ms with ${matches.length} matches"`);
}
catch (error) {
// Non-critical error
}
}
return result;
}
catch (error) {
console.error(`[RegionalAgent:${this.config.region}] Error processing query ${request.id}:`, error);
this.emit('query:failed', {
queryId: request.id,
error: error instanceof Error ? error.message : 'Unknown error',
});
throw error;
}
finally {
this.activeStreams--;
this.rateLimiter.release();
}
}
/**
* Validate query request
*/
validateQuery(request) {
if (!request.vector || request.vector.length !== this.config.vectorDimensions) {
throw new Error(`Invalid vector dimensions: expected ${this.config.vectorDimensions}, got ${request.vector?.length || 0}`);
}
if (request.topK <= 0 || request.topK > 1000) {
throw new Error(`Invalid topK value: ${request.topK} (must be between 1 and 1000)`);
}
}
/**
* Search vectors in local index
*/
async searchVectors(request) {
// Placeholder for actual vector search
// In production, this would use FAISS, Annoy, or similar library
const matches = [];
// Simulate vector search
for (const [id, vector] of this.localIndex.entries()) {
const score = this.calculateSimilarity(request.vector, vector);
// Apply filters if present
if (request.filters && !this.matchesFilters(vector.metadata, request.filters)) {
continue;
}
matches.push({
id,
score,
metadata: vector.metadata || {},
});
}
// Sort by score and return top-k
matches.sort((a, b) => b.score - a.score);
return matches.slice(0, request.topK);
}
/**
* Calculate cosine similarity between vectors
*/
calculateSimilarity(v1, v2) {
let dotProduct = 0;
let norm1 = 0;
let norm2 = 0;
for (let i = 0; i < v1.length; i++) {
dotProduct += v1[i] * v2[i];
norm1 += v1[i] * v1[i];
norm2 += v2[i] * v2[i];
}
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}
/**
* Check if metadata matches filters
*/
matchesFilters(metadata, filters) {
for (const [key, value] of Object.entries(filters)) {
if (metadata[key] !== value) {
return false;
}
}
return true;
}
/**
* Add/update vectors in local index
*/
async indexVectors(vectors) {
console.log(`[RegionalAgent:${this.config.region}] Indexing ${vectors.length} vectors`);
for (const { id, vector, metadata } of vectors) {
this.localIndex.set(id, { vector, metadata });
}
// Queue for cross-region sync
this.syncQueue.push({
type: 'index',
data: vectors,
timestamp: Date.now(),
sourceRegion: this.config.region,
});
this.emit('vectors:indexed', { count: vectors.length });
if (this.config.enableClaudeFlowHooks) {
try {
await execAsync(`npx claude-flow@alpha hooks post-edit --file "local-index" --memory-key "swarm/${this.config.agentId}/index-update"`);
}
catch (error) {
// Non-critical
}
}
}
/**
* Delete vectors from local index
*/
async deleteVectors(ids) {
console.log(`[RegionalAgent:${this.config.region}] Deleting ${ids.length} vectors`);
for (const id of ids) {
this.localIndex.delete(id);
}
// Queue for cross-region sync
this.syncQueue.push({
type: 'delete',
data: ids,
timestamp: Date.now(),
sourceRegion: this.config.region,
});
this.emit('vectors:deleted', { count: ids.length });
}
/**
* Handle sync payload from other regions
*/
async handleSyncPayload(payload) {
// Don't process our own sync messages
if (payload.sourceRegion === this.config.region) {
return;
}
console.log(`[RegionalAgent:${this.config.region}] Received sync payload from ${payload.sourceRegion}: ${payload.type}`);
try {
switch (payload.type) {
case 'index':
await this.indexVectors(payload.data);
break;
case 'update':
await this.indexVectors(payload.data);
break;
case 'delete':
await this.deleteVectors(payload.data);
break;
}
this.emit('sync:applied', {
type: payload.type,
sourceRegion: payload.sourceRegion,
});
}
catch (error) {
console.error(`[RegionalAgent:${this.config.region}] Error applying sync payload:`, error);
this.emit('sync:failed', {
type: payload.type,
sourceRegion: payload.sourceRegion,
error: error instanceof Error ? error.message : 'Unknown error',
});
}
}
/**
* Start metrics reporting loop
*/
startMetricsReporting() {
this.metricsTimer = setInterval(() => {
this.reportMetrics();
}, this.config.metricsReportInterval);
}
/**
* Report metrics to coordinator
*/
reportMetrics() {
const metrics = {
agentId: this.config.agentId,
region: this.config.region,
cpuUsage: this.getCpuUsage(),
memoryUsage: this.getMemoryUsage(),
activeStreams: this.activeStreams,
queryLatency: this.totalQueries > 0 ? this.totalLatency / this.totalQueries : 0,
timestamp: Date.now(),
healthy: this.isHealthy(),
};
this.emit('metrics:report', metrics);
// Reset counters (sliding window)
if (this.totalQueries > 1000) {
this.totalQueries = 0;
this.totalLatency = 0;
}
}
/**
* Get CPU usage (placeholder)
*/
getCpuUsage() {
// In production, this would read from /proc/stat or similar
return Math.random() * 100;
}
/**
* Get memory usage (placeholder)
*/
getMemoryUsage() {
// In production, this would read from process.memoryUsage()
const usage = process.memoryUsage();
return (usage.heapUsed / usage.heapTotal) * 100;
}
/**
* Check if agent is healthy
*/
isHealthy() {
return (this.activeStreams < this.config.maxConcurrentStreams &&
this.getMemoryUsage() < 90 &&
this.getCpuUsage() < 90);
}
/**
* Start sync process loop
*/
startSyncProcess() {
this.syncTimer = setInterval(() => {
this.processSyncQueue();
}, this.config.syncInterval);
}
/**
* Process sync queue (send to other regions)
*/
async processSyncQueue() {
if (this.syncQueue.length === 0)
return;
const batch = this.syncQueue.splice(0, 100); // Process in batches
console.log(`[RegionalAgent:${this.config.region}] Processing sync batch: ${batch.length} items`);
for (const payload of batch) {
this.emit('sync:broadcast', payload);
}
}
/**
* Get agent status
*/
getStatus() {
return {
agentId: this.config.agentId,
region: this.config.region,
healthy: this.isHealthy(),
activeStreams: this.activeStreams,
indexSize: this.localIndex.size,
syncQueueSize: this.syncQueue.length,
avgQueryLatency: this.totalQueries > 0 ? this.totalLatency / this.totalQueries : 0,
};
}
/**
* Shutdown agent gracefully
*/
async shutdown() {
console.log(`[RegionalAgent:${this.config.region}] Shutting down agent ${this.config.agentId}...`);
// Stop timers
if (this.metricsTimer) {
clearInterval(this.metricsTimer);
}
if (this.syncTimer) {
clearInterval(this.syncTimer);
}
// Process remaining sync queue
await this.processSyncQueue();
// Save local index
await this.saveLocalIndex();
if (this.config.enableClaudeFlowHooks) {
try {
await execAsync(`npx claude-flow@alpha hooks post-task --task-id "agent-${this.config.agentId}-shutdown"`);
await execAsync(`npx claude-flow@alpha hooks session-end --export-metrics true`);
}
catch (error) {
console.warn(`[RegionalAgent:${this.config.region}] Error executing shutdown hooks:`, error);
}
}
this.emit('agent:shutdown', {
agentId: this.config.agentId,
region: this.config.region,
});
}
/**
* Save local index to persistent storage
*/
async saveLocalIndex() {
try {
console.log(`[RegionalAgent:${this.config.region}] Saving local index to ${this.config.localStoragePath}`);
// Placeholder for actual storage saving
// In production, this would write to disk/database
console.log(`[RegionalAgent:${this.config.region}] Local index saved: ${this.localIndex.size} vectors`);
}
catch (error) {
console.error(`[RegionalAgent:${this.config.region}] Error saving local index:`, error);
throw error;
}
}
}
exports.RegionalAgent = RegionalAgent;
/**
* Rate limiter for query processing
*/
class RateLimiter {
constructor(config) {
this.config = config;
this.requests = 0;
this.windowStart = Date.now();
}
tryAcquire() {
const now = Date.now();
// Reset window if expired
if (now - this.windowStart >= this.config.windowMs) {
this.requests = 0;
this.windowStart = now;
}
if (this.requests < this.config.maxRequests) {
this.requests++;
return true;
}
return false;
}
release() {
if (this.requests > 0) {
this.requests--;
}
}
}
//# sourceMappingURL=regional-agent.js.map