@swizzy_ai/kit 1.0.0 → 1.0.2

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.
Files changed (31) hide show
  1. package/README.md +835 -439
  2. package/dist/core/wizard/bungee/executor.d.ts +15 -0
  3. package/dist/core/wizard/bungee/executor.d.ts.map +1 -0
  4. package/dist/core/wizard/context-manager.d.ts +9 -0
  5. package/dist/core/wizard/context-manager.d.ts.map +1 -0
  6. package/dist/core/wizard/logger.d.ts +8 -0
  7. package/dist/core/wizard/logger.d.ts.map +1 -0
  8. package/dist/core/wizard/schema-utils.d.ts +16 -0
  9. package/dist/core/wizard/schema-utils.d.ts.map +1 -0
  10. package/dist/core/wizard/step-data-generator.d.ts +24 -0
  11. package/dist/core/wizard/step-data-generator.d.ts.map +1 -0
  12. package/dist/core/wizard/steps/base.d.ts +4 -3
  13. package/dist/core/wizard/steps/base.d.ts.map +1 -1
  14. package/dist/core/wizard/steps/compute.d.ts +2 -2
  15. package/dist/core/wizard/steps/compute.d.ts.map +1 -1
  16. package/dist/core/wizard/steps/text.d.ts +2 -2
  17. package/dist/core/wizard/steps/text.d.ts.map +1 -1
  18. package/dist/core/wizard/usage-tracker.d.ts +25 -0
  19. package/dist/core/wizard/usage-tracker.d.ts.map +1 -0
  20. package/dist/core/wizard/visualization-manager.d.ts +34 -0
  21. package/dist/core/wizard/visualization-manager.d.ts.map +1 -0
  22. package/dist/core/wizard/wizard.d.ts +39 -40
  23. package/dist/core/wizard/wizard.d.ts.map +1 -1
  24. package/dist/index.d.ts +46 -46
  25. package/dist/index.js +1207 -626
  26. package/dist/index.js.map +1 -1
  27. package/dist/services/client/index.d.ts +1 -0
  28. package/dist/services/client/index.d.ts.map +1 -1
  29. package/dist/services/client/providers.d.ts.map +1 -1
  30. package/dist/ui/wizard-visualizer.html +570 -385
  31. package/package.json +6 -2
package/README.md CHANGED
@@ -2,583 +2,980 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- <h1 style="font-size:2.5em; font-weight:bold; margin:1em 0;">
6
- <img
7
- src="https://img.shields.io/badge/Wizard-Framework-blue?style=for-the-badge&logo=magic&logoColor=white"
8
- alt="Wizard Framework"
9
- style="border-radius: 10px;"
10
- />
11
- </h1>
5
+ **A Framework for Designing LLM Experiences**
12
6
 
