@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.
- package/README.md +841 -438
- package/dist/core/wizard/bungee/executor.d.ts +15 -0
- package/dist/core/wizard/bungee/executor.d.ts.map +1 -0
- package/dist/core/wizard/context-manager.d.ts +9 -0
- package/dist/core/wizard/context-manager.d.ts.map +1 -0
- package/dist/core/wizard/logger.d.ts +8 -0
- package/dist/core/wizard/logger.d.ts.map +1 -0
- package/dist/core/wizard/schema-utils.d.ts +16 -0
- package/dist/core/wizard/schema-utils.d.ts.map +1 -0
- package/dist/core/wizard/step-data-generator.d.ts +24 -0
- package/dist/core/wizard/step-data-generator.d.ts.map +1 -0
- package/dist/core/wizard/steps/base.d.ts +4 -3
- package/dist/core/wizard/steps/base.d.ts.map +1 -1
- package/dist/core/wizard/steps/compute.d.ts +2 -2
- package/dist/core/wizard/steps/compute.d.ts.map +1 -1
- package/dist/core/wizard/steps/text.d.ts +2 -2
- package/dist/core/wizard/steps/text.d.ts.map +1 -1
- package/dist/core/wizard/usage-tracker.d.ts +25 -0
- package/dist/core/wizard/usage-tracker.d.ts.map +1 -0
- package/dist/core/wizard/visualization-manager.d.ts +34 -0
- package/dist/core/wizard/visualization-manager.d.ts.map +1 -0
- package/dist/core/wizard/wizard.d.ts +39 -40
- package/dist/core/wizard/wizard.d.ts.map +1 -1
- package/dist/index.d.ts +46 -46
- package/dist/index.js +1207 -626
- package/dist/index.js.map +1 -1
- package/dist/services/client/index.d.ts +1 -0
- package/dist/services/client/index.d.ts.map +1 -1
- package/dist/services/client/providers.d.ts.map +1 -1
- package/dist/ui/wizard-visualizer.html +570 -385
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -2,583 +2,987 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
[](https://www.npmjs.com/package/@swizzy_ai/kit)
|
|
7
|
+
[](https://www.npmjs.com/package/@swizzy_ai/kit)
|
|
16
8
|
[](https://opensource.org/licenses/MIT)
|
|
17
|
-
[](https://www.typescriptlang.org/)
|
|
18
|
-
[](https://nodejs.org/)
|
|
19
|
-
|
|
20
|
-
[](https://github.com/swizzy-ai/wizard-framework/stargazers)
|
|
21
|
-
[](https://github.com/swizzy-ai/wizard-framework/network/members)
|
|
22
|
-
[](https://github.com/swizzy-ai/wizard-framework/watchers)
|
|
23
9
|
|
|
24
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
29
|
+
### What Wizard Believes
|
|
50
30
|
|
|
51
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
-
####
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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: '
|
|
121
|
-
instruction: 'Analyze
|
|
122
|
-
|
|
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: (
|
|
125
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
179
|
+
#### Step Anatomy
|
|
150
180
|
|
|
151
|
-
|
|
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
|
-
|
|
191
|
+
Choose the right step type for your task:
|
|
162
192
|
|
|
163
|
-
|
|
193
|
+
**Normal Step (Structured Data)**
|
|
164
194
|
|
|
165
|
-
|
|
195
|
+
Use when you need the LLM to generate data matching a specific schema:
|
|
166
196
|
|
|
167
197
|
```javascript
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
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: '
|
|
178
|
-
instruction: '
|
|
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
|
-
|
|
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: '
|
|
237
|
+
id: 'validate',
|
|
185
238
|
update: (result, context, actions) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
196
|
-
|
|
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: '
|
|
201
|
-
instruction: '
|
|
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
|
-
|
|
207
|
-
|
|
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.
|
|
211
|
-
id: '
|
|
212
|
-
instruction:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
318
|
+
---
|
|
224
319
|
|
|
225
|
-
|
|
320
|
+
### Update Functions: State Mutation and Control
|
|
226
321
|
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
346
|
+
title: z.string(),
|
|
347
|
+
date: z.string(),
|
|
348
|
+
amount: z.number()
|
|
237
349
|
}),
|
|
238
350
|
model: Models.SWIZZY_DEFAULT,
|
|
239
|
-
update: (
|
|
240
|
-
|
|
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
|
-
|
|
247
|
-
|
|
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: '
|
|
394
|
+
id: 'step_1',
|
|
252
395
|
update: (result, context, actions) => {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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.
|
|
267
|
-
id: '
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
451
|
+
---
|
|
280
452
|
|
|
281
|
-
|
|
453
|
+
#### `actions.retry()`
|
|
282
454
|
|
|
283
|
-
|
|
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
|
-
|
|
291
|
-
id: '
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
484
|
+
**Use when**: The LLM output needs improvement or validation failed.
|
|
301
485
|
|
|
302
|
-
|
|
486
|
+
**Pro tip**: Track retry counts to avoid infinite loops.
|
|
303
487
|
|
|
304
|
-
|
|
305
|
-
Use `{{variableName}}` in instructions for simple value replacement:
|
|
488
|
+
---
|
|
306
489
|
|
|
307
|
-
|
|
308
|
-
wizard.setContext({
|
|
309
|
-
userName: 'Bob',
|
|
310
|
-
task: 'write a poem'
|
|
311
|
-
});
|
|
490
|
+
#### `actions.stop()`
|
|
312
491
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
321
|
-
|
|
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.
|
|
325
|
-
id: '
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
531
|
+
**Use when**: Rate limiting, waiting for external systems, or pacing execution.
|
|
338
532
|
|
|
339
|
-
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
### Signal Patterns
|
|
340
536
|
|
|
341
|
-
####
|
|
342
|
-
Generates structured text using LLMs with schema validation.
|
|
537
|
+
#### Sequential Execution
|
|
343
538
|
|
|
344
539
|
```javascript
|
|
345
540
|
wizard.addTextStep({
|
|
346
|
-
id: '
|
|
347
|
-
instruction: '
|
|
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: (
|
|
355
|
-
|
|
356
|
-
actions.
|
|
357
|
-
|
|
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
|
-
####
|
|
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: '
|
|
370
|
-
instruction: 'Validate the extracted entities',
|
|
564
|
+
id: 'router',
|
|
371
565
|
update: (result, context, actions) => {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
####
|
|
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: '
|
|
393
|
-
instruction: '
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
|
|
602
|
+
#### Loop Pattern
|
|
403
603
|
|
|
404
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
##
|
|
640
|
+
## Advanced Patterns
|
|
426
641
|
|
|
427
|
-
|
|
642
|
+
### The Bungee Action: Parallelism
|
|
428
643
|
|
|
429
|
-
|
|
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
|
-
|
|
646
|
+
#### Anatomy of a Bungee
|
|
432
647
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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[
|
|
442
|
-
B --> C[
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
C --> F[
|
|
446
|
-
D -->
|
|
447
|
-
E -->
|
|
448
|
-
F --> G
|
|
449
|
-
G --> H[
|
|
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
|
-
|
|
667
|
+
---
|
|
454
668
|
|
|
455
|
-
|
|
669
|
+
#### Basic Bungee Example
|
|
456
670
|
|
|
457
671
|
```javascript
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
//
|
|
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(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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.
|
|
774
|
+
if (result.hasRelevantInfo && result.relevanceScore >= 7) {
|
|
482
775
|
actions.updateContext({
|
|
483
|
-
[`page_${context.pageNumber}
|
|
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
|
-
**
|
|
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
|
-
|
|
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('
|
|
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:
|
|
504
|
-
timeout: 30000 //
|
|
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
|
-
|
|
512
|
-
|
|
513
|
-
### Real-Time Visualization
|
|
845
|
+
### Multi-API Configuration
|
|
514
846
|
|
|
515
|
-
|
|
847
|
+
Wizard supports multiple LLM providers. Configure API keys for different models:
|
|
516
848
|
|
|
517
849
|
```javascript
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
859
|
+
// Use different models for different tasks
|
|
532
860
|
wizard.addTextStep({
|
|
533
|
-
id: '
|
|
534
|
-
instruction: '
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
-
|
|
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
|
-
|
|
883
|
+
## Complete Example
|
|
558
884
|
|
|
559
|
-
|
|
885
|
+
Here's a full workflow that demonstrates all concepts:
|
|
560
886
|
|
|
561
|
-
```
|
|
562
|
-
|
|
887
|
+
```javascript
|
|
888
|
+
const { Wizard, Models } = require('@swizzy_ai/kit');
|
|
889
|
+
const { z } = require('zod');
|
|
563
890
|
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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: (
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
|
|
643
|
-
updateContext: (updates:
|
|
644
|
-
llmClient: LLMClient; // Direct LLM access if needed
|
|
645
|
-
goto: (stepId: string) => FlowControlSignal;
|
|
1035
|
+
{
|
|
1036
|
+
updateContext: (updates: object) => void;
|
|
646
1037
|
next: () => FlowControlSignal;
|
|
647
|
-
|
|
1038
|
+
goto: (stepId: string) => FlowControlSignal;
|
|
648
1039
|
retry: () => FlowControlSignal;
|
|
1040
|
+
stop: () => FlowControlSignal;
|
|
649
1041
|
wait: () => FlowControlSignal;
|
|
650
1042
|
bungee: {
|
|
651
|
-
init: () => BungeeBuilder;
|
|
652
|
-
}
|
|
1043
|
+
init: () => BungeeBuilder;
|
|
1044
|
+
}
|
|
653
1045
|
}
|
|
654
1046
|
```
|
|
655
1047
|
|
|
656
|
-
###
|
|
1048
|
+
### Bungee Builder Interface
|
|
657
1049
|
|
|
658
1050
|
```typescript
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
-
##
|
|
1070
|
+
## Comparison with Alternatives
|
|
669
1071
|
|
|
670
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1085
|
+
## Examples
|
|
681
1086
|
|
|
682
|
-
|
|
683
|
-
# Install dependencies
|
|
684
|
-
npm install
|
|
1087
|
+
Check the `/examples` directory for complete workflows:
|
|
685
1088
|
|
|
686
|
-
|
|
687
|
-
|
|
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
|
-
|
|
690
|
-
npm run build
|
|
1095
|
+
---
|
|
691
1096
|
|
|
692
|
-
|
|
693
|
-
cd examples && node document-reader.js
|
|
694
|
-
```
|
|
1097
|
+
## Contributing
|
|
695
1098
|
|
|
696
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
-
|
|
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">
|