@sparkleideas/integration 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -0
- package/package.json +55 -0
- package/src/__tests__/agent-adapter.test.ts +271 -0
- package/src/__tests__/agentic-flow-agent.test.ts +176 -0
- package/src/__tests__/token-optimizer.test.ts +176 -0
- package/src/agent-adapter.ts +651 -0
- package/src/agentic-flow-agent.ts +802 -0
- package/src/agentic-flow-bridge.ts +803 -0
- package/src/attention-coordinator.ts +679 -0
- package/src/feature-flags.ts +485 -0
- package/src/index.ts +466 -0
- package/src/long-running-worker.ts +871 -0
- package/src/multi-model-router.ts +1079 -0
- package/src/provider-adapter.ts +1168 -0
- package/src/sdk-bridge.ts +435 -0
- package/src/sona-adapter.ts +824 -0
- package/src/specialized-worker.ts +864 -0
- package/src/swarm-adapter.ts +1112 -0
- package/src/token-optimizer.ts +306 -0
- package/src/types.ts +494 -0
- package/src/worker-base.ts +822 -0
- package/src/worker-pool.ts +933 -0
- package/tmp.json +0 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkerBase - Abstract Base Worker Class
|
|
3
|
+
*
|
|
4
|
+
* Provides the foundation for all worker patterns in Claude Flow v3,
|
|
5
|
+
* aligned with @sparkleideas/agentic-flow@alpha's worker architecture.
|
|
6
|
+
*
|
|
7
|
+
* Key Features:
|
|
8
|
+
* - Specialization embeddings for intelligent task routing
|
|
9
|
+
* - Load balancing and capacity tracking
|
|
10
|
+
* - Capability-based task matching
|
|
11
|
+
* - Memory and coordination integration
|
|
12
|
+
*
|
|
13
|
+
* This implements ADR-001 by building on @sparkleideas/agentic-flow patterns
|
|
14
|
+
* while providing Claude Flow-specific extensions.
|
|
15
|
+
*
|
|
16
|
+
* @module v3/integration/worker-base
|
|
17
|
+
* @version 3.0.0-alpha.1
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
import type { Task, TaskResult, AgentStatus, Message } from './agentic-flow-agent.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Worker configuration interface
|
|
25
|
+
*/
|
|
26
|
+
export interface WorkerConfig {
|
|
27
|
+
/** Unique worker identifier */
|
|
28
|
+
id: string;
|
|
29
|
+
/** Worker type classification */
|
|
30
|
+
type: WorkerType;
|
|
31
|
+
/** Human-readable name */
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Worker capabilities */
|
|
34
|
+
capabilities: string[];
|
|
35
|
+
/** Specialization embedding vector (for similarity-based routing) */
|
|
36
|
+
specialization?: Float32Array | number[];
|
|
37
|
+
/** Maximum concurrent tasks */
|
|
38
|
+
maxConcurrentTasks?: number;
|
|
39
|
+
/** Task execution timeout in milliseconds */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
/** Worker priority (0-100, higher = more preferred) */
|
|
42
|
+
priority?: number;
|
|
43
|
+
/** Memory configuration */
|
|
44
|
+
memory?: WorkerMemoryConfig;
|
|
45
|
+
/** Coordination configuration */
|
|
46
|
+
coordination?: WorkerCoordinationConfig;
|
|
47
|
+
/** Provider configuration for multi-model support */
|
|
48
|
+
provider?: WorkerProviderConfig;
|
|
49
|
+
/** Additional metadata */
|
|
50
|
+
metadata?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Worker type classification
|
|
55
|
+
*/
|
|
56
|
+
export type WorkerType =
|
|
57
|
+
| 'coder'
|
|
58
|
+
| 'reviewer'
|
|
59
|
+
| 'tester'
|
|
60
|
+
| 'researcher'
|
|
61
|
+
| 'planner'
|
|
62
|
+
| 'architect'
|
|
63
|
+
| 'coordinator'
|
|
64
|
+
| 'security'
|
|
65
|
+
| 'performance'
|
|
66
|
+
| 'specialized'
|
|
67
|
+
| 'long-running'
|
|
68
|
+
| 'generic';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Worker memory configuration
|
|
72
|
+
*/
|
|
73
|
+
export interface WorkerMemoryConfig {
|
|
74
|
+
/** Enable persistent memory */
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
/** Memory namespace for isolation */
|
|
77
|
+
namespace?: string;
|
|
78
|
+
/** Maximum memory entries */
|
|
79
|
+
maxEntries?: number;
|
|
80
|
+
/** Enable embedding-based retrieval */
|
|
81
|
+
enableEmbeddings?: boolean;
|
|
82
|
+
/** Memory bank ID (for cross-session persistence) */
|
|
83
|
+
memoryBankId?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Worker coordination configuration
|
|
88
|
+
*/
|
|
89
|
+
export interface WorkerCoordinationConfig {
|
|
90
|
+
/** Enable coordination with other workers */
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
/** Coordination protocol */
|
|
93
|
+
protocol?: 'direct' | 'broadcast' | 'pub-sub' | 'request-response';
|
|
94
|
+
/** Message queue capacity */
|
|
95
|
+
queueCapacity?: number;
|
|
96
|
+
/** Heartbeat interval in milliseconds */
|
|
97
|
+
heartbeatInterval?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Worker provider configuration for multi-model support
|
|
102
|
+
*/
|
|
103
|
+
export interface WorkerProviderConfig {
|
|
104
|
+
/** Provider identifier */
|
|
105
|
+
providerId?: string;
|
|
106
|
+
/** Model identifier */
|
|
107
|
+
modelId?: string;
|
|
108
|
+
/** Provider-specific options */
|
|
109
|
+
options?: Record<string, unknown>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Agent output interface (compatible with @sparkleideas/agentic-flow)
|
|
114
|
+
*/
|
|
115
|
+
export interface AgentOutput {
|
|
116
|
+
/** Output content */
|
|
117
|
+
content: string | Record<string, unknown>;
|
|
118
|
+
/** Success indicator */
|
|
119
|
+
success: boolean;
|
|
120
|
+
/** Error if failed */
|
|
121
|
+
error?: Error;
|
|
122
|
+
/** Execution duration in milliseconds */
|
|
123
|
+
duration: number;
|
|
124
|
+
/** Tokens used (if applicable) */
|
|
125
|
+
tokensUsed?: number;
|
|
126
|
+
/** Artifacts produced */
|
|
127
|
+
artifacts?: WorkerArtifact[];
|
|
128
|
+
/** Metadata */
|
|
129
|
+
metadata?: Record<string, unknown>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Worker artifact - files or data produced by task execution
|
|
134
|
+
*/
|
|
135
|
+
export interface WorkerArtifact {
|
|
136
|
+
/** Artifact identifier */
|
|
137
|
+
id: string;
|
|
138
|
+
/** Artifact type */
|
|
139
|
+
type: 'file' | 'data' | 'code' | 'log' | 'metric';
|
|
140
|
+
/** Artifact name */
|
|
141
|
+
name: string;
|
|
142
|
+
/** Artifact content or path */
|
|
143
|
+
content: string | Buffer | Record<string, unknown>;
|
|
144
|
+
/** Content size in bytes */
|
|
145
|
+
size?: number;
|
|
146
|
+
/** Creation timestamp */
|
|
147
|
+
createdAt: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Worker metrics for monitoring
|
|
152
|
+
*/
|
|
153
|
+
export interface WorkerMetrics {
|
|
154
|
+
/** Total tasks executed */
|
|
155
|
+
tasksExecuted: number;
|
|
156
|
+
/** Successful task count */
|
|
157
|
+
tasksSucceeded: number;
|
|
158
|
+
/** Failed task count */
|
|
159
|
+
tasksFailed: number;
|
|
160
|
+
/** Average execution duration */
|
|
161
|
+
avgDuration: number;
|
|
162
|
+
/** Total tokens used */
|
|
163
|
+
totalTokensUsed: number;
|
|
164
|
+
/** Current load (0.0-1.0) */
|
|
165
|
+
currentLoad: number;
|
|
166
|
+
/** Uptime in milliseconds */
|
|
167
|
+
uptime: number;
|
|
168
|
+
/** Last activity timestamp */
|
|
169
|
+
lastActivity: number;
|
|
170
|
+
/** Health score (0.0-1.0) */
|
|
171
|
+
healthScore: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Worker health status
|
|
176
|
+
*/
|
|
177
|
+
export interface WorkerHealth {
|
|
178
|
+
/** Health status */
|
|
179
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
180
|
+
/** Health score (0.0-1.0) */
|
|
181
|
+
score: number;
|
|
182
|
+
/** Active issues */
|
|
183
|
+
issues: string[];
|
|
184
|
+
/** Last health check timestamp */
|
|
185
|
+
lastCheck: number;
|
|
186
|
+
/** Resource usage */
|
|
187
|
+
resources: {
|
|
188
|
+
memoryMb: number;
|
|
189
|
+
cpuPercent: number;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* WorkerBase - Abstract base class for all workers
|
|
195
|
+
*
|
|
196
|
+
* This class provides the foundation for:
|
|
197
|
+
* - SpecializedWorker: Domain-specific task processing
|
|
198
|
+
* - LongRunningWorker: Checkpoint-based long-running tasks
|
|
199
|
+
* - Generic workers for various use cases
|
|
200
|
+
*
|
|
201
|
+
* Usage:
|
|
202
|
+
* ```typescript
|
|
203
|
+
* class CoderWorker extends WorkerBase {
|
|
204
|
+
* async execute(task: Task): Promise<AgentOutput> {
|
|
205
|
+
* // Implementation
|
|
206
|
+
* }
|
|
207
|
+
* }
|
|
208
|
+
*
|
|
209
|
+
* const worker = new CoderWorker({
|
|
210
|
+
* id: 'coder-1',
|
|
211
|
+
* type: 'coder',
|
|
212
|
+
* capabilities: ['code-generation', 'refactoring'],
|
|
213
|
+
* });
|
|
214
|
+
*
|
|
215
|
+
* await worker.initialize();
|
|
216
|
+
* const result = await worker.execute(task);
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
export abstract class WorkerBase extends EventEmitter {
|
|
220
|
+
// ===== Public Properties =====
|
|
221
|
+
|
|
222
|
+
/** Unique worker identifier */
|
|
223
|
+
readonly id: string;
|
|
224
|
+
|
|
225
|
+
/** Worker type classification */
|
|
226
|
+
readonly type: WorkerType;
|
|
227
|
+
|
|
228
|
+
/** Human-readable name */
|
|
229
|
+
readonly name: string;
|
|
230
|
+
|
|
231
|
+
/** Worker capabilities */
|
|
232
|
+
capabilities: string[];
|
|
233
|
+
|
|
234
|
+
/** Specialization embedding vector */
|
|
235
|
+
specialization?: Float32Array;
|
|
236
|
+
|
|
237
|
+
/** Current load factor (0.0-1.0) */
|
|
238
|
+
load: number = 0;
|
|
239
|
+
|
|
240
|
+
/** Current status */
|
|
241
|
+
status: AgentStatus = 'spawning';
|
|
242
|
+
|
|
243
|
+
/** Worker configuration (publicly readable for pool operations) */
|
|
244
|
+
readonly config: WorkerConfig;
|
|
245
|
+
|
|
246
|
+
/** Initialization state */
|
|
247
|
+
protected initialized: boolean = false;
|
|
248
|
+
|
|
249
|
+
/** Current concurrent task count */
|
|
250
|
+
protected currentTaskCount: number = 0;
|
|
251
|
+
|
|
252
|
+
/** Creation timestamp */
|
|
253
|
+
protected createdAt: number;
|
|
254
|
+
|
|
255
|
+
/** Worker metrics */
|
|
256
|
+
protected metrics: WorkerMetrics;
|
|
257
|
+
|
|
258
|
+
/** Message queue for coordination */
|
|
259
|
+
protected messageQueue: Message[] = [];
|
|
260
|
+
|
|
261
|
+
/** Memory reference (for persistent memory integration) */
|
|
262
|
+
protected memoryBankId?: string;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Create a new WorkerBase instance
|
|
266
|
+
*
|
|
267
|
+
* @param config - Worker configuration
|
|
268
|
+
*/
|
|
269
|
+
constructor(config: WorkerConfig) {
|
|
270
|
+
super();
|
|
271
|
+
|
|
272
|
+
// Validate required fields
|
|
273
|
+
if (!config.id) {
|
|
274
|
+
throw new Error('Worker config must include id');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
this.id = config.id;
|
|
278
|
+
this.type = config.type || 'generic';
|
|
279
|
+
this.name = config.name || `${this.type}-${this.id}`;
|
|
280
|
+
this.capabilities = config.capabilities || [];
|
|
281
|
+
this.config = config;
|
|
282
|
+
this.createdAt = Date.now();
|
|
283
|
+
|
|
284
|
+
// Set specialization embedding
|
|
285
|
+
if (config.specialization) {
|
|
286
|
+
this.specialization = config.specialization instanceof Float32Array
|
|
287
|
+
? config.specialization
|
|
288
|
+
: new Float32Array(config.specialization);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Initialize metrics
|
|
292
|
+
this.metrics = {
|
|
293
|
+
tasksExecuted: 0,
|
|
294
|
+
tasksSucceeded: 0,
|
|
295
|
+
tasksFailed: 0,
|
|
296
|
+
avgDuration: 0,
|
|
297
|
+
totalTokensUsed: 0,
|
|
298
|
+
currentLoad: 0,
|
|
299
|
+
uptime: 0,
|
|
300
|
+
lastActivity: Date.now(),
|
|
301
|
+
healthScore: 1.0,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Memory configuration
|
|
305
|
+
if (config.memory?.enabled) {
|
|
306
|
+
this.memoryBankId = config.memory.memoryBankId;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this.emit('created', { workerId: this.id, type: this.type });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ===== Abstract Methods =====
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Execute a task
|
|
316
|
+
*
|
|
317
|
+
* This is the core method that subclasses must implement.
|
|
318
|
+
* It receives a task and returns the execution result.
|
|
319
|
+
*
|
|
320
|
+
* @param task - Task to execute
|
|
321
|
+
* @returns Agent output with results
|
|
322
|
+
*/
|
|
323
|
+
abstract execute(task: Task): Promise<AgentOutput>;
|
|
324
|
+
|
|
325
|
+
// ===== Lifecycle Methods =====
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Initialize the worker
|
|
329
|
+
*
|
|
330
|
+
* Sets up resources, connections, and prepares for task execution.
|
|
331
|
+
*/
|
|
332
|
+
async initialize(): Promise<void> {
|
|
333
|
+
if (this.initialized) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
this.emit('initializing', { workerId: this.id });
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
// Initialize memory if configured
|
|
341
|
+
if (this.config.memory?.enabled) {
|
|
342
|
+
await this.initializeMemory();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Initialize coordination if configured
|
|
346
|
+
if (this.config.coordination?.enabled) {
|
|
347
|
+
await this.initializeCoordination();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Call subclass initialization hook
|
|
351
|
+
await this.onInitialize();
|
|
352
|
+
|
|
353
|
+
this.status = 'idle';
|
|
354
|
+
this.initialized = true;
|
|
355
|
+
|
|
356
|
+
this.emit('initialized', { workerId: this.id });
|
|
357
|
+
} catch (error) {
|
|
358
|
+
this.status = 'error';
|
|
359
|
+
this.emit('initialization-failed', {
|
|
360
|
+
workerId: this.id,
|
|
361
|
+
error: error as Error,
|
|
362
|
+
});
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Shutdown the worker gracefully
|
|
369
|
+
*
|
|
370
|
+
* Releases resources and completes cleanup.
|
|
371
|
+
*/
|
|
372
|
+
async shutdown(): Promise<void> {
|
|
373
|
+
this.emit('shutting-down', { workerId: this.id });
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
// Call subclass shutdown hook
|
|
377
|
+
await this.onShutdown();
|
|
378
|
+
|
|
379
|
+
this.status = 'terminated';
|
|
380
|
+
this.initialized = false;
|
|
381
|
+
|
|
382
|
+
this.emit('shutdown', { workerId: this.id });
|
|
383
|
+
} catch (error) {
|
|
384
|
+
this.emit('shutdown-error', {
|
|
385
|
+
workerId: this.id,
|
|
386
|
+
error: error as Error,
|
|
387
|
+
});
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ===== Task Execution =====
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Execute a task with wrapper logic
|
|
396
|
+
*
|
|
397
|
+
* Handles load tracking, metrics, and error handling.
|
|
398
|
+
*
|
|
399
|
+
* @param task - Task to execute
|
|
400
|
+
* @returns Task result with metrics
|
|
401
|
+
*/
|
|
402
|
+
async executeTask(task: Task): Promise<TaskResult> {
|
|
403
|
+
this.ensureInitialized();
|
|
404
|
+
|
|
405
|
+
// Check capacity
|
|
406
|
+
const maxTasks = this.config.maxConcurrentTasks || 1;
|
|
407
|
+
if (this.currentTaskCount >= maxTasks) {
|
|
408
|
+
throw new Error(`Worker ${this.id} at capacity (${maxTasks} tasks)`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
this.currentTaskCount++;
|
|
412
|
+
this.updateLoad();
|
|
413
|
+
this.status = 'busy';
|
|
414
|
+
const startTime = Date.now();
|
|
415
|
+
|
|
416
|
+
this.emit('task-started', { workerId: this.id, taskId: task.id });
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
// Execute via subclass implementation
|
|
420
|
+
const output = await this.execute(task);
|
|
421
|
+
|
|
422
|
+
const duration = Date.now() - startTime;
|
|
423
|
+
|
|
424
|
+
// Update metrics
|
|
425
|
+
this.updateMetricsSuccess(duration, output.tokensUsed);
|
|
426
|
+
|
|
427
|
+
const result: TaskResult = {
|
|
428
|
+
taskId: task.id,
|
|
429
|
+
success: output.success,
|
|
430
|
+
output: output.content,
|
|
431
|
+
duration,
|
|
432
|
+
tokensUsed: output.tokensUsed,
|
|
433
|
+
metadata: output.metadata,
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
this.emit('task-completed', {
|
|
437
|
+
workerId: this.id,
|
|
438
|
+
taskId: task.id,
|
|
439
|
+
duration,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
return result;
|
|
443
|
+
} catch (error) {
|
|
444
|
+
const duration = Date.now() - startTime;
|
|
445
|
+
|
|
446
|
+
// Update metrics for failure
|
|
447
|
+
this.updateMetricsFailure(duration);
|
|
448
|
+
|
|
449
|
+
const result: TaskResult = {
|
|
450
|
+
taskId: task.id,
|
|
451
|
+
success: false,
|
|
452
|
+
error: error as Error,
|
|
453
|
+
duration,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
this.emit('task-failed', {
|
|
457
|
+
workerId: this.id,
|
|
458
|
+
taskId: task.id,
|
|
459
|
+
error: error as Error,
|
|
460
|
+
duration,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
return result;
|
|
464
|
+
} finally {
|
|
465
|
+
this.currentTaskCount--;
|
|
466
|
+
this.updateLoad();
|
|
467
|
+
this.status = this.currentTaskCount > 0 ? 'busy' : 'idle';
|
|
468
|
+
this.metrics.lastActivity = Date.now();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ===== Embedding & Matching =====
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Get the specialization embedding
|
|
476
|
+
*
|
|
477
|
+
* Returns the worker's specialization vector for similarity-based routing.
|
|
478
|
+
* If no specialization is set, generates a default based on capabilities.
|
|
479
|
+
*
|
|
480
|
+
* @returns Specialization embedding vector
|
|
481
|
+
*/
|
|
482
|
+
getEmbedding(): Float32Array {
|
|
483
|
+
if (this.specialization) {
|
|
484
|
+
return this.specialization;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Generate default embedding from capabilities
|
|
488
|
+
return this.generateDefaultEmbedding();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Calculate similarity with a task embedding
|
|
493
|
+
*
|
|
494
|
+
* Uses cosine similarity to match worker specialization with task requirements.
|
|
495
|
+
*
|
|
496
|
+
* @param taskEmbedding - Task embedding vector
|
|
497
|
+
* @returns Similarity score (0.0-1.0)
|
|
498
|
+
*/
|
|
499
|
+
calculateSimilarity(taskEmbedding: Float32Array | number[]): number {
|
|
500
|
+
const workerEmbedding = this.getEmbedding();
|
|
501
|
+
const taskArray = taskEmbedding instanceof Float32Array
|
|
502
|
+
? taskEmbedding
|
|
503
|
+
: new Float32Array(taskEmbedding);
|
|
504
|
+
|
|
505
|
+
// Cosine similarity
|
|
506
|
+
return this.cosineSimilarity(workerEmbedding, taskArray);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Check if worker has required capabilities for a task
|
|
511
|
+
*
|
|
512
|
+
* @param requiredCapabilities - Required capability list
|
|
513
|
+
* @returns True if worker has all required capabilities
|
|
514
|
+
*/
|
|
515
|
+
hasCapabilities(requiredCapabilities: string[]): boolean {
|
|
516
|
+
return requiredCapabilities.every((cap) =>
|
|
517
|
+
this.capabilities.includes(cap)
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ===== Load Management =====
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Update the load factor
|
|
525
|
+
*
|
|
526
|
+
* @param delta - Load change (optional, recalculates if not provided)
|
|
527
|
+
*/
|
|
528
|
+
updateLoad(delta?: number): void {
|
|
529
|
+
if (typeof delta === 'number') {
|
|
530
|
+
this.load = Math.max(0, Math.min(1, this.load + delta));
|
|
531
|
+
} else {
|
|
532
|
+
// Calculate based on current task count
|
|
533
|
+
const maxTasks = this.config.maxConcurrentTasks || 1;
|
|
534
|
+
this.load = this.currentTaskCount / maxTasks;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
this.metrics.currentLoad = this.load;
|
|
538
|
+
|
|
539
|
+
this.emit('load-updated', { workerId: this.id, load: this.load });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Check if worker is available for tasks
|
|
544
|
+
*/
|
|
545
|
+
isAvailable(): boolean {
|
|
546
|
+
return (
|
|
547
|
+
this.initialized &&
|
|
548
|
+
this.status !== 'terminated' &&
|
|
549
|
+
this.status !== 'error' &&
|
|
550
|
+
this.currentTaskCount < (this.config.maxConcurrentTasks || 1)
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// ===== Health & Metrics =====
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Get worker health status
|
|
558
|
+
*/
|
|
559
|
+
getHealth(): WorkerHealth {
|
|
560
|
+
const uptime = Date.now() - this.createdAt;
|
|
561
|
+
this.metrics.uptime = uptime;
|
|
562
|
+
|
|
563
|
+
// Calculate health score
|
|
564
|
+
const successRate =
|
|
565
|
+
this.metrics.tasksExecuted > 0
|
|
566
|
+
? this.metrics.tasksSucceeded / this.metrics.tasksExecuted
|
|
567
|
+
: 1;
|
|
568
|
+
|
|
569
|
+
const healthScore = Math.min(1, successRate * 0.7 + (1 - this.load) * 0.3);
|
|
570
|
+
this.metrics.healthScore = healthScore;
|
|
571
|
+
|
|
572
|
+
let status: WorkerHealth['status'] = 'healthy';
|
|
573
|
+
const issues: string[] = [];
|
|
574
|
+
|
|
575
|
+
if (healthScore < 0.5) {
|
|
576
|
+
status = 'unhealthy';
|
|
577
|
+
issues.push('Low health score');
|
|
578
|
+
} else if (healthScore < 0.8) {
|
|
579
|
+
status = 'degraded';
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (this.load > 0.9) {
|
|
583
|
+
issues.push('High load');
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (this.status === 'error') {
|
|
587
|
+
status = 'unhealthy';
|
|
588
|
+
issues.push('Worker in error state');
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return {
|
|
592
|
+
status,
|
|
593
|
+
score: healthScore,
|
|
594
|
+
issues,
|
|
595
|
+
lastCheck: Date.now(),
|
|
596
|
+
resources: {
|
|
597
|
+
memoryMb: this.estimateMemoryUsage(),
|
|
598
|
+
cpuPercent: this.load * 100,
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Get worker metrics
|
|
605
|
+
*/
|
|
606
|
+
getMetrics(): WorkerMetrics {
|
|
607
|
+
this.metrics.uptime = Date.now() - this.createdAt;
|
|
608
|
+
return { ...this.metrics };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ===== Coordination =====
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Send a message to another worker
|
|
615
|
+
*
|
|
616
|
+
* @param to - Target worker ID
|
|
617
|
+
* @param message - Message to send
|
|
618
|
+
*/
|
|
619
|
+
async sendMessage(to: string, message: Message): Promise<void> {
|
|
620
|
+
this.emit('message-send', {
|
|
621
|
+
from: this.id,
|
|
622
|
+
to,
|
|
623
|
+
message,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Receive a message from another worker
|
|
629
|
+
*
|
|
630
|
+
* @param message - Received message
|
|
631
|
+
*/
|
|
632
|
+
async receiveMessage(message: Message): Promise<void> {
|
|
633
|
+
this.messageQueue.push(message);
|
|
634
|
+
|
|
635
|
+
this.emit('message-received', {
|
|
636
|
+
workerId: this.id,
|
|
637
|
+
message,
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
// Process message
|
|
641
|
+
await this.processMessage(message);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// ===== Protected Hook Methods =====
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Hook called during initialization
|
|
648
|
+
* Override in subclasses for custom initialization
|
|
649
|
+
*/
|
|
650
|
+
protected async onInitialize(): Promise<void> {
|
|
651
|
+
// Default: no-op
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Hook called during shutdown
|
|
656
|
+
* Override in subclasses for custom cleanup
|
|
657
|
+
*/
|
|
658
|
+
protected async onShutdown(): Promise<void> {
|
|
659
|
+
// Default: no-op
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Process a received message
|
|
664
|
+
* Override in subclasses for custom message handling
|
|
665
|
+
*
|
|
666
|
+
* @param message - Message to process
|
|
667
|
+
*/
|
|
668
|
+
protected async processMessage(message: Message): Promise<void> {
|
|
669
|
+
// Default: emit event for external handling
|
|
670
|
+
this.emit('message-process', { workerId: this.id, message });
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// ===== Private Methods =====
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Initialize memory integration
|
|
677
|
+
*/
|
|
678
|
+
private async initializeMemory(): Promise<void> {
|
|
679
|
+
if (!this.memoryBankId) {
|
|
680
|
+
this.memoryBankId = `memory_${this.id}_${Date.now()}`;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
this.emit('memory-initialized', {
|
|
684
|
+
workerId: this.id,
|
|
685
|
+
memoryBankId: this.memoryBankId,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Initialize coordination
|
|
691
|
+
*/
|
|
692
|
+
private async initializeCoordination(): Promise<void> {
|
|
693
|
+
this.emit('coordination-initialized', {
|
|
694
|
+
workerId: this.id,
|
|
695
|
+
protocol: this.config.coordination?.protocol || 'direct',
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Ensure worker is initialized
|
|
701
|
+
*/
|
|
702
|
+
protected ensureInitialized(): void {
|
|
703
|
+
if (!this.initialized) {
|
|
704
|
+
throw new Error(`Worker ${this.id} not initialized. Call initialize() first.`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Update metrics for successful task
|
|
710
|
+
*/
|
|
711
|
+
private updateMetricsSuccess(duration: number, tokensUsed?: number): void {
|
|
712
|
+
this.metrics.tasksExecuted++;
|
|
713
|
+
this.metrics.tasksSucceeded++;
|
|
714
|
+
|
|
715
|
+
// Update average duration
|
|
716
|
+
const total = this.metrics.avgDuration * (this.metrics.tasksSucceeded - 1) + duration;
|
|
717
|
+
this.metrics.avgDuration = total / this.metrics.tasksSucceeded;
|
|
718
|
+
|
|
719
|
+
if (tokensUsed) {
|
|
720
|
+
this.metrics.totalTokensUsed += tokensUsed;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Update metrics for failed task
|
|
726
|
+
*/
|
|
727
|
+
private updateMetricsFailure(duration: number): void {
|
|
728
|
+
this.metrics.tasksExecuted++;
|
|
729
|
+
this.metrics.tasksFailed++;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Generate default embedding from capabilities
|
|
734
|
+
*/
|
|
735
|
+
private generateDefaultEmbedding(): Float32Array {
|
|
736
|
+
// Create a simple hash-based embedding from capabilities
|
|
737
|
+
const dimension = 64;
|
|
738
|
+
const embedding = new Float32Array(dimension);
|
|
739
|
+
|
|
740
|
+
for (const cap of this.capabilities) {
|
|
741
|
+
const hash = this.hashString(cap);
|
|
742
|
+
for (let i = 0; i < dimension; i++) {
|
|
743
|
+
embedding[i] += ((hash >> (i % 32)) & 1) ? 0.1 : -0.1;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Normalize
|
|
748
|
+
const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));
|
|
749
|
+
if (norm > 0) {
|
|
750
|
+
for (let i = 0; i < dimension; i++) {
|
|
751
|
+
embedding[i] /= norm;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return embedding;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Calculate cosine similarity between two vectors
|
|
760
|
+
*/
|
|
761
|
+
private cosineSimilarity(a: Float32Array, b: Float32Array): number {
|
|
762
|
+
if (a.length !== b.length) {
|
|
763
|
+
// Pad shorter array or use minimum length
|
|
764
|
+
const minLen = Math.min(a.length, b.length);
|
|
765
|
+
a = a.slice(0, minLen);
|
|
766
|
+
b = b.slice(0, minLen);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
let dot = 0;
|
|
770
|
+
let normA = 0;
|
|
771
|
+
let normB = 0;
|
|
772
|
+
|
|
773
|
+
for (let i = 0; i < a.length; i++) {
|
|
774
|
+
dot += a[i] * b[i];
|
|
775
|
+
normA += a[i] * a[i];
|
|
776
|
+
normB += b[i] * b[i];
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
780
|
+
return denominator > 0 ? dot / denominator : 0;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Simple string hash function
|
|
785
|
+
*/
|
|
786
|
+
private hashString(str: string): number {
|
|
787
|
+
let hash = 0;
|
|
788
|
+
for (let i = 0; i < str.length; i++) {
|
|
789
|
+
const char = str.charCodeAt(i);
|
|
790
|
+
hash = ((hash << 5) - hash) + char;
|
|
791
|
+
hash = hash & hash;
|
|
792
|
+
}
|
|
793
|
+
return hash;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Estimate memory usage in MB
|
|
798
|
+
*/
|
|
799
|
+
private estimateMemoryUsage(): number {
|
|
800
|
+
// Base estimate: 2MB + 100KB per capability + queue size
|
|
801
|
+
const base = 2;
|
|
802
|
+
const capabilityOverhead = this.capabilities.length * 0.1;
|
|
803
|
+
const queueOverhead = this.messageQueue.length * 0.01;
|
|
804
|
+
return base + capabilityOverhead + queueOverhead;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Create a worker with the given configuration
|
|
810
|
+
*
|
|
811
|
+
* @param config - Worker configuration
|
|
812
|
+
* @param ExecutorClass - Worker class to instantiate
|
|
813
|
+
* @returns Initialized worker instance
|
|
814
|
+
*/
|
|
815
|
+
export async function createWorker<T extends WorkerBase>(
|
|
816
|
+
config: WorkerConfig,
|
|
817
|
+
ExecutorClass: new (config: WorkerConfig) => T
|
|
818
|
+
): Promise<T> {
|
|
819
|
+
const worker = new ExecutorClass(config);
|
|
820
|
+
await worker.initialize();
|
|
821
|
+
return worker;
|
|
822
|
+
}
|