13
- **Type-Safe AI Workflow Framework**
14
-
15
- [![npm version](https://img.shields.io/badge/npm-1.0.0-blue.svg?style=flat&logo=npm)](https://www.npmjs.com/package/@swizzy_ai/kit)
7
+ [![npm version](https://img.shields.io/badge/npm-1.0.0-blue.svg)](https://www.npmjs.com/package/@swizzy_ai/kit)
16
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
17
- [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
18
- [![Node.js](https://img.shields.io/badge/Node.js-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
19
-
20
- [![GitHub stars](https://img.shields.io/github/stars/swizzy-ai/wizard-framework?style=social)](https://github.com/swizzy-ai/wizard-framework/stargazers)
21
- [![GitHub forks](https://img.shields.io/github/forks/swizzy-ai/wizard-framework?style=social)](https://github.com/swizzy-ai/wizard-framework/network/members)
22
- [![GitHub watchers](https://img.shields.io/github/watchers/swizzy-ai/wizard-framework?style=social)](https://github.com/swizzy-ai/wizard-framework/watchers)
23
9
 
24
- If you find Wizard Framework useful, please give it a star. It helps the community grow!
25
-
26
- *Structured Control • Type Safety First • Full Observability*
10
+ [Install](#installation) [Documentation](https://docs.wizard.dev) [Examples](#examples)
27
11
 
28
12
  </div>
29
13
 
30
14
  ---
31
15
 
32
- ## Project Status[![](./docs/img/pin.svg)](#project-status)
33
-
34
- <table>
35
- <tr>
36
- <td><img src="https://img.shields.io/badge/Build-Passing-brightgreen.svg?style=flat&logo=github-actions&logoColor=white" alt="Build Status"/></td>
37
- <td><img src="https://img.shields.io/badge/Tests-Passing-brightgreen.svg?style=flat&logo=jest&logoColor=white" alt="Test Status"/></td>
38
- <td><img src="https://img.shields.io/badge/Coverage-95%25-brightgreen.svg?style=flat&logo=codecov&logoColor=white" alt="Coverage"/></td>
39
- </tr>
40
- <tr>
41
- <td><img src="https://img.shields.io/badge/Solution-TypeScript-blue.svg?style=flat&logo=typescript&logoColor=white&labelColor=363D44" alt="TypeScript solution"/></td>
42
- <td><img src="https://img.shields.io/badge/Runtime-Node.js-blue?style=flat&logo=node.js&logoColor=white&labelColor=363D44" alt="Node.js runtime"/></td>
43
- <td><img src="https://img.shields.io/badge/AI-LLM%20Integration-blue?style=flat&logo=openai&logoColor=white&labelColor=363D44" alt="AI Integration"/></td>
44
- </tr>
45
- </table>
16
+ ## Why Wizard Exists
46
17
 
47
- ---
18
+ ### The Current State is Broken
19
+
20
+ Today's LLM orchestration assumes the **LLM itself is the agent** - an intelligent entity you hand tools to and hope it figures things out. This paradigm works brilliantly in controlled environments (see: Claude Code's success), but fails catastrophically when:
21
+
22
+ - The environment isn't perfectly curated
23
+ - Mistakes or hallucinations have real costs
24
+ - Context limits constrain what the model can see
25
+ - Each turn summons a "new spirit" with no memory of the last
26
+
27
+ The mental model is fundamentally flawed.
48
28
 
49
- ## Table of Contents[![](./docs/img/pin.svg)](#table-of-contents)
29
+ ### What Wizard Believes
50
30
 
51
- - [What is a Wizard?](#what-is-a-wizard)
52
- - [The 3 Core Pillars](#the-3-core-pillars)
53
- - [Installation](#installation)
54
- - [Core Components](#core-components)
55
- - [Feature Spotlight: Bungee Jumps](#feature-spotlight-bungee-jumps)
56
- - [Developer Experience](#developer-experience)
57
- - [API Reference](#api-reference)
58
- - [Examples](#examples)
59
- - [Contributing](#contributing)
60
- - [License](#license)
61
- - [Call to Action](#call-to-action)
31
+ **Real agency and intelligence are emergent products of process and design.**
62
32
 
63
- > [!IMPORTANT]
64
- > Full technical guidance for building, using, and integrating Wizard Framework is available in the [documentation](./docs/ "Wizard Framework documentation").
33
+ Wizard inverts the paradigm: the **developer is the orchestrator**, the **LLM is the user**. You design the experience - what information to present, what to collect, what happens next. The LLM focuses on what it does best: generating the most plausible text for each scenario.
34
+
35
+ This is agentic Lego bricks, not autonomous agents.
65
36
 
66
37
  ---
67
38
 
68
- ## What is a Wizard?[![](./docs/img/pin.svg)](#what-is-a-wizard)
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install @swizzy_ai/kit
43
+ ```
44
+
45
+ ### API Key Configuration
46
+
47
+ For multimodal functions and different LLM providers, set all your API keys as environment variables:
69
48
 
70
- A **Wizard** transforms AI workflows into deterministic state machines. Instead of chaining LLM calls haphazardly, Wizards provide structured control flow, type-safe data handling, and complete observability.
49
+ ```bash
50
+ # Include multiple API keys
51
+ OPENAI_API_KEY="your-openai-key"
52
+ ANTHROPIC_API_KEY="your-anthropic-key"
53
+ GEMINI_API_KEY="your-gemini-key"
54
+ ```
55
+
56
+ Or configure them programmatically:
71
57
 
72
58
  ```javascript
73
59
  const { Wizard, Models } = require('@swizzy_ai/kit');
74
60
 
75
61
  const wizard = new Wizard({
76
62
  id: 'my-workflow',
77
- onUsage: (usage, provider) => {
78
- console.log(`Used ${usage.totalTokens} tokens`);
79
- }
80
63
  });
81
64
  ```
82
65
 
83
- ### The 3 Core Pillars
66
+ ---
67
+
68
+ ## Core Concepts
69
+
70
+ ### Understanding Wizard's Architecture
84
71
 
85
- #### 🏗️ Structured Control
86
- Move beyond unstructured chains to finite state machines. Every step has a clear purpose, and flow control is explicit and predictable.
72
+ Wizard workflows operate on a simple execution loop:
73
+
74
+ ```
75
+ Step → LLM Generation → Update Function → Signal → Next Step
76
+ ```
77
+
78
+ You, the developer, design each step in this loop:
79
+ - **What the LLM sees** (via context functions)
80
+ - **What the LLM generates** (via instructions and schemas)
81
+ - **What happens next** (via signals)
82
+
83
+ The LLM never decides where to go or what to do - it only generates text based on what you show it and where to tell it. You orchestrate the intelligence.
84
+
85
+ ---
86
+
87
+ ### Context and State Management
88
+
89
+ **Context** is the shared state that flows through your entire workflow. Every step can read from it and write to it.
90
+
91
+ #### Setting Initial Context
87
92
 
88
93
  ```javascript
89
- wizard.addTextStep({
90
- id: 'analyze_sentiment',
91
- instruction: 'Analyze sentiment of: {{text}}',
92
- schema: z.object({
93
- sentiment: z.enum(['positive', 'negative', 'neutral']),
94
- confidence: z.number().min(0).max(1)
95
- }),
96
- model: Models.SWIZZY_DEFAULT,
94
+ wizard.setContext({
95
+ userQuestion: 'What are the key findings?',
96
+ documents: [
97
+ { title: 'Q1 Report', pages: 45, data: [...] },
98
+ { title: 'Q2 Report', pages: 52, data: [...] }
99
+ ],
100
+ processedCount: 0
101
+ });
102
+ ```
103
+
104
+ #### Reading Context
105
+
106
+ Context is available in every `update` function and `context`:
107
+
108
+ ```javascript
109
+ wizard.addComputeStep({
110
+ id: 'check_status',
97
111
  update: (result, context, actions) => {
98
- if (result.confidence < 0.8) {
99
- return actions.retry(); // Explicit error handling
100
- }
101
- return actions.next(); // Clear flow control
112
+ // Access any context property
113
+ console.log(`Processed ${context.processedCount} documents`);
114
+ return actions.next();
102
115
  }
103
116
  });
104
117
  ```
105
118
 
106
- #### 🔒 Type Safety First
107
- Leverage Zod schemas to ensure LLMs produce usable data structures, not just conversational text. Catch errors at runtime, not in production.
119
+ #### Updating Context
108
120
 
109
121
  ```javascript
110
- const AnalysisSchema = z.object({
111
- topics: z.array(z.string()),
112
- sentiment: z.enum(['positive', 'negative', 'neutral']),
113
- entities: z.array(z.object({
114
- name: z.string(),
115
- type: z.enum(['person', 'organization', 'location'])
116
- }))
122
+ wizard.addComputeStep({
123
+ id: 'increment_counter',
124
+ update: (result, context, actions) => {
125
+ actions.updateContext({
126
+ processedCount: context.processedCount + 1
127
+ });
128
+ return actions.next();
129
+ }
117
130
  });
131
+ ```
118
132
 
133
+ #### Context Functions: Controlling What the LLM Sees
134
+
135
+ The `context` transforms your full context into a focused view for the LLM. This is where you design the information architecture.
136
+
137
+ ```javascript
119
138
  wizard.addTextStep({
120
- id: 'analyze_content',
121
- instruction: 'Analyze this content: {{content}}',
122
- schema: AnalysisSchema,
139
+ id: 'analyze',
140
+ instruction: 'Analyze these documents:\n{{formattedDocs}}',
141
+ context: (context) => ({
142
+ // Transform complex data into LLM-friendly format
143
+ formattedDocs: context.documents
144
+ .map((doc, i) => `${i + 1}. ${doc.title} (${doc.pages} pages)`)
145
+ .join('\n'),
146
+ // Only expose what's needed
147
+ totalDocs: context.documents.length
148
+ }),
149
+ schema: z.object({
150
+ insights: z.array(z.string())
151
+ }),
123
152
  model: Models.SWIZZY_DEFAULT,
124
- update: (validatedData, context, actions) => {
125
- // validatedData is fully typed - no runtime surprises
126
- console.log(validatedData.sentiment); // TypeScript knows this exists
153
+ update: (result, context, actions) => {
154
+ actions.updateContext({ insights: result.insights });
127
155
  return actions.next();
128
156
  }
129
157
  });
130
158
  ```
131
159
 
132
- #### 👁️ Full Observability
133
- If you can't see your workflow executing in real-time, you can't debug it. Our visualization layer makes every step, token, and decision transparent.
160
+ **Why This Matters**:
161
+ - Keep token usage low by showing only relevant data
162
+ - Present information in the optimal format
163
+ - Prevent the LLM from seeing sensitive or confusing data
164
+ - Design the exact "user interface" the LLM experiences
134
165
 
135
- ```javascript
136
- // Start real-time visualization
137
- const { server, url } = await wizard.visualize(3000);
138
- console.log(`Open ${url} to watch your workflow execute`);
166
+ ---
139
167
 
140
- // Features:
141
- // - Live step execution tracking
142
- // - Token usage per step
143
- // - Context changes in real-time
144
- // - Interactive pause/resume controls
145
- ```
168
+ ### Steps: The Building Blocks
146
169
 
147
- ---
170
+ A **Step** is the fundamental unit of a Wizard workflow. Each step defines a single action in your process.
148
171
 
149
- ## Installation
172
+ #### Step Anatomy
150
173
 
151
- ```bash
152
- npm install @swizzy_ai/kit
153
- # or
154
- yarn add @swizzy_ai/kit
155
- # or
156
- pnpm add @swizzy_ai/kit
157
- ```
174
+ Every step has five components:
158
175
 
159
- ---
176
+ 1. **ID**: Unique identifier for the step
177
+ 2. **Type**: What kind of step (text, structured, or compute)
178
+ 3. **Instruction**: What to ask the LLM (for LLM steps)
179
+ 4. **Context Function**: What information to expose (optional)
180
+ 5. **Update Function**: How to handle the result and what to do next
181
+
182
+ #### Step Variants
160
183
 
161
- ## Core Components
184
+ Choose the right step type for your task:
162
185
 
163
- ### Shared Memory (Context)
186
+ **Normal Step (Structured Data)**
164
187
 
165
- Context is the persistent data store that all steps can read from and write to. Think of it as a shared JavaScript object that maintains state throughout the workflow.
188
+ Use when you need the LLM to generate data matching a specific schema:
166
189
 
167
190
  ```javascript
168
- // Initialize context
169
- wizard.setContext({
170
- userName: 'Alice',
171
- documents: ['doc1.pdf', 'doc2.pdf'],
172
- currentStep: 0
191
+ wizard.addStep({
192
+ id: 'extract_entities',
193
+ instruction: 'Extract entities from: {{text}}',
194
+ schema: z.object({
195
+ people: z.array(z.string()),
196
+ organizations: z.array(z.string()),
197
+ locations: z.array(z.string())
198
+ }),
199
+ model: Models.SWIZZY_DEFAULT,
200
+ update: (result, context, actions) => {
201
+ // result is guaranteed to match schema
202
+ actions.updateContext({ entities: result });
203
+ return actions.next();
204
+ }
173
205
  });
206
+ ```
207
+
208
+ **Text Step (Simple Text)**
209
+
210
+ Use when you just need a text response without structured data:
174
211
 
175
- // Steps can read context
212
+ ```javascript
176
213
  wizard.addTextStep({
177
- id: 'greet_user',
178
- instruction: 'Greet {{userName}} and mention they have {{documents.length}} documents',
179
- // ...
214
+ id: 'summarize',
215
+ instruction: 'Summarize: {{document}}',
216
+ model: Models.SWIZZY_DEFAULT,
217
+ update: (text, context, actions) => {
218
+ actions.updateContext({ summary: text });
219
+ return actions.next();
220
+ }
180
221
  });
222
+ ```
223
+
224
+ **Compute Step (Non-LLM Logic)**
225
+
226
+ Use for calculations, API calls, validation, or any logic that doesn't need an LLM:
181
227
 
182
- // Steps can write to context
228
+ ```javascript
183
229
  wizard.addComputeStep({
184
- id: 'process_docs',
230
+ id: 'validate',
185
231
  update: (result, context, actions) => {
186
- actions.updateContext({
187
- processedCount: context.documents.length,
188
- status: 'completed'
232
+ if (context.entities.people.length === 0) {
233
+ return actions.retry(); // No entities found, retry
234
+ }
235
+
236
+ // Call external API
237
+ const enrichedData = await fetch(`/api/enrich`, {
238
+ method: 'POST',
239
+ body: JSON.stringify(context.entities)
189
240
  });
241
+
242
+ actions.updateContext({ enrichedData });
190
243
  return actions.next();
191
244
  }
192
245
  });
193
246
  ```
194
247
 
195
- #### Template Variables
196
- Use `{{variable}}` syntax for simple value injection:
248
+ ---
249
+
250
+ ### Instructions: Prompt Templates
251
+
252
+ The **instruction** is the prompt shown to the LLM. It uses a template engine that replaces `{{variables}}` with data from your context function.
253
+
254
+ #### Basic Template Syntax
197
255
 
198
256
  ```javascript
199
257
  wizard.addTextStep({
200
- id: 'personalize',
201
- instruction: 'Hello {{userName}}, you are {{userAge}} years old',
202
- // ...
258
+ id: 'greet_user',
259
+ instruction: 'Greet {{userName}} who has {{documentCount}} documents',
260
+ context: (context) => ({
261
+ userName: context.user.name,
262
+ documentCount: context.user.documents.length
263
+ }),
264
+ model: Models.SWIZZY_DEFAULT,
265
+ update: (text, context, actions) => {
266
+ console.log(text); // "Hello Alice! I see you have 5 documents."
267
+ return actions.next();
268
+ }
203
269
  });
204
270
  ```
205
271
 
206
- #### Context Functions
207
- For complex data preparation:
272
+ At runtime, the instruction becomes:
273
+ ```
274
+ "Greet Alice who has 5 documents"
275
+ ```
276
+
277
+ #### Multi-line Instructions
208
278
 
209
279
  ```javascript
210
- wizard.addTextStep({
211
- id: 'summarize',
212
- instruction: 'Summarize these documents: {{docList}}',
213
- contextType: 'template',
214
- contextFunction: (context) => ({
215
- docList: context.documents.map((doc, i) =>
216
- `${i + 1}. ${doc.title} (${doc.pages} pages)`
217
- ).join('\n')
280
+ wizard.addStep({
281
+ id: 'analyze_sentiment',
282
+ instruction: `
283
+ You are analyzing customer feedback.
284
+
285
+ Customer Review:
286
+ {{review}}
287
+
288
+ Previous Sentiment: {{previousSentiment}}
289
+
290
+ Analyze the sentiment and confidence level.
291
+ `.trim(),
292
+ context: (context) => ({
293
+ review: context.currentReview.text,
294
+ previousSentiment: context.lastSentiment || 'unknown'
295
+ }),
296
+ schema: z.object({
297
+ sentiment: z.enum(['positive', 'negative', 'neutral']),
298
+ confidence: z.number().min(0).max(1)
218
299
  }),
219
- // ...
300
+ model: Models.SWIZZY_DEFAULT,
301
+ update: (result, context, actions) => {
302
+ actions.updateContext({
303
+ lastSentiment: result.sentiment,
304
+ sentimentConfidence: result.confidence
305
+ });
306
+ return actions.next();
307
+ }
220
308
  });
221
309
  ```
222
310
 
223
- ### Steps: The Building Blocks
311
+ ---
224
312
 
225
- Steps are the individual units of execution in your workflow. There are three types:
313
+ ### Update Functions: State Mutation and Control
226
314
 
227
- #### 1. TextStep: LLM-Powered Steps
228
- Generate structured text with schema validation.
315
+ The **update function** runs after a step completes. It's where you:
316
+ 1. Process the LLM's output
317
+ 2. Update context/state
318
+ 3. Decide what happens next (via signals)
319
+
320
+ #### Function Signature
229
321
 
230
322
  ```javascript
231
- wizard.addTextStep({
232
- id: 'extract_keywords',
233
- instruction: 'Extract key topics from: {{text}}',
323
+ update: (result, context, actions) => {
324
+ // result: The output from this step
325
+ // context: Current workflow state (read-only)
326
+ // actions: Methods to update state and control flow
327
+
328
+ return actions.next(); // Must return a signal
329
+ }
330
+ ```
331
+
332
+ #### Processing Results
333
+
334
+ ```javascript
335
+ wizard.addStep({
336
+ id: 'extract_data',
337
+ instruction: 'Extract key data from {{document}}',
234
338
  schema: z.object({
235
- keywords: z.array(z.string()),
236
- confidence: z.number()
339
+ title: z.string(),
340
+ date: z.string(),
341
+ amount: z.number()
237
342
  }),
238
343
  model: Models.SWIZZY_DEFAULT,
239
- update: (data, context, actions) => {
240
- actions.updateContext({ keywords: data.keywords });
344
+ update: (result, context, actions) => {
345
+ // Validate the result
346
+ if (result.amount < 0) {
347
+ console.error('Invalid amount detected');
348
+ return actions.retry();
349
+ }
350
+
351
+ // Transform the result
352
+ const enrichedData = {
353
+ ...result,
354
+ parsedDate: new Date(result.date),
355
+ formattedAmount: `$${result.amount.toFixed(2)}`
356
+ };
357
+
358
+ // Update context
359
+ actions.updateContext({ extractedData: enrichedData });
360
+
361
+ // Continue
241
362
  return actions.next();
242
363
  }
243
364
  });
244
365
  ```
245
366
 
246
- #### 2. ComputeStep: Logic Steps
247
- Pure computation without LLM calls.
367
+ ---
368
+
369
+ ## Workflow Control: Signals
370
+
371
+ ### What Are Signals?
372
+
373
+ **Signals** are how steps communicate "what happens next" in your workflow. Every `update` function must return a signal.
374
+
375
+ Think of signals as the control flow language of Wizard - they let you build complex branching logic, retry mechanisms, and dynamic routing.
376
+
377
+ ---
378
+
379
+ ### Available Signals
380
+
381
+ #### `actions.next()`
382
+
383
+ Move to the next sequential step in your workflow.
248
384
 
249
385
  ```javascript
250
386
  wizard.addComputeStep({
251
- id: 'validate_keywords',
387
+ id: 'step_1',
252
388
  update: (result, context, actions) => {
253
- const keywords = context.keywords;
254
- if (keywords.length < 3) {
255
- return actions.goto('extract_keywords'); // Retry extraction
256
- }
257
- return actions.next();
389
+ return actions.next(); // Goes to step_2
390
+ }
391
+ });
392
+
393
+ wizard.addComputeStep({
394
+ id: 'step_2',
395
+ update: (result, context, actions) => {
396
+ return actions.next(); // Goes to step_3
258
397
  }
259
398
  });
260
399
  ```
261
400
 
262
- #### 3. General Step: Custom Steps
263
- Maximum flexibility with `addStep()`.
401
+ **Use when**: Following a linear, predictable flow.
402
+
403
+ ---
404
+
405
+ #### `actions.goto(stepId)`
406
+
407
+ Jump to a specific step by its ID.
264
408
 
265
409
  ```javascript
266
- wizard.addStep({
267
- id: 'custom_logic',
268
- instruction: 'Custom step logic',
269
- customHandler: myCustomFunction,
270
- update: (data, context, actions) => {
271
- // Your custom logic here
410
+ wizard.addComputeStep({
411
+ id: 'router',
412
+ update: (result, context, actions) => {
413
+ if (context.needsValidation) {
414
+ return actions.goto('validate_step');
415
+ }
416
+
417
+ if (context.needsEnrichment) {
418
+ return actions.goto('enrich_step');
419
+ }
420
+
421
+ return actions.goto('finalize_step');
422
+ }
423
+ });
424
+
425
+ wizard.addComputeStep({
426
+ id: 'validate_step',
427
+ update: (result, context, actions) => {
428
+ // Validation logic
429
+ return actions.next();
430
+ }
431
+ });
432
+
433
+ wizard.addComputeStep({
434
+ id: 'enrich_step',
435
+ update: (result, context, actions) => {
436
+ // Enrichment logic
272
437
  return actions.next();
273
438
  }
274
439
  });
275
440
  ```
276
441
 
277
- ---
442
+ **Use when**: Implementing conditional branching or complex routing logic.
278
443
 
279
- ## The Architecture (Core Concepts)
444
+ ---
280
445
 
281
- ### The Wizard: Your Orchestrator
446
+ #### `actions.retry()`
282
447
 
283
- The `Wizard` class is the heart of the framework. It manages:
284
- - **Step Registry**: All your workflow steps
285
- - **Context Store**: Shared memory across steps
286
- - **Execution Engine**: Runs steps in the correct order
287
- - **Flow Control**: Handles branching, retries, and termination
448
+ Re-run the current step.
288
449
 
289
450
  ```javascript
290
- const wizard = new Wizard({
291
- id: 'my-workflow',
292
- systemPrompt: 'You are a helpful AI assistant.',
293
- onUsage: (usage, provider) => {
294
- // Track token usage
295
- console.log(`${usage.totalTokens} tokens used`);
451
+ wizard.addStep({
452
+ id: 'extract_with_retry',
453
+ instruction: 'Extract entities from: {{text}}',
454
+ schema: z.object({
455
+ entities: z.array(z.string()),
456
+ confidence: z.number()
457
+ }),
458
+ model: Models.SWIZZY_DEFAULT,
459
+ update: (result, context, actions) => {
460
+ // Retry if confidence is too low
461
+ if (result.confidence < 0.8) {
462
+ const retryCount = context.retryCount || 0;
463
+
464
+ if (retryCount < 3) {
465
+ actions.updateContext({ retryCount: retryCount + 1 });
466
+ return actions.retry();
467
+ }
468
+ }
469
+
470
+ // Success or max retries reached
471
+ actions.updateContext({ finalEntities: result.entities });
472
+ return actions.next();
296
473
  }
297
474
  });
298
475
  ```
299
476
 
300
- ### The Context: Shared Memory
477
+ **Use when**: The LLM output needs improvement or validation failed.
301
478
 
302
- Context is the "memory" that persists across all steps. Think of it as a shared JavaScript object that steps can read from and write to.
479
+ **Pro tip**: Track retry counts to avoid infinite loops.
303
480
 
304
- #### Template Variables
305
- Use `{{variableName}}` in instructions for simple value replacement:
481
+ ---
306
482
 
307
- ```javascript
308
- wizard.setContext({
309
- userName: 'Bob',
310
- task: 'write a poem'
311
- });
483
+ #### `actions.stop()`
312
484
 
313
- wizard.addTextStep({
314
- id: 'create_poem',
315
- instruction: 'Write a {{task}} about {{userName}}',
316
- // ...
485
+ End the workflow immediately.
486
+
487
+ ```javascript
488
+ wizard.addComputeStep({
489
+ id: 'check_completion',
490
+ update: (result, context, actions) => {
491
+ if (context.allDocumentsProcessed) {
492
+ console.log('Workflow complete!');
493
+ return actions.stop(); // End here
494
+ }
495
+
496
+ return actions.next(); // Continue processing
497
+ }
317
498
  });
318
499
  ```
319
500
 
320
- #### Context Functions
321
- For complex data transformations, use `contextFunction`:
501
+ **Use when**: Early termination, error conditions, or goal achievement.
502
+
503
+ ---
504
+
505
+ #### `actions.wait()`
506
+
507
+ Pause for 10 seconds before continuing to the next step.
322
508
 
323
509
  ```javascript
324
- wizard.addTextStep({
325
- id: 'analyze_data',
326
- instruction: 'Analyze this dataset: {{formattedData}}',
327
- contextType: 'template',
328
- contextFunction: (context) => ({
329
- formattedData: context.rawData.map(item =>
330
- `${item.name}: ${item.value}`
331
- ).join(', ')
332
- }),
333
- // ...
510
+ wizard.addComputeStep({
511
+ id: 'rate_limit_handler',
512
+ update: (result, context, actions) => {
513
+ if (context.apiCallsThisMinute >= 60) {
514
+ console.log('Rate limit reached, waiting...');
515
+ actions.updateContext({ apiCallsThisMinute: 0 });
516
+ return actions.wait(); // Pause 10 seconds
517
+ }
518
+
519
+ return actions.next();
520
+ }
334
521
  });
335
522
  ```
336
523
 
337
- ### The Steps: Your Building Blocks
524
+ **Use when**: Rate limiting, waiting for external systems, or pacing execution.
338
525
 
339
- Steps are the individual units of work in your workflow. There are three types:
526
+ ---
527
+
528
+ ### Signal Patterns
340
529
 
341
- #### 1. TextStep: LLM Interactions
342
- Generates structured text using LLMs with schema validation.
530
+ #### Sequential Execution
343
531
 
344
532
  ```javascript
345
533
  wizard.addTextStep({
346
- id: 'extract_entities',
347
- instruction: 'Extract named entities from: {{text}}',
348
- schema: z.object({
349
- people: z.array(z.string()),
350
- organizations: z.array(z.string()),
351
- locations: z.array(z.string())
352
- }),
534
+ id: 'step_1',
535
+ instruction: 'Do task 1',
353
536
  model: Models.SWIZZY_DEFAULT,
354
- update: (validatedData, context, actions) => {
355
- // validatedData is guaranteed to match the schema
356
- actions.updateContext({
357
- extractedEntities: validatedData
358
- });
537
+ update: (text, context, actions) => {
538
+ actions.updateContext({ task1Result: text });
539
+ return actions.next();
540
+ }
541
+ });
542
+
543
+ wizard.addTextStep({
544
+ id: 'step_2',
545
+ instruction: 'Do task 2 using {{task1Result}}',
546
+ model: Models.SWIZZY_DEFAULT,
547
+ update: (text, context, actions) => {
359
548
  return actions.next();
360
549
  }
361
550
  });
362
551
  ```
363
552
 
364
- #### 2. ComputeStep: Pure Logic
365
- For computations, API calls, or any code that doesn't need LLMs.
553
+ #### Conditional Routing
366
554
 
367
555
  ```javascript
368
556
  wizard.addComputeStep({
369
- id: 'validate_data',
370
- instruction: 'Validate the extracted entities',
557
+ id: 'router',
371
558
  update: (result, context, actions) => {
372
- const entities = context.extractedEntities;
373
- const isValid = entities.people.length > 0 ||
374
- entities.organizations.length > 0;
375
-
376
- if (!isValid) {
377
- console.log('⚠️ No entities found, retrying...');
378
- return actions.retry();
559
+ if (context.complexity === 'simple') {
560
+ return actions.goto('simple_path');
561
+ } else if (context.complexity === 'medium') {
562
+ return actions.goto('medium_path');
563
+ } else {
564
+ return actions.goto('complex_path');
379
565
  }
380
-
381
- actions.updateContext({ validationPassed: true });
382
- return actions.next();
383
566
  }
384
567
  });
385
568
  ```
386
569
 
387
- #### 3. General Step: Full Control
388
- The `addStep()` method accepts any configuration for maximum flexibility.
570
+ #### Retry with Backoff
389
571
 
390
572
  ```javascript
391
573
  wizard.addStep({
392
- id: 'custom_step',
393
- instruction: 'Custom logic here',
394
- customProperty: 'value',
395
- update: (data, context, actions) => {
396
- // Your custom logic
574
+ id: 'api_call_with_retry',
575
+ instruction: 'Process: {{data}}',
576
+ schema: z.object({ success: z.boolean() }),
577
+ model: Models.SWIZZY_DEFAULT,
578
+ update: (result, context, actions) => {
579
+ if (!result.success) {
580
+ const retries = context.retries || 0;
581
+
582
+ if (retries < 3) {
583
+ actions.updateContext({ retries: retries + 1 });
584
+ return actions.wait(); // Wait before retry
585
+ }
586
+
587
+ return actions.goto('error_handler');
588
+ }
589
+
397
590
  return actions.next();
398
591
  }
399
592
  });
400
593
  ```
401
594
 
402
- ### Flow Control: The Router
595
+ #### Loop Pattern
403
596
 
404
- Control execution flow with explicit signals:
597
+ ```javascript
598
+ wizard.addComputeStep({
599
+ id: 'process_items',
600
+ update: (result, context, actions) => {
601
+ const currentIndex = context.currentIndex || 0;
602
+
603
+ if (currentIndex < context.items.length) {
604
+ actions.updateContext({
605
+ currentItem: context.items[currentIndex],
606
+ currentIndex: currentIndex + 1
607
+ });
608
+ return actions.goto('process_single_item');
609
+ }
610
+
611
+ // All items processed
612
+ return actions.next();
613
+ }
614
+ });
405
615
 
406
- ```mermaid
407
- stateDiagram-v2
408
- [*] --> Step1
409
- Step1 --> Step2: next()
410
- Step1 --> Step3: goto('step3')
411
- Step1 --> [*]: stop()
412
- Step2 --> Step2: retry()
413
- Step2 --> Waiting: wait()
414
- Waiting --> Step2: 10s timeout
415
- ```
416
-
417
- - **`actions.next()`**: Continue to the next step in sequence
418
- - **`actions.goto('stepId')`**: Jump to any step by ID
419
- - **`actions.stop()`**: End the workflow immediately
420
- - **`actions.retry()`**: Retry the current step (with exponential backoff)
421
- - **`actions.wait()`**: Pause for 10 seconds before continuing
616
+ wizard.addTextStep({
617
+ id: 'process_single_item',
618
+ instruction: 'Process: {{currentItem}}',
619
+ model: Models.SWIZZY_DEFAULT,
620
+ update: (text, context, actions) => {
621
+ // Store result
622
+ actions.updateContext({
623
+ [`result_${context.currentIndex}`]: text
624
+ });
625
+ // Loop back
626
+ return actions.goto('process_items');
627
+ }
628
+ });
629
+ ```
422
630
 
423
631
  ---
424
632
 
425
- ## Feature Spotlight: Bungee Jumps (Parallelism)
633
+ ## Advanced Patterns
426
634
 
427
- **Bungee Jumps are our secret weapon** - the pattern that makes @swizzy_ai/kit uniquely powerful for AI workflows.
635
+ ### The Bungee Action: Parallelism
428
636
 
429
- ### The Concept
637
+ The **Bungee Action** is Wizard's most powerful pattern. It implements fan-out/fan-in: launch a step multiple times in parallel, then automatically return to the anchor point.
430
638
 
431
- Traditional AI workflows are sequential: Step 1 → Step 2 → Step 3. But what if Step 2 needs to process 100 documents? You'd wait 100x longer than necessary.
639
+ #### Anatomy of a Bungee
432
640
 
433
- Bungee Jumps implement the **fan-out/fan-in pattern**:
434
- 1. **Anchor**: The main flow pauses at a step
435
- 2. **Jump**: Launch multiple parallel "worker" instances
436
- 3. **Batch**: Each worker processes a different data point
437
- 4. **Return**: All workers complete, main flow resumes
641
+ - **Anchor Step**: Where the bungee launches from (typically a compute step)
642
+ - **Destination Step**: The step that runs in parallel for each item
643
+ - **Batch**: Each parallel run receives unique context
644
+ - **Fan-in**: All parallel runs complete, execution returns to the anchor
645
+ - **Continuation**: The anchor's next signal determines where to go
438
646
 
439
647
  ```mermaid
440
648
  graph TD
441
- A[Main Flow] --> B[Anchor Step]
442
- B --> C[🪂 Bungee Jump]
443
- C --> D[Worker 1<br/>Process Item A]
444
- C --> E[Worker 2<br/>Process Item B]
445
- C --> F[Worker N<br/>Process Item Z]
446
- D --> G[Collect Results]
447
- E --> G
448
- F --> G
449
- G --> H[Return to Anchor]
450
- H --> I[Continue Main Flow]
649
+ A[Anchor Step] --> B[🪂 Bungee Launch]
650
+ B --> C[Worker 1: Item A]
651
+ B --> D[Worker 2: Item B]
652
+ B --> E[Worker N: Item Z]
653
+ C --> F[All Complete]
654
+ D --> F
655
+ E --> F
656
+ F --> G[Return to Anchor]
657
+ G --> H[Continue Flow]
451
658
  ```
452
659
 
453
- ### Real-World Example: Document Search
660
+ ---
454
661
 
455
- Imagine searching a 100-page document for answers. Instead of checking pages one-by-one:
662
+ #### Basic Bungee Example
456
663
 
457
664
  ```javascript
458
- // Sequential approach (slow)
459
- for (let page = 1; page <= 100; page++) {
460
- searchPage(page); // 100 sequential calls = ~5 minutes
461
- }
665
+ wizard.setContext({
666
+ documents: ['doc1.txt', 'doc2.txt', 'doc3.txt']
667
+ });
668
+
669
+ // Anchor step
670
+ wizard.addComputeStep({
671
+ id: 'parallel_processor',
672
+ update: (result, context, actions) => {
673
+ return actions.bungee.init()
674
+ .batch(
675
+ 'process_document', // Destination step
676
+ context.documents.length, // Run 3 times
677
+ (index) => ({ // Each run gets unique context
678
+ documentName: context.documents[index],
679
+ documentIndex: index
680
+ })
681
+ )
682
+ .config({
683
+ concurrency: 2, // Process 2 at a time
684
+ timeout: 30000 // 30 second timeout per worker
685
+ })
686
+ .jump(); // Launch!
687
+ }
688
+ });
689
+
690
+ // Destination step (runs 3 times in parallel)
691
+ wizard.addTextStep({
692
+ id: 'process_document',
693
+ instruction: 'Summarize document: {{documentName}}',
694
+ schema: z.object({
695
+ summary: z.string(),
696
+ wordCount: z.number()
697
+ }),
698
+ model: Models.SWIZZY_DEFAULT,
699
+ update: (result, context, actions) => {
700
+ // Store result with unique key
701
+ actions.updateContext({
702
+ [`summary_${context.documentIndex}`]: result.summary,
703
+ [`wordCount_${context.documentIndex}`]: result.wordCount
704
+ });
705
+ return actions.next(); // Return to anchor
706
+ }
707
+ });
462
708
 
463
- // Bungee approach (fast)
709
+ // Back at anchor - collect results
710
+ wizard.addComputeStep({
711
+ id: 'collect_results',
712
+ update: (result, context, actions) => {
713
+ const allSummaries = context.documents.map((_, i) =>
714
+ context[`summary_${i}`]
715
+ );
716
+
717
+ actions.updateContext({ allSummaries });
718
+ return actions.next();
719
+ }
720
+ });
721
+ ```
722
+
723
+ ---
724
+
725
+ #### Real-World Example: Parallel Document Search
726
+
727
+ Search 100 pages simultaneously instead of sequentially:
728
+
729
+ ```javascript
730
+ wizard.setContext({
731
+ userQuestion: 'What are the key findings about climate change?',
732
+ totalPages: 100
733
+ });
734
+
735
+ // Anchor: Launch parallel search
464
736
  wizard.addComputeStep({
465
737
  id: 'parallel_search',
466
738
  update: (result, context, actions) => {
467
739
  return actions.bungee.init()
468
- .batch('search_page', 100, (pageIndex) => ({
469
- pageNumber: pageIndex + 1,
470
- query: context.userQuestion
471
- }))
472
- .config({ concurrency: 10 }) // 10 pages at once
473
- .jump(); // ~30 seconds total
740
+ .batch(
741
+ 'search_page',
742
+ context.totalPages, // 100 parallel searches
743
+ (pageIndex) => ({
744
+ pageNumber: pageIndex + 1,
745
+ query: context.userQuestion
746
+ })
747
+ )
748
+ .config({
749
+ concurrency: 10, // 10 workers at a time
750
+ timeout: 30000
751
+ })
752
+ .jump();
474
753
  }
475
754
  });
476
755
 
756
+ // Worker: Search a single page
477
757
  wizard.addTextStep({
478
758
  id: 'search_page',
479
- instruction: 'Search page {{pageNumber}} for: {{query}}',
759
+ instruction: 'Search page {{pageNumber}} for information about: {{query}}',
760
+ schema: z.object({
761
+ hasRelevantInfo: z.boolean(),
762
+ excerpt: z.string().optional(),
763
+ relevanceScore: z.number().min(0).max(10).optional()
764
+ }),
765
+ model: Models.SWIZZY_DEFAULT,
480
766
  update: (result, context, actions) => {
481
- if (result.includes('relevant content')) {
767
+ if (result.hasRelevantInfo && result.relevanceScore >= 7) {
482
768
  actions.updateContext({
483
- [`page_${context.pageNumber}_result`]: result
769
+ [`page_${context.pageNumber}_excerpt`]: result.excerpt,
770
+ [`page_${context.pageNumber}_score`]: result.relevanceScore
484
771
  });
485
772
  }
773
+ return actions.next(); // Return to anchor
774
+ }
775
+ });
776
+
777
+ // Collect and rank results
778
+ wizard.addComputeStep({
779
+ id: 'collect_and_rank',
780
+ update: (result, context, actions) => {
781
+ const results = [];
782
+
783
+ for (let i = 1; i <= context.totalPages; i++) {
784
+ const excerpt = context[`page_${i}_excerpt`];
785
+ const score = context[`page_${i}_score`];
786
+
787
+ if (excerpt) {
788
+ results.push({ page: i, excerpt, score });
789
+ }
790
+ }
791
+
792
+ // Sort by relevance
793
+ results.sort((a, b) => b.score - a.score);
794
+
795
+ actions.updateContext({
796
+ searchResults: results.slice(0, 10) // Top 10
797
+ });
798
+
486
799
  return actions.next();
487
800
  }
488
801
  });
489
802
  ```
490
803
 
491
- **Result**: 10x faster execution with the same code complexity.
804
+ **Performance**: Process 100 pages in ~30 seconds (with concurrency: 10) instead of 5+ minutes sequentially.
805
+
806
+ ---
807
+
808
+ #### When to Use Bungee
492
809
 
493
- ### Configuration Options
810
+ **Perfect for:**
811
+ - Batch processing (documents, images, data records)
812
+ - Parallel API calls or searches
813
+ - Multi-source information gathering
814
+ - Any workflow bottleneck that can be parallelized
815
+
816
+ **Not ideal for:**
817
+ - Tasks that must run sequentially
818
+ - When order matters
819
+ - Shared state that can't be partitioned
820
+
821
+ ---
822
+
823
+ #### Bungee Configuration Options
494
824
 
495
825
  ```javascript
496
826
  actions.bungee.init()
497
- .batch('stepId', itemCount, (index) => ({
498
- // Context for each worker instance
499
- itemIndex: index,
500
- data: items[index]
501
- }))
827
+ .batch('destination_step', count, contextFn)
502
828
  .config({
503
- concurrency: 5, // Max parallel workers
504
- timeout: 30000 // Per-worker timeout in ms
829
+ concurrency: 10, // Max parallel workers (default: 5)
830
+ timeout: 30000, // Timeout per worker in ms (default: 60000)
831
+ retryOnError: true // Retry failed workers (default: false)
505
832
  })
506
- .jump()
833
+ .jump();
507
834
  ```
508
835
 
509
836
  ---
510
837
 
511
- ## Developer Experience (DX)
512
-
513
- ### Real-Time Visualization
838
+ ### Multi-API Configuration
514
839
 
515
- Debug workflows visually with the built-in web interface:
840
+ Wizard supports multiple LLM providers. Configure API keys for different models:
516
841
 
517
842
  ```javascript
518
- const { server, url } = await wizard.visualize(3000);
519
- console.log(`🎯 Open ${url} in your browser`);
520
- ```
521
-
522
- **Features:**
523
- - **Live Step Tracking**: See each step execute in real-time
524
- - **Token Monitoring**: Track LLM usage per step and total
525
- - **Context Inspector**: View context changes as they happen
526
- - **Interactive Controls**: Pause, resume, or step through execution
527
- - **Error Visualization**: See failures and retry attempts
528
-
529
- ### Error Handling & Resilience
843
+ const wizard = new Wizard({
844
+ id: 'multi-provider-workflow',
845
+ apiKeys: {
846
+ openai: process.env.OPENAI_API_KEY,
847
+ anthropic: process.env.ANTHROPIC_API_KEY,
848
+ gemini: process.env.GEMINI_API_KEY
849
+ }
850
+ });
530
851
 
531
- ```javascript
852
+ // Use different models for different tasks
532
853
  wizard.addTextStep({
533
- id: 'unreliable_step',
534
- instruction: 'This might fail sometimes',
535
- update: (result, context, actions) => {
536
- if (result.includes('ERROR')) {
537
- // Automatic retry with backoff
538
- return actions.retry();
539
- }
540
-
541
- if (someCondition) {
542
- // Jump to error handling step
543
- return actions.goto('error_handler');
544
- }
854
+ id: 'fast_extraction',
855
+ instruction: 'Quick extract: {{data}}',
856
+ model: Models.GPT_4O_MINI, // Fast, cheap
857
+ update: (text, context, actions) => {
858
+ actions.updateContext({ extracted: text });
859
+ return actions.next();
860
+ }
861
+ });
545
862
 
863
+ wizard.addTextStep({
864
+ id: 'deep_analysis',
865
+ instruction: 'Deeply analyze: {{extracted}}',
866
+ model: Models.CLAUDE_SONNET, // Powerful reasoning
867
+ update: (text, context, actions) => {
868
+ actions.updateContext({ analysis: text });
546
869
  return actions.next();
547
870
  }
548
871
  });
549
872
  ```
550
873
 
551
- **Built-in Resilience:**
552
- - Automatic retry with exponential backoff
553
- - Configurable timeout handling
554
- - Context preservation across retries
555
- - Error context tracking (`${stepId}_error`)
874
+ ---
556
875
 
557
- ### TypeScript + Zod Integration
876
+ ## Complete Example
558
877
 
559
- Full type safety from LLM outputs to your application code:
878
+ Here's a full workflow that demonstrates all concepts:
560
879
 
561
- ```typescript
562
- import { z } from 'zod';
880
+ ```javascript
881
+ const { Wizard, Models } = require('@swizzy_ai/kit');
882
+ const { z } = require('zod');
563
883
 
564
- const AnalysisSchema = z.object({
565
- sentiment: z.enum(['positive', 'negative', 'neutral']),
566
- confidence: z.number().min(0).max(1),
567
- keywords: z.array(z.string())
884
+ const wizard = new Wizard({
885
+ id: 'document-analyzer',
886
+ onUsage: (usage, provider) => {
887
+ console.log(`📊 ${provider}: ${usage.totalTokens} tokens`);
888
+ }
568
889
  });
569
890
 
570
- wizard.addTextStep({
571
- id: 'analyze_text',
572
- instruction: 'Analyze: {{text}}',
573
- schema: AnalysisSchema,
891
+ // Initialize context
892
+ wizard.setContext({
893
+ documentText: 'Your document content here...',
894
+ userQuery: 'Extract key insights',
895
+ maxRetries: 3
896
+ });
897
+
898
+ // Step 1: Extract entities with retry logic
899
+ wizard.addStep({
900
+ id: 'extract_entities',
901
+ instruction: 'Extract named entities from: {{documentText}}',
902
+ schema: z.object({
903
+ people: z.array(z.string()),
904
+ organizations: z.array(z.string()),
905
+ confidence: z.number().min(0).max(1)
906
+ }),
574
907
  model: Models.SWIZZY_DEFAULT,
575
- update: (data, context, actions) => {
576
- // data is fully typed: AnalysisSchema.Type
577
- console.log(data.sentiment); // TypeScript knows this is a valid enum
578
- console.log(data.confidence); // TypeScript knows this is a number 0-1
908
+ update: (result, context, actions) => {
909
+ const retries = context.extractRetries || 0;
910
+
911
+ // Low confidence? Retry
912
+ if (result.confidence < 0.8 && retries < context.maxRetries) {
913
+ actions.updateContext({ extractRetries: retries + 1 });
914
+ return actions.retry();
915
+ }
916
+
917
+ // Store entities
918
+ actions.updateContext({
919
+ entities: result,
920
+ extractRetries: 0
921
+ });
922
+ return actions.next();
923
+ }
924
+ });
925
+
926
+ // Step 2: Validate entities
927
+ wizard.addComputeStep({
928
+ id: 'validate_entities',
929
+ update: (result, context, actions) => {
930
+ const hasEntities =
931
+ context.entities.people.length > 0 ||
932
+ context.entities.organizations.length > 0;
933
+
934
+ if (!hasEntities) {
935
+ console.log('No entities found, retrying extraction');
936
+ return actions.goto('extract_entities');
937
+ }
938
+
939
+ console.log(`Found ${context.entities.people.length} people, ${context.entities.organizations.length} orgs`);
579
940
  return actions.next();
580
941
  }
581
942
  });
943
+
944
+ // Step 3: Generate summary
945
+ wizard.addTextStep({
946
+ id: 'generate_summary',
947
+ instruction: `
948
+ Summarize key insights about these entities:
949
+
950
+ {{entityList}}
951
+
952
+ Focus on their relationships and significance.
953
+ `.trim(),
954
+ context: (context) => ({
955
+ entityList: [
956
+ ...context.entities.people.map(p => `Person: ${p}`),
957
+ ...context.entities.organizations.map(o => `Organization: ${o}`)
958
+ ].join('\n')
959
+ }),
960
+ schema: z.object({
961
+ summary: z.string(),
962
+ keyInsights: z.array(z.string()),
963
+ confidence: z.number()
964
+ }),
965
+ model: Models.SWIZZY_DEFAULT,
966
+ update: (result, context, actions) => {
967
+ console.log('Summary:', result.summary);
968
+ console.log('Key Insights:');
969
+ result.keyInsights.forEach((insight, i) => {
970
+ console.log(` ${i + 1}. ${insight}`);
971
+ });
972
+
973
+ return actions.stop(); // Workflow complete!
974
+ }
975
+ });
976
+
977
+ // Run the workflow
978
+ wizard.run();
582
979
  ```
583
980
 
584
981
  ---
@@ -588,130 +985,129 @@ wizard.addTextStep({
588
985
  ### Wizard Constructor
589
986
 
590
987
  ```typescript
591
- new Wizard(config: WizardConfig)
988
+ new Wizard(config: {
989
+ id: string;
990
+ systemPrompt?: string;
991
+ onUsage?: (usage: TokenUsage, provider: string) => void;
992
+ apiKeys?: {
993
+ openai?: string;
994
+ anthropic?: string;
995
+ gemini?: string;
996
+ }
997
+ })
592
998
  ```
593
999
 
594
- ```typescript
595
- interface WizardConfig {
596
- id: string; // Unique workflow identifier
597
- systemPrompt?: string; // Global system prompt for LLMs
598
- onUsage?: (usage: TokenUsage, provider: string) => void; // Token tracking callback
599
- }
600
- ```
1000
+ ### Context Methods
601
1001
 
602
- ### Core Methods
1002
+ | Method | Description |
1003
+ |--------|-------------|
1004
+ | `setContext(data)` | Initialize workflow context |
1005
+ | `getContext()` | Retrieve current context |
1006
+ | `updateContext(data)` | Update context (returns Wizard instance for chaining) |
603
1007
 
604
- | Method | Description | Example |
605
- |--------|-------------|---------|
606
- | `addStep(config)` | Add any type of step | `wizard.addStep({ id: 'step1', ... })` |
607
- | `addTextStep(config)` | Add LLM text generation step | `wizard.addTextStep({ id: 'generate', ... })` |
608
- | `addComputeStep(config)` | Add computational step | `wizard.addComputeStep({ id: 'process', ... })` |
609
- | `setContext(data)` | Initialize shared context | `wizard.setContext({ user: 'Alice' })` |
610
- | `getContext()` | Get current context | `const ctx = wizard.getContext()` |
611
- | `updateContext(data)` | Update context (fluent) | `wizard.updateContext({ result: data })` |
612
- | `run()` | Execute the workflow | `await wizard.run()` |
613
- | `visualize(port)` | Start web UI | `await wizard.visualize(3000)` |
1008
+ ### Step Methods
614
1009
 
615
- ### Step Configuration
1010
+ | Method | Description |
1011
+ |--------|-------------|
1012
+ | `addStep(config)` | Add structured LLM step with schema validation |
1013
+ | `addTextStep(config)` | Add simple text generation step |
1014
+ | `addComputeStep(config)` | Add non-LLM logic step |
616
1015
 
617
- #### TextStep Configuration
618
- ```typescript
619
- interface TextStepConfig {
620
- id: string; // Unique step identifier
621
- instruction: string; // LLM prompt/instruction
622
- schema?: z.ZodType; // Output validation schema
623
- model: string; // LLM model identifier
624
- contextType?: 'template' | 'xml' | 'both'; // How to inject context
625
- contextFunction?: (context: any) => any; // Dynamic context builder
626
- update: StepUpdateFunction; // Result handler
627
- }
628
- ```
1016
+ ### Execution Methods
629
1017
 
630
- #### ComputeStep Configuration
631
- ```typescript
632
- interface ComputeStepConfig {
633
- id: string; // Unique step identifier
634
- instruction: string; // Documentation/description
635
- update: StepUpdateFunction; // Logic handler
636
- }
637
- ```
1018
+ | Method | Description |
1019
+ |--------|-------------|
1020
+ | `run()` | Execute the workflow from the first step |
1021
+ | `stop()` | Manually stop a running workflow |
638
1022
 
639
1023
  ### Actions Interface
640
1024
 
1025
+ Available in every `update` function:
1026
+
641
1027
  ```typescript
642
- interface WizardActions {
643
- updateContext: (updates: Record<string, any>) => void;
644
- llmClient: LLMClient; // Direct LLM access if needed
645
- goto: (stepId: string) => FlowControlSignal;
1028
+ {
1029
+ updateContext: (updates: object) => void;
646
1030
  next: () => FlowControlSignal;
647
- stop: () => FlowControlSignal;
1031
+ goto: (stepId: string) => FlowControlSignal;
648
1032
  retry: () => FlowControlSignal;
1033
+ stop: () => FlowControlSignal;
649
1034
  wait: () => FlowControlSignal;
650
1035
  bungee: {
651
- init: () => BungeeBuilder; // Start parallel execution
652
- };
1036
+ init: () => BungeeBuilder;
1037
+ }
653
1038
  }
654
1039
  ```
655
1040
 
656
- ### TokenUsage Interface
1041
+ ### Bungee Builder Interface
657
1042
 
658
1043
  ```typescript
659
- interface TokenUsage {
660
- promptTokens: number;
661
- completionTokens: number;
662
- totalTokens: number;
1044
+ BungeeBuilder {
1045
+ batch: (
1046
+ destinationStepId: string,
1047
+ count: number,
1048
+ context: (index: number) => object
1049
+ ) => BungeeBuilder;
1050
+
1051
+ config: (options: {
1052
+ concurrency?: number;
1053
+ timeout?: number;
1054
+ retryOnError?: boolean;
1055
+ }) => BungeeBuilder;
1056
+
1057
+ jump: () => FlowControlSignal;
663
1058
  }
664
1059
  ```
665
1060
 
666
1061
  ---
667
1062
 
668
- ## Contributing
1063
+ ## Comparison with Alternatives
669
1064
 
670
- We welcome contributions! Here's how to get started:
1065
+ | Feature | Wizard | LangChain | Vercel AI SDK | Raw LLM APIs |
1066
+ |---------|---------|-----------|---------------|--------------|
1067
+ | **Mental Model** | Developer orchestrates, LLM executes | LLM is the agent | Linear chat flows | Full manual control |
1068
+ | **Context Control** | ✅ Context functions + templates | ⚠️ Chain-based passing | ❌ Manual | ❌ Manual |
1069
+ | **State Machine** | ✅ Explicit signals (next/goto/retry/stop) | ❌ Sequential chains | ❌ Linear only | ❌ Build yourself |
1070
+ | **Type Safety** | ✅ Zod validation on outputs | ⚠️ Optional | ⚠️ TypeScript only | ❌ None |
1071
+ | **Parallelism** | ✅ Bungee actions | ❌ Manual orchestration | ❌ Manual | ❌ Manual |
1072
+ | **Flow Control** | ✅ Signals with branching | ⚠️ Limited | ⚠️ Limited | ❌ Manual |
1073
+ | **Learning Curve** | Medium | Steep | Low | High |
1074
+ | **Best For** | Complex multi-step workflows | General orchestration | Simple chat | Full control |
671
1075
 
672
- 1. **Fork** the repository
673
- 2. **Clone** your fork: `git clone https://github.com/swizzy-ai/swizzy-kit.git`
674
- 3. **Install** dependencies: `npm install`
675
- 4. **Create** a feature branch: `git checkout -b feature/amazing-feature`
676
- 5. **Make** your changes with tests
677
- 6. **Run** tests: `npm test`
678
- 7. **Submit** a pull request
1076
+ ---
679
1077
 
680
- ### Development Setup
1078
+ ## Examples
681
1079
 
682
- ```bash
683
- # Install dependencies
684
- npm install
1080
+ Check the `/examples` directory for complete workflows:
685
1081
 
686
- # Run tests
687
- npm test
1082
+ - **Document Analyzer**: Extract entities and generate insights with retry logic
1083
+ - **Parallel Search**: Search 100+ pages simultaneously using Bungee actions
1084
+ - **Multi-Step Validation**: Complex validation with conditional routing
1085
+ - **Data Pipeline**: Transform and validate data across multiple steps
1086
+ - **Multi-Provider Workflow**: Use different LLM providers for different tasks
688
1087
 
689
- # Build TypeScript
690
- npm run build
1088
+ ---
691
1089
 
692
- # Run examples
693
- cd examples && node document-reader.js
694
- ```
1090
+ ## Contributing
695
1091
 
696
- ### Guidelines
1092
+ We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
1093
+
1094
+ ```bash
1095
+ # Setup
1096
+ git clone https://github.com/swizzy-ai/wizard-framework.git
1097
+ npm install
697
1098
 
698
- - **Type Safety**: All code must be TypeScript with proper types
699
- - **Testing**: Add tests for new features
700
- - **Documentation**: Update README and inline docs
701
- - **Consistency**: Match existing code style and patterns
1099
+ # Development
1100
+ npm test # Run tests
1101
+ npm run build # Build TypeScript
1102
+ npm run lint # Lint code
1103
+ ```
702
1104
 
703
1105
  ---
704
1106
 
705
1107
  ## License
706
1108
 
707
- **MIT License** - see [LICENSE](LICENSE) file for details.
1109
+ MIT License - see [LICENSE](./LICENSE) for details.
708
1110
 
709
1111
  ---
710
1112
 
711
- <div align="center">
712
-
713
- **Built with ❤️ for the AI orchestration revolution**
714
-
715
- [GitHub](https://github.com/swizzy-ai/swizzy-kit) • [Documentation](https://swizzy-kit.dev) • [Discord](https://discord.gg/swizzy-kit)
716
-
717
- </div>
1113
+ <div align="center">