@swizzy_ai/kit 1.0.0 → 1.0.1

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