@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.
- package/package.json +12 -1
- package/real-agents.js +381 -12
- package/real-workers.js +970 -0
- package/real-workflows.js +739 -0
|
@@ -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;
|