@bratsos/workflow-engine 0.0.11
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 +986 -0
- package/package.json +112 -0
- package/skills/workflow-engine/SKILL.md +539 -0
- package/skills/workflow-engine/references/01-stage-definitions.md +535 -0
- package/skills/workflow-engine/references/02-workflow-builder.md +421 -0
- package/skills/workflow-engine/references/03-runtime-setup.md +497 -0
- package/skills/workflow-engine/references/04-ai-integration.md +587 -0
- package/skills/workflow-engine/references/05-persistence-setup.md +614 -0
- package/skills/workflow-engine/references/06-async-batch-stages.md +514 -0
- package/skills/workflow-engine/references/07-testing-patterns.md +546 -0
- package/skills/workflow-engine/references/08-common-patterns.md +503 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# WorkflowBuilder
|
|
2
|
+
|
|
3
|
+
Complete API for building type-safe workflows with sequential and parallel stages.
|
|
4
|
+
|
|
5
|
+
## Creating a Workflow
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { WorkflowBuilder } from "@bratsos/workflow-engine";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
const workflow = new WorkflowBuilder(
|
|
12
|
+
"workflow-id", // Unique identifier
|
|
13
|
+
"Workflow Name", // Display name
|
|
14
|
+
"Description", // Description
|
|
15
|
+
InputSchema, // Zod schema for workflow input
|
|
16
|
+
OutputSchema // Zod schema for final output
|
|
17
|
+
)
|
|
18
|
+
.pipe(stage1)
|
|
19
|
+
.pipe(stage2)
|
|
20
|
+
.build();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## WorkflowBuilder Methods
|
|
24
|
+
|
|
25
|
+
### pipe(stage)
|
|
26
|
+
|
|
27
|
+
Add a stage to execute sequentially after the previous stage.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
const workflow = new WorkflowBuilder(...)
|
|
31
|
+
.pipe(extractionStage) // Execution group 1
|
|
32
|
+
.pipe(processingStage) // Execution group 2
|
|
33
|
+
.pipe(outputStage) // Execution group 3
|
|
34
|
+
.build();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Each `.pipe()` call:
|
|
38
|
+
- Creates a new execution group
|
|
39
|
+
- Validates stage dependencies against existing stages
|
|
40
|
+
- Accumulates the stage output in the workflow context
|
|
41
|
+
|
|
42
|
+
### parallel(stages)
|
|
43
|
+
|
|
44
|
+
Add multiple stages that execute concurrently in the same execution group.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const workflow = new WorkflowBuilder(...)
|
|
48
|
+
.pipe(extractionStage) // Group 1
|
|
49
|
+
.parallel([classifyStage, summarizeStage]) // Group 2 (parallel)
|
|
50
|
+
.pipe(mergeStage) // Group 3
|
|
51
|
+
.build();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Parallel stages:
|
|
55
|
+
- Run in the same execution group
|
|
56
|
+
- Receive the same input (output from previous group)
|
|
57
|
+
- Must not depend on each other
|
|
58
|
+
|
|
59
|
+
### build()
|
|
60
|
+
|
|
61
|
+
Finalize and return the `Workflow` object.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const workflow = builder.build();
|
|
65
|
+
|
|
66
|
+
// Workflow is immutable after build()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Workflow Class Methods
|
|
70
|
+
|
|
71
|
+
### getExecutionPlan()
|
|
72
|
+
|
|
73
|
+
Returns stages grouped by execution order.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const plan = workflow.getExecutionPlan();
|
|
77
|
+
// [
|
|
78
|
+
// [{ stage: extractionStage, executionGroup: 1 }],
|
|
79
|
+
// [{ stage: classifyStage, executionGroup: 2 },
|
|
80
|
+
// { stage: summarizeStage, executionGroup: 2 }],
|
|
81
|
+
// [{ stage: mergeStage, executionGroup: 3 }]
|
|
82
|
+
// ]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### getStageIds()
|
|
86
|
+
|
|
87
|
+
Returns all stage IDs in execution order.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const ids = workflow.getStageIds();
|
|
91
|
+
// ["extraction", "classify", "summarize", "merge"]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### getStage(stageId)
|
|
95
|
+
|
|
96
|
+
Get a specific stage by ID.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const stage = workflow.getStage("extraction");
|
|
100
|
+
if (stage) {
|
|
101
|
+
console.log(stage.name);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### hasStage(stageId)
|
|
106
|
+
|
|
107
|
+
Check if a stage exists.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
if (workflow.hasStage("optional-stage")) {
|
|
111
|
+
// ...
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### getExecutionOrder()
|
|
116
|
+
|
|
117
|
+
Get a human-readable visualization of execution order.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
console.log(workflow.getExecutionOrder());
|
|
121
|
+
// Workflow: My Workflow (my-workflow)
|
|
122
|
+
// Total stages: 4
|
|
123
|
+
// Execution groups: 3
|
|
124
|
+
//
|
|
125
|
+
// Execution Order:
|
|
126
|
+
// ================
|
|
127
|
+
// 1. Data Extraction (extraction)
|
|
128
|
+
// Extracts data from documents
|
|
129
|
+
//
|
|
130
|
+
// 2. [PARALLEL]
|
|
131
|
+
// - Classification (classify)
|
|
132
|
+
// - Summarization (summarize)
|
|
133
|
+
//
|
|
134
|
+
// 3. Merge Results (merge)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### getStageConfigs()
|
|
138
|
+
|
|
139
|
+
Get configuration metadata for all stages.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const configs = workflow.getStageConfigs();
|
|
143
|
+
// {
|
|
144
|
+
// "extraction": {
|
|
145
|
+
// schema: ZodObject,
|
|
146
|
+
// defaults: { maxPages: 100 },
|
|
147
|
+
// name: "Data Extraction",
|
|
148
|
+
// description: "..."
|
|
149
|
+
// },
|
|
150
|
+
// ...
|
|
151
|
+
// }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### getDefaultConfig()
|
|
155
|
+
|
|
156
|
+
Generate default configuration for all stages.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const config = workflow.getDefaultConfig();
|
|
160
|
+
// {
|
|
161
|
+
// "extraction": { maxPages: 100, extractImages: false },
|
|
162
|
+
// "classify": { model: "gemini-2.5-flash" },
|
|
163
|
+
// ...
|
|
164
|
+
// }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### validateConfig(config)
|
|
168
|
+
|
|
169
|
+
Validate a configuration object.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const result = workflow.validateConfig({
|
|
173
|
+
extraction: { maxPages: -1 }, // Invalid!
|
|
174
|
+
classify: { model: "invalid" },
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (!result.valid) {
|
|
178
|
+
for (const error of result.errors) {
|
|
179
|
+
console.log(`${error.stageId}: ${error.error}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### estimateCost(input, config)
|
|
185
|
+
|
|
186
|
+
Estimate total workflow cost (if stages implement `estimateCost`).
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const cost = workflow.estimateCost(
|
|
190
|
+
{ documentUrl: "..." },
|
|
191
|
+
workflow.getDefaultConfig()
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### getStagesInExecutionGroup(groupIndex)
|
|
196
|
+
|
|
197
|
+
Get stages in a specific execution group.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const parallelStages = workflow.getStagesInExecutionGroup(2);
|
|
201
|
+
// [classifyStage, summarizeStage]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### getStageIndex(stageId)
|
|
205
|
+
|
|
206
|
+
Get the sequential index of a stage (0-based).
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const index = workflow.getStageIndex("classify");
|
|
210
|
+
// 1
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### getExecutionGroupIndex(stageId)
|
|
214
|
+
|
|
215
|
+
Get the execution group for a stage.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const group = workflow.getExecutionGroupIndex("classify");
|
|
219
|
+
// 2
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### getPreviousStageId(stageId)
|
|
223
|
+
|
|
224
|
+
Get the ID of the stage immediately before.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const prevId = workflow.getPreviousStageId("merge");
|
|
228
|
+
// "summarize" (last in previous group)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Type Inference
|
|
232
|
+
|
|
233
|
+
### InferWorkflowContext
|
|
234
|
+
|
|
235
|
+
Extract the accumulated context type from a workflow.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import type { InferWorkflowContext } from "@bratsos/workflow-engine";
|
|
239
|
+
|
|
240
|
+
type MyContext = InferWorkflowContext<typeof workflow>;
|
|
241
|
+
// {
|
|
242
|
+
// "extraction": ExtractionOutput;
|
|
243
|
+
// "classify": ClassifyOutput;
|
|
244
|
+
// "summarize": SummarizeOutput;
|
|
245
|
+
// }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### InferWorkflowInput / InferWorkflowOutput
|
|
249
|
+
|
|
250
|
+
Extract input/output types.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import type {
|
|
254
|
+
InferWorkflowInput,
|
|
255
|
+
InferWorkflowOutput,
|
|
256
|
+
} from "@bratsos/workflow-engine";
|
|
257
|
+
|
|
258
|
+
type Input = InferWorkflowInput<typeof workflow>;
|
|
259
|
+
type Output = InferWorkflowOutput<typeof workflow>;
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### InferWorkflowStageIds
|
|
263
|
+
|
|
264
|
+
Extract stage IDs as a union type.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import type { InferWorkflowStageIds } from "@bratsos/workflow-engine";
|
|
268
|
+
|
|
269
|
+
type StageId = InferWorkflowStageIds<typeof workflow>;
|
|
270
|
+
// "extraction" | "classify" | "summarize" | "merge"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### InferStageOutputById
|
|
274
|
+
|
|
275
|
+
Get the output type for a specific stage.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import type { InferStageOutputById } from "@bratsos/workflow-engine";
|
|
279
|
+
|
|
280
|
+
type ExtractionOutput = InferStageOutputById<typeof workflow, "extraction">;
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Dependency Validation
|
|
284
|
+
|
|
285
|
+
The builder validates dependencies at build time:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const stageA = defineStage({
|
|
289
|
+
id: "stage-a",
|
|
290
|
+
dependencies: ["stage-b"], // ERROR: stage-b doesn't exist yet!
|
|
291
|
+
// ...
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// This will throw an error:
|
|
295
|
+
new WorkflowBuilder(...)
|
|
296
|
+
.pipe(stageA) // Throws: "stage-a" has missing dependencies: stage-b
|
|
297
|
+
.pipe(stageB)
|
|
298
|
+
.build();
|
|
299
|
+
|
|
300
|
+
// Correct order:
|
|
301
|
+
new WorkflowBuilder(...)
|
|
302
|
+
.pipe(stageB) // Add dependency first
|
|
303
|
+
.pipe(stageA) // Now stage-a can depend on stage-b
|
|
304
|
+
.build();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Complete Example
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { WorkflowBuilder, defineStage } from "@bratsos/workflow-engine";
|
|
311
|
+
import { z } from "zod";
|
|
312
|
+
|
|
313
|
+
// Define stages
|
|
314
|
+
const extractStage = defineStage({
|
|
315
|
+
id: "extract",
|
|
316
|
+
name: "Extract Data",
|
|
317
|
+
schemas: {
|
|
318
|
+
input: z.object({ url: z.string() }),
|
|
319
|
+
output: z.object({ text: z.string(), metadata: z.any() }),
|
|
320
|
+
config: z.object({ maxLength: z.number().default(10000) }),
|
|
321
|
+
},
|
|
322
|
+
async execute(ctx) {
|
|
323
|
+
const data = await fetch(ctx.input.url).then(r => r.text());
|
|
324
|
+
return { output: { text: data.slice(0, ctx.config.maxLength), metadata: {} } };
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const classifyStage = defineStage({
|
|
329
|
+
id: "classify",
|
|
330
|
+
name: "Classify Content",
|
|
331
|
+
dependencies: ["extract"],
|
|
332
|
+
schemas: {
|
|
333
|
+
input: "none",
|
|
334
|
+
output: z.object({ categories: z.array(z.string()) }),
|
|
335
|
+
config: z.object({}),
|
|
336
|
+
},
|
|
337
|
+
async execute(ctx) {
|
|
338
|
+
const { text } = ctx.require("extract");
|
|
339
|
+
// Classification logic...
|
|
340
|
+
return { output: { categories: ["tech", "news"] } };
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const summarizeStage = defineStage({
|
|
345
|
+
id: "summarize",
|
|
346
|
+
name: "Summarize",
|
|
347
|
+
dependencies: ["extract"],
|
|
348
|
+
schemas: {
|
|
349
|
+
input: "none",
|
|
350
|
+
output: z.object({ summary: z.string() }),
|
|
351
|
+
config: z.object({ maxWords: z.number().default(100) }),
|
|
352
|
+
},
|
|
353
|
+
async execute(ctx) {
|
|
354
|
+
const { text } = ctx.require("extract");
|
|
355
|
+
// Summarization logic...
|
|
356
|
+
return { output: { summary: text.slice(0, 500) } };
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const mergeStage = defineStage({
|
|
361
|
+
id: "merge",
|
|
362
|
+
name: "Merge Results",
|
|
363
|
+
dependencies: ["classify", "summarize"],
|
|
364
|
+
schemas: {
|
|
365
|
+
input: "none",
|
|
366
|
+
output: z.object({
|
|
367
|
+
summary: z.string(),
|
|
368
|
+
categories: z.array(z.string()),
|
|
369
|
+
}),
|
|
370
|
+
config: z.object({}),
|
|
371
|
+
},
|
|
372
|
+
async execute(ctx) {
|
|
373
|
+
const classify = ctx.require("classify");
|
|
374
|
+
const summarize = ctx.require("summarize");
|
|
375
|
+
return {
|
|
376
|
+
output: {
|
|
377
|
+
summary: summarize.summary,
|
|
378
|
+
categories: classify.categories,
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Build workflow
|
|
385
|
+
const documentWorkflow = new WorkflowBuilder(
|
|
386
|
+
"document-analysis",
|
|
387
|
+
"Document Analysis",
|
|
388
|
+
"Analyzes documents: extracts, classifies, and summarizes",
|
|
389
|
+
z.object({ url: z.string().url() }),
|
|
390
|
+
z.object({ summary: z.string(), categories: z.array(z.string()) })
|
|
391
|
+
)
|
|
392
|
+
.pipe(extractStage)
|
|
393
|
+
.parallel([classifyStage, summarizeStage])
|
|
394
|
+
.pipe(mergeStage)
|
|
395
|
+
.build();
|
|
396
|
+
|
|
397
|
+
// Use the workflow
|
|
398
|
+
console.log(documentWorkflow.getExecutionOrder());
|
|
399
|
+
console.log("Default config:", documentWorkflow.getDefaultConfig());
|
|
400
|
+
|
|
401
|
+
// Validate custom config
|
|
402
|
+
const validation = documentWorkflow.validateConfig({
|
|
403
|
+
extract: { maxLength: 5000 },
|
|
404
|
+
summarize: { maxWords: 50 },
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
if (!validation.valid) {
|
|
408
|
+
console.error("Config errors:", validation.errors);
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Execution Flow
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
Input → [Group 1: extract] → [Group 2: classify, summarize (parallel)] → [Group 3: merge] → Output
|
|
416
|
+
↓ ↓ ↓ ↓
|
|
417
|
+
workflowContext: Gets extract Gets extract Gets both
|
|
418
|
+
{ extract: ... } output output outputs
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
The workflow context accumulates all stage outputs, making them available to subsequent stages via `ctx.require()` or `ctx.optional()`.
|