@ruvector/edge-net 0.1.4 → 0.1.6

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.
@@ -0,0 +1,739 @@
1
+ /**
2
+ * @ruvector/edge-net REAL Workflow Orchestration
3
+ *
4
+ * Actually functional workflow system with:
5
+ * - Real LLM agent execution for each step
6
+ * - Real dependency resolution
7
+ * - Real parallel/sequential execution
8
+ * - Real result aggregation
9
+ *
10
+ * @module @ruvector/edge-net/real-workflows
11
+ */
12
+
13
+ import { EventEmitter } from 'events';
14
+ import { randomBytes } from 'crypto';
15
+ import { RealAgentManager, LLMClient } from './real-agents.js';
16
+ import { RealWorkerPool } from './real-workers.js';
17
+
18
+ // ============================================
19
+ // WORKFLOW STEP TYPES
20
+ // ============================================
21
+
22
+ export const StepTypes = {
23
+ AGENT: 'agent', // LLM agent execution
24
+ WORKER: 'worker', // Worker pool execution
25
+ PARALLEL: 'parallel', // Parallel sub-steps
26
+ SEQUENTIAL: 'sequential', // Sequential sub-steps
27
+ CONDITION: 'condition', // Conditional branching
28
+ TRANSFORM: 'transform', // Data transformation
29
+ AGGREGATE: 'aggregate', // Result aggregation
30
+ };
31
+
32
+ // ============================================
33
+ // WORKFLOW TEMPLATES
34
+ // ============================================
35
+
36
+ export const WorkflowTemplates = {
37
+ 'code-review': {
38
+ name: 'Code Review',
39
+ description: 'Comprehensive code review with multiple agents',
40
+ steps: [
41
+ {
42
+ id: 'analyze',
43
+ type: 'agent',
44
+ agentType: 'analyst',
45
+ prompt: 'Analyze the code structure and identify key components: {{input}}',
46
+ },
47
+ {
48
+ id: 'review-quality',
49
+ type: 'agent',
50
+ agentType: 'reviewer',
51
+ prompt: 'Review code quality, best practices, and potential issues based on analysis: {{analyze.output}}',
52
+ dependsOn: ['analyze'],
53
+ },
54
+ {
55
+ id: 'review-security',
56
+ type: 'agent',
57
+ agentType: 'reviewer',
58
+ prompt: 'Review security vulnerabilities and concerns: {{input}}',
59
+ },
60
+ {
61
+ id: 'suggestions',
62
+ type: 'agent',
63
+ agentType: 'coder',
64
+ prompt: 'Provide specific code improvement suggestions based on reviews:\nQuality: {{review-quality.output}}\nSecurity: {{review-security.output}}',
65
+ dependsOn: ['review-quality', 'review-security'],
66
+ },
67
+ ],
68
+ },
69
+
70
+ 'feature-dev': {
71
+ name: 'Feature Development',
72
+ description: 'End-to-end feature development workflow',
73
+ steps: [
74
+ {
75
+ id: 'research',
76
+ type: 'agent',
77
+ agentType: 'researcher',
78
+ prompt: 'Research requirements and best practices for: {{input}}',
79
+ },
80
+ {
81
+ id: 'design',
82
+ type: 'agent',
83
+ agentType: 'analyst',
84
+ prompt: 'Design the architecture and approach based on research: {{research.output}}',
85
+ dependsOn: ['research'],
86
+ },
87
+ {
88
+ id: 'implement',
89
+ type: 'agent',
90
+ agentType: 'coder',
91
+ prompt: 'Implement the feature based on design: {{design.output}}',
92
+ dependsOn: ['design'],
93
+ },
94
+ {
95
+ id: 'test',
96
+ type: 'agent',
97
+ agentType: 'tester',
98
+ prompt: 'Write tests for the implementation: {{implement.output}}',
99
+ dependsOn: ['implement'],
100
+ },
101
+ {
102
+ id: 'review',
103
+ type: 'agent',
104
+ agentType: 'reviewer',
105
+ prompt: 'Final review of implementation and tests:\nCode: {{implement.output}}\nTests: {{test.output}}',
106
+ dependsOn: ['implement', 'test'],
107
+ },
108
+ ],
109
+ },
110
+
111
+ 'bug-fix': {
112
+ name: 'Bug Fix',
113
+ description: 'Systematic bug investigation and fix workflow',
114
+ steps: [
115
+ {
116
+ id: 'investigate',
117
+ type: 'agent',
118
+ agentType: 'analyst',
119
+ prompt: 'Investigate the bug and identify root cause: {{input}}',
120
+ },
121
+ {
122
+ id: 'fix',
123
+ type: 'agent',
124
+ agentType: 'coder',
125
+ prompt: 'Implement the fix for: {{investigate.output}}',
126
+ dependsOn: ['investigate'],
127
+ },
128
+ {
129
+ id: 'test',
130
+ type: 'agent',
131
+ agentType: 'tester',
132
+ prompt: 'Write regression tests to prevent recurrence: {{fix.output}}',
133
+ dependsOn: ['fix'],
134
+ },
135
+ {
136
+ id: 'verify',
137
+ type: 'agent',
138
+ agentType: 'reviewer',
139
+ prompt: 'Verify the fix is complete and correct:\nFix: {{fix.output}}\nTests: {{test.output}}',
140
+ dependsOn: ['fix', 'test'],
141
+ },
142
+ ],
143
+ },
144
+
145
+ 'optimization': {
146
+ name: 'Performance Optimization',
147
+ description: 'Performance analysis and optimization workflow',
148
+ steps: [
149
+ {
150
+ id: 'profile',
151
+ type: 'agent',
152
+ agentType: 'optimizer',
153
+ prompt: 'Profile and identify performance bottlenecks: {{input}}',
154
+ },
155
+ {
156
+ id: 'analyze',
157
+ type: 'agent',
158
+ agentType: 'analyst',
159
+ prompt: 'Analyze profiling results and prioritize optimizations: {{profile.output}}',
160
+ dependsOn: ['profile'],
161
+ },
162
+ {
163
+ id: 'optimize',
164
+ type: 'agent',
165
+ agentType: 'coder',
166
+ prompt: 'Implement optimizations based on analysis: {{analyze.output}}',
167
+ dependsOn: ['analyze'],
168
+ },
169
+ {
170
+ id: 'benchmark',
171
+ type: 'agent',
172
+ agentType: 'tester',
173
+ prompt: 'Benchmark optimized code and compare: {{optimize.output}}',
174
+ dependsOn: ['optimize'],
175
+ },
176
+ ],
177
+ },
178
+
179
+ 'research': {
180
+ name: 'Research',
181
+ description: 'Deep research and analysis workflow',
182
+ steps: [
183
+ {
184
+ id: 'gather',
185
+ type: 'agent',
186
+ agentType: 'researcher',
187
+ prompt: 'Gather information and sources on: {{input}}',
188
+ },
189
+ {
190
+ id: 'analyze',
191
+ type: 'agent',
192
+ agentType: 'analyst',
193
+ prompt: 'Analyze gathered information: {{gather.output}}',
194
+ dependsOn: ['gather'],
195
+ },
196
+ {
197
+ id: 'synthesize',
198
+ type: 'agent',
199
+ agentType: 'researcher',
200
+ prompt: 'Synthesize findings into actionable insights: {{analyze.output}}',
201
+ dependsOn: ['analyze'],
202
+ },
203
+ ],
204
+ },
205
+ };
206
+
207
+ // ============================================
208
+ // WORKFLOW STEP
209
+ // ============================================
210
+
211
+ class WorkflowStep {
212
+ constructor(config) {
213
+ this.id = config.id;
214
+ this.type = config.type || StepTypes.AGENT;
215
+ this.agentType = config.agentType;
216
+ this.prompt = config.prompt;
217
+ this.dependsOn = config.dependsOn || [];
218
+ this.options = config.options || {};
219
+ this.subSteps = config.subSteps || [];
220
+ this.condition = config.condition;
221
+ this.transform = config.transform;
222
+
223
+ this.status = 'pending';
224
+ this.output = null;
225
+ this.error = null;
226
+ this.startTime = null;
227
+ this.endTime = null;
228
+ }
229
+
230
+ /**
231
+ * Interpolate template variables
232
+ */
233
+ interpolate(template, context) {
234
+ return template.replace(/\{\{(\w+(?:\.\w+)?)\}\}/g, (match, path) => {
235
+ const parts = path.split('.');
236
+ let value = context;
237
+
238
+ for (const part of parts) {
239
+ if (value && typeof value === 'object') {
240
+ value = value[part];
241
+ } else {
242
+ return match; // Keep original if not found
243
+ }
244
+ }
245
+
246
+ if (typeof value === 'object') {
247
+ return JSON.stringify(value, null, 2);
248
+ }
249
+
250
+ return value !== undefined ? String(value) : match;
251
+ });
252
+ }
253
+
254
+ getInfo() {
255
+ return {
256
+ id: this.id,
257
+ type: this.type,
258
+ status: this.status,
259
+ duration: this.endTime && this.startTime ? this.endTime - this.startTime : null,
260
+ dependsOn: this.dependsOn,
261
+ hasOutput: this.output !== null,
262
+ error: this.error,
263
+ };
264
+ }
265
+ }
266
+
267
+ // ============================================
268
+ // REAL WORKFLOW ORCHESTRATOR
269
+ // ============================================
270
+
271
+ /**
272
+ * Real workflow orchestrator with actual LLM execution
273
+ */
274
+ export class RealWorkflowOrchestrator extends EventEmitter {
275
+ constructor(options = {}) {
276
+ super();
277
+ this.agentManager = null;
278
+ this.workerPool = null;
279
+ this.workflows = new Map();
280
+ this.options = options;
281
+
282
+ this.stats = {
283
+ workflowsCompleted: 0,
284
+ workflowsFailed: 0,
285
+ stepsExecuted: 0,
286
+ totalDuration: 0,
287
+ };
288
+ }
289
+
290
+ /**
291
+ * Initialize orchestrator
292
+ */
293
+ async initialize() {
294
+ // Initialize agent manager for LLM execution
295
+ this.agentManager = new RealAgentManager({
296
+ provider: this.options.provider || 'anthropic',
297
+ apiKey: this.options.apiKey,
298
+ });
299
+ await this.agentManager.initialize();
300
+
301
+ // Initialize worker pool for compute tasks
302
+ this.workerPool = new RealWorkerPool({ size: 4 });
303
+ await this.workerPool.initialize();
304
+
305
+ return this;
306
+ }
307
+
308
+ /**
309
+ * Create workflow from template or custom definition
310
+ */
311
+ createWorkflow(nameOrConfig, customTask = null) {
312
+ let config;
313
+
314
+ if (typeof nameOrConfig === 'string') {
315
+ const template = WorkflowTemplates[nameOrConfig];
316
+ if (!template) {
317
+ throw new Error(`Unknown workflow template: ${nameOrConfig}`);
318
+ }
319
+ config = {
320
+ ...template,
321
+ input: customTask,
322
+ };
323
+ } else {
324
+ config = nameOrConfig;
325
+ }
326
+
327
+ const workflow = {
328
+ id: `wf-${randomBytes(6).toString('hex')}`,
329
+ name: config.name,
330
+ description: config.description,
331
+ input: config.input,
332
+ steps: config.steps.map(s => new WorkflowStep(s)),
333
+ status: 'created',
334
+ results: {},
335
+ startTime: null,
336
+ endTime: null,
337
+ error: null,
338
+ };
339
+
340
+ this.workflows.set(workflow.id, workflow);
341
+ this.emit('workflow-created', { workflowId: workflow.id, name: workflow.name });
342
+
343
+ return workflow;
344
+ }
345
+
346
+ /**
347
+ * Execute a workflow
348
+ */
349
+ async executeWorkflow(workflowId) {
350
+ const workflow = this.workflows.get(workflowId);
351
+ if (!workflow) {
352
+ throw new Error(`Workflow not found: ${workflowId}`);
353
+ }
354
+
355
+ workflow.status = 'running';
356
+ workflow.startTime = Date.now();
357
+ workflow.results = { input: workflow.input };
358
+
359
+ this.emit('workflow-start', { workflowId, name: workflow.name });
360
+
361
+ try {
362
+ // Build dependency graph
363
+ const graph = this.buildDependencyGraph(workflow.steps);
364
+
365
+ // Execute steps respecting dependencies
366
+ await this.executeSteps(workflow, graph);
367
+
368
+ workflow.status = 'completed';
369
+ workflow.endTime = Date.now();
370
+
371
+ const duration = workflow.endTime - workflow.startTime;
372
+ this.stats.workflowsCompleted++;
373
+ this.stats.totalDuration += duration;
374
+
375
+ this.emit('workflow-complete', {
376
+ workflowId,
377
+ duration,
378
+ results: workflow.results,
379
+ });
380
+
381
+ return {
382
+ workflowId,
383
+ status: 'completed',
384
+ duration,
385
+ results: workflow.results,
386
+ steps: workflow.steps.map(s => s.getInfo()),
387
+ };
388
+
389
+ } catch (error) {
390
+ workflow.status = 'failed';
391
+ workflow.error = error.message;
392
+ workflow.endTime = Date.now();
393
+
394
+ this.stats.workflowsFailed++;
395
+
396
+ this.emit('workflow-error', { workflowId, error: error.message });
397
+
398
+ throw error;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Build dependency graph
404
+ */
405
+ buildDependencyGraph(steps) {
406
+ const graph = new Map();
407
+ const stepMap = new Map();
408
+
409
+ for (const step of steps) {
410
+ stepMap.set(step.id, step);
411
+ graph.set(step.id, new Set(step.dependsOn));
412
+ }
413
+
414
+ return { graph, stepMap };
415
+ }
416
+
417
+ /**
418
+ * Execute steps respecting dependencies
419
+ */
420
+ async executeSteps(workflow, { graph, stepMap }) {
421
+ const completed = new Set();
422
+ const running = new Map();
423
+
424
+ const isReady = (stepId) => {
425
+ const deps = graph.get(stepId);
426
+ return [...deps].every(d => completed.has(d));
427
+ };
428
+
429
+ const getReadySteps = () => {
430
+ const ready = [];
431
+ for (const [stepId, deps] of graph) {
432
+ if (!completed.has(stepId) && !running.has(stepId) && isReady(stepId)) {
433
+ ready.push(stepMap.get(stepId));
434
+ }
435
+ }
436
+ return ready;
437
+ };
438
+
439
+ while (completed.size < stepMap.size) {
440
+ const readySteps = getReadySteps();
441
+
442
+ if (readySteps.length === 0 && running.size === 0) {
443
+ throw new Error('Workflow deadlock: no steps ready and none running');
444
+ }
445
+
446
+ // Execute ready steps in parallel
447
+ for (const step of readySteps) {
448
+ const promise = this.executeStep(step, workflow.results)
449
+ .then(result => {
450
+ workflow.results[step.id] = { output: result };
451
+ completed.add(step.id);
452
+ running.delete(step.id);
453
+ this.stats.stepsExecuted++;
454
+ })
455
+ .catch(error => {
456
+ step.error = error.message;
457
+ step.status = 'failed';
458
+ throw error;
459
+ });
460
+
461
+ running.set(step.id, promise);
462
+ }
463
+
464
+ // Wait for at least one to complete
465
+ if (running.size > 0) {
466
+ await Promise.race(running.values());
467
+ }
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Execute a single step
473
+ */
474
+ async executeStep(step, context) {
475
+ step.status = 'running';
476
+ step.startTime = Date.now();
477
+
478
+ this.emit('step-start', { stepId: step.id, type: step.type });
479
+
480
+ try {
481
+ let result;
482
+
483
+ switch (step.type) {
484
+ case StepTypes.AGENT:
485
+ result = await this.executeAgentStep(step, context);
486
+ break;
487
+
488
+ case StepTypes.WORKER:
489
+ result = await this.executeWorkerStep(step, context);
490
+ break;
491
+
492
+ case StepTypes.PARALLEL:
493
+ result = await this.executeParallelStep(step, context);
494
+ break;
495
+
496
+ case StepTypes.SEQUENTIAL:
497
+ result = await this.executeSequentialStep(step, context);
498
+ break;
499
+
500
+ case StepTypes.TRANSFORM:
501
+ result = await this.executeTransformStep(step, context);
502
+ break;
503
+
504
+ case StepTypes.CONDITION:
505
+ result = await this.executeConditionStep(step, context);
506
+ break;
507
+
508
+ case StepTypes.AGGREGATE:
509
+ result = await this.executeAggregateStep(step, context);
510
+ break;
511
+
512
+ default:
513
+ throw new Error(`Unknown step type: ${step.type}`);
514
+ }
515
+
516
+ step.output = result;
517
+ step.status = 'completed';
518
+ step.endTime = Date.now();
519
+
520
+ this.emit('step-complete', {
521
+ stepId: step.id,
522
+ duration: step.endTime - step.startTime,
523
+ });
524
+
525
+ return result;
526
+
527
+ } catch (error) {
528
+ step.status = 'failed';
529
+ step.error = error.message;
530
+ step.endTime = Date.now();
531
+
532
+ this.emit('step-error', { stepId: step.id, error: error.message });
533
+ throw error;
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Execute agent step with real LLM
539
+ */
540
+ async executeAgentStep(step, context) {
541
+ const prompt = step.interpolate(step.prompt, context);
542
+
543
+ const result = await this.agentManager.quickExecute(
544
+ step.agentType || 'coder',
545
+ prompt,
546
+ {
547
+ model: step.options.model || 'balanced',
548
+ ...step.options,
549
+ }
550
+ );
551
+
552
+ return result.content;
553
+ }
554
+
555
+ /**
556
+ * Execute worker step
557
+ */
558
+ async executeWorkerStep(step, context) {
559
+ const data = step.interpolate(
560
+ JSON.stringify(step.options.data || context.input),
561
+ context
562
+ );
563
+
564
+ return this.workerPool.execute(
565
+ step.options.taskType || 'process',
566
+ JSON.parse(data),
567
+ step.options
568
+ );
569
+ }
570
+
571
+ /**
572
+ * Execute parallel sub-steps
573
+ */
574
+ async executeParallelStep(step, context) {
575
+ const subSteps = step.subSteps.map(s => new WorkflowStep(s));
576
+ const promises = subSteps.map(s => this.executeStep(s, context));
577
+ const results = await Promise.all(promises);
578
+
579
+ return results.reduce((acc, result, i) => {
580
+ acc[subSteps[i].id] = result;
581
+ return acc;
582
+ }, {});
583
+ }
584
+
585
+ /**
586
+ * Execute sequential sub-steps
587
+ */
588
+ async executeSequentialStep(step, context) {
589
+ const subSteps = step.subSteps.map(s => new WorkflowStep(s));
590
+ const results = {};
591
+
592
+ for (const subStep of subSteps) {
593
+ results[subStep.id] = await this.executeStep(subStep, { ...context, ...results });
594
+ }
595
+
596
+ return results;
597
+ }
598
+
599
+ /**
600
+ * Execute transform step
601
+ */
602
+ async executeTransformStep(step, context) {
603
+ const inputKey = step.options.input || 'input';
604
+ const input = context[inputKey]?.output || context[inputKey] || context.input;
605
+
606
+ if (step.transform) {
607
+ // Custom transform function as string
608
+ const fn = new Function('input', 'context', step.transform);
609
+ return fn(input, context);
610
+ }
611
+
612
+ // Default transforms
613
+ const transformType = step.options.transformType || 'identity';
614
+ switch (transformType) {
615
+ case 'json':
616
+ return JSON.parse(input);
617
+ case 'stringify':
618
+ return JSON.stringify(input);
619
+ case 'extract':
620
+ return input[step.options.key];
621
+ default:
622
+ return input;
623
+ }
624
+ }
625
+
626
+ /**
627
+ * Execute condition step
628
+ */
629
+ async executeConditionStep(step, context) {
630
+ const condition = step.interpolate(step.condition, context);
631
+
632
+ // Evaluate condition
633
+ const fn = new Function('context', `return ${condition}`);
634
+ const result = fn(context);
635
+
636
+ if (result && step.options.then) {
637
+ const thenStep = new WorkflowStep(step.options.then);
638
+ return this.executeStep(thenStep, context);
639
+ } else if (!result && step.options.else) {
640
+ const elseStep = new WorkflowStep(step.options.else);
641
+ return this.executeStep(elseStep, context);
642
+ }
643
+
644
+ return result;
645
+ }
646
+
647
+ /**
648
+ * Execute aggregate step
649
+ */
650
+ async executeAggregateStep(step, context) {
651
+ const keys = step.options.keys || Object.keys(context).filter(k => k !== 'input');
652
+ const aggregated = {};
653
+
654
+ for (const key of keys) {
655
+ if (context[key]) {
656
+ aggregated[key] = context[key].output || context[key];
657
+ }
658
+ }
659
+
660
+ if (step.options.format === 'summary') {
661
+ return Object.entries(aggregated)
662
+ .map(([k, v]) => `## ${k}\n${typeof v === 'string' ? v : JSON.stringify(v, null, 2)}`)
663
+ .join('\n\n');
664
+ }
665
+
666
+ return aggregated;
667
+ }
668
+
669
+ /**
670
+ * Run workflow by template name
671
+ */
672
+ async run(templateName, input, options = {}) {
673
+ const workflow = this.createWorkflow(templateName, input);
674
+ return this.executeWorkflow(workflow.id);
675
+ }
676
+
677
+ /**
678
+ * Run custom workflow
679
+ */
680
+ async runCustom(config) {
681
+ const workflow = this.createWorkflow(config);
682
+ return this.executeWorkflow(workflow.id);
683
+ }
684
+
685
+ /**
686
+ * Get workflow status
687
+ */
688
+ getWorkflow(workflowId) {
689
+ const workflow = this.workflows.get(workflowId);
690
+ if (!workflow) return null;
691
+
692
+ return {
693
+ id: workflow.id,
694
+ name: workflow.name,
695
+ status: workflow.status,
696
+ steps: workflow.steps.map(s => s.getInfo()),
697
+ duration: workflow.endTime && workflow.startTime
698
+ ? workflow.endTime - workflow.startTime
699
+ : null,
700
+ error: workflow.error,
701
+ };
702
+ }
703
+
704
+ /**
705
+ * Get orchestrator stats
706
+ */
707
+ getStats() {
708
+ return {
709
+ ...this.stats,
710
+ activeWorkflows: [...this.workflows.values()]
711
+ .filter(w => w.status === 'running').length,
712
+ agentManager: this.agentManager?.listAgents()?.length || 0,
713
+ workerPool: this.workerPool?.getStatus(),
714
+ };
715
+ }
716
+
717
+ /**
718
+ * Shutdown orchestrator
719
+ */
720
+ async shutdown() {
721
+ if (this.agentManager) {
722
+ await this.agentManager.close();
723
+ }
724
+ if (this.workerPool) {
725
+ await this.workerPool.shutdown();
726
+ }
727
+ }
728
+
729
+ // Alias for shutdown
730
+ async close() {
731
+ return this.shutdown();
732
+ }
733
+ }
734
+
735
+ // Export WorkflowStep (not exported with export class)
736
+ export { WorkflowStep };
737
+
738
+ // Default export
739
+ export default RealWorkflowOrchestrator;