@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.
@@ -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()`.