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