@wundr.io/langgraph-orchestrator 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +842 -0
  2. package/dist/checkpointing.d.ts +265 -0
  3. package/dist/checkpointing.d.ts.map +1 -0
  4. package/dist/checkpointing.js +577 -0
  5. package/dist/checkpointing.js.map +1 -0
  6. package/dist/edges/conditional-edge.d.ts +230 -0
  7. package/dist/edges/conditional-edge.d.ts.map +1 -0
  8. package/dist/edges/conditional-edge.js +439 -0
  9. package/dist/edges/conditional-edge.js.map +1 -0
  10. package/dist/edges/loop-edge.d.ts +290 -0
  11. package/dist/edges/loop-edge.d.ts.map +1 -0
  12. package/dist/edges/loop-edge.js +503 -0
  13. package/dist/edges/loop-edge.js.map +1 -0
  14. package/dist/index.d.ts +125 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +269 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/nodes/decision-node.d.ts +276 -0
  19. package/dist/nodes/decision-node.d.ts.map +1 -0
  20. package/dist/nodes/decision-node.js +403 -0
  21. package/dist/nodes/decision-node.js.map +1 -0
  22. package/dist/nodes/human-node.d.ts +272 -0
  23. package/dist/nodes/human-node.d.ts.map +1 -0
  24. package/dist/nodes/human-node.js +394 -0
  25. package/dist/nodes/human-node.js.map +1 -0
  26. package/dist/nodes/llm-node.d.ts +173 -0
  27. package/dist/nodes/llm-node.d.ts.map +1 -0
  28. package/dist/nodes/llm-node.js +325 -0
  29. package/dist/nodes/llm-node.js.map +1 -0
  30. package/dist/nodes/tool-node.d.ts +151 -0
  31. package/dist/nodes/tool-node.d.ts.map +1 -0
  32. package/dist/nodes/tool-node.js +373 -0
  33. package/dist/nodes/tool-node.js.map +1 -0
  34. package/dist/prebuilt-graphs/plan-execute-refine.d.ts +149 -0
  35. package/dist/prebuilt-graphs/plan-execute-refine.d.ts.map +1 -0
  36. package/dist/prebuilt-graphs/plan-execute-refine.js +600 -0
  37. package/dist/prebuilt-graphs/plan-execute-refine.js.map +1 -0
  38. package/dist/state-graph.d.ts +158 -0
  39. package/dist/state-graph.d.ts.map +1 -0
  40. package/dist/state-graph.js +756 -0
  41. package/dist/state-graph.js.map +1 -0
  42. package/dist/types.d.ts +762 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +73 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +57 -0
  47. package/src/checkpointing.ts +702 -0
  48. package/src/edges/conditional-edge.ts +518 -0
  49. package/src/edges/loop-edge.ts +623 -0
  50. package/src/index.ts +416 -0
  51. package/src/nodes/decision-node.ts +538 -0
  52. package/src/nodes/human-node.ts +572 -0
  53. package/src/nodes/llm-node.ts +448 -0
  54. package/src/nodes/tool-node.ts +525 -0
  55. package/src/prebuilt-graphs/plan-execute-refine.ts +769 -0
  56. package/src/state-graph.ts +990 -0
  57. package/src/types.ts +729 -0
package/README.md ADDED
@@ -0,0 +1,842 @@
1
+ # @wundr.io/langgraph-orchestrator
2
+
3
+ LangGraph-style cyclic, state-driven workflow orchestration for AI agents. This package provides a powerful framework for building complex AI workflows with conditional branching, state management, checkpointing, and human-in-the-loop capabilities.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @wundr.io/langgraph-orchestrator
9
+ # or
10
+ pnpm add @wundr.io/langgraph-orchestrator
11
+ # or
12
+ yarn add @wundr.io/langgraph-orchestrator
13
+ ```
14
+
15
+ ## Package Overview
16
+
17
+ The `@wundr.io/langgraph-orchestrator` package enables you to build sophisticated AI agent workflows using a state graph paradigm. Key features include:
18
+
19
+ - **State Graph Architecture**: Define workflows as directed graphs with nodes and edges
20
+ - **Cyclic Workflows**: Support for loops and iterative processing patterns
21
+ - **Decision Nodes**: Conditional branching based on state values
22
+ - **Checkpointing**: Save and restore workflow state for fault tolerance
23
+ - **Human-in-the-Loop**: Pause workflows for human input or approval
24
+ - **Type Safety**: Full TypeScript support with Zod schema validation
25
+
26
+ ## Quick Start
27
+
28
+ ```typescript
29
+ import {
30
+ StateGraph,
31
+ createLLMNode,
32
+ createToolNode,
33
+ createDecisionNode,
34
+ MemoryCheckpointer
35
+ } from '@wundr.io/langgraph-orchestrator';
36
+
37
+ // Create a workflow graph
38
+ const graph = new StateGraph('my-workflow')
39
+ .addNode('agent', createLLMNode({
40
+ id: 'agent',
41
+ name: 'Agent',
42
+ config: { model: 'claude-3-sonnet-20240229' }
43
+ }))
44
+ .addNode('tools', createToolNode({
45
+ id: 'tools',
46
+ name: 'Tools'
47
+ }))
48
+ .addEdge('agent', 'tools')
49
+ .addConditionalEdge('tools', 'agent', {
50
+ type: 'exists',
51
+ field: 'data.pendingToolCalls'
52
+ })
53
+ .setEntryPoint('agent')
54
+ .setCheckpointer(new MemoryCheckpointer());
55
+
56
+ // Execute the workflow
57
+ const result = await graph.execute({
58
+ initialState: {
59
+ data: { task: 'Research AI developments' }
60
+ }
61
+ });
62
+ ```
63
+
64
+ ## Core Concepts
65
+
66
+ ### StateGraph
67
+
68
+ The `StateGraph` class is the foundation for defining workflows. It manages nodes, edges, and execution flow.
69
+
70
+ ```typescript
71
+ const graph = new StateGraph('workflow-name', {
72
+ maxIterations: 100, // Cycle protection
73
+ timeout: 300000, // 5 minute timeout
74
+ checkpointEnabled: true, // Enable state persistence
75
+ logLevel: 'info'
76
+ });
77
+ ```
78
+
79
+ ### AgentState
80
+
81
+ All workflows operate on an `AgentState` object that maintains:
82
+
83
+ - `id`: Unique state identifier
84
+ - `messages`: Conversation history
85
+ - `data`: Arbitrary key-value store for workflow data
86
+ - `currentStep`: Current node in execution
87
+ - `history`: State history for debugging
88
+ - `metadata`: Execution tracking metadata
89
+
90
+ ### Nodes
91
+
92
+ Nodes are the processing units in your workflow. Each node receives state, performs operations, and returns updated state with optional next node specification.
93
+
94
+ ### Edges
95
+
96
+ Edges define transitions between nodes:
97
+
98
+ - **Direct edges**: Always follow to the target node
99
+ - **Conditional edges**: Follow based on state conditions
100
+ - **Loop edges**: Enable cyclic workflows
101
+ - **Parallel edges**: Branch to multiple nodes
102
+
103
+ ---
104
+
105
+ ## Decision Nodes
106
+
107
+ Decision nodes enable conditional branching in your workflows based on state values. The package provides five decision node factory functions, each suited for different routing patterns.
108
+
109
+ ### Condition Types
110
+
111
+ All decision nodes use condition types to evaluate state values:
112
+
113
+ | Type | Description | Example |
114
+ |------|-------------|---------|
115
+ | `equals` | Exact match comparison | `{ type: 'equals', field: 'data.status', value: 'complete' }` |
116
+ | `not_equals` | Inverse of equals | `{ type: 'not_equals', field: 'data.error', value: null }` |
117
+ | `contains` | Array includes or string contains | `{ type: 'contains', field: 'data.tags', value: 'urgent' }` |
118
+ | `greater_than` | Numeric comparison (>) | `{ type: 'greater_than', field: 'data.score', value: 0.8 }` |
119
+ | `less_than` | Numeric comparison (<) | `{ type: 'less_than', field: 'data.retries', value: 3 }` |
120
+ | `exists` | Value is not null/undefined | `{ type: 'exists', field: 'data.result' }` |
121
+ | `not_exists` | Value is null/undefined | `{ type: 'not_exists', field: 'error' }` |
122
+ | `custom` | Custom evaluation function | `{ type: 'custom', evaluate: async (state) => state.data.x > 10 }` |
123
+
124
+ ### Field Path Notation
125
+
126
+ Conditions use dot notation to access nested state values:
127
+
128
+ ```typescript
129
+ // Access state.data.user.role
130
+ { type: 'equals', field: 'data.user.role', value: 'admin' }
131
+
132
+ // Access state.messages
133
+ { type: 'exists', field: 'messages' }
134
+
135
+ // Access state.metadata.stepCount
136
+ { type: 'greater_than', field: 'metadata.stepCount', value: 5 }
137
+ ```
138
+
139
+ ---
140
+
141
+ ### createDecisionNode
142
+
143
+ The most flexible decision node, allowing multiple branches with priority-based evaluation.
144
+
145
+ **API:**
146
+
147
+ ```typescript
148
+ function createDecisionNode<TState extends AgentState = AgentState>(options: {
149
+ id: string; // Unique node identifier
150
+ name: string; // Human-readable name
151
+ config: DecisionNodeConfig; // Decision configuration
152
+ nodeConfig?: NodeConfig; // Optional node settings
153
+ }): NodeDefinition<TState>
154
+ ```
155
+
156
+ **DecisionNodeConfig:**
157
+
158
+ ```typescript
159
+ interface DecisionNodeConfig {
160
+ branches: DecisionBranch[]; // Decision branches
161
+ defaultBranch?: string; // Fallback target node
162
+ throwOnNoMatch?: boolean; // Error if no match (default: false)
163
+ decide?: (state: AgentState) => string | Promise<string>; // Custom function
164
+ }
165
+
166
+ interface DecisionBranch {
167
+ name: string; // Branch identifier
168
+ target: string; // Target node name
169
+ condition: EdgeCondition; // Branch condition
170
+ priority?: number; // Evaluation order (higher first)
171
+ }
172
+ ```
173
+
174
+ **Example:**
175
+
176
+ ```typescript
177
+ import { createDecisionNode } from '@wundr.io/langgraph-orchestrator';
178
+
179
+ const router = createDecisionNode({
180
+ id: 'router',
181
+ name: 'Task Router',
182
+ config: {
183
+ branches: [
184
+ {
185
+ name: 'search',
186
+ target: 'search-node',
187
+ condition: { type: 'equals', field: 'data.action', value: 'search' },
188
+ priority: 2
189
+ },
190
+ {
191
+ name: 'answer',
192
+ target: 'answer-node',
193
+ condition: { type: 'equals', field: 'data.action', value: 'answer' },
194
+ priority: 1
195
+ }
196
+ ],
197
+ defaultBranch: 'fallback-node'
198
+ }
199
+ });
200
+
201
+ graph.addNode('router', router);
202
+ ```
203
+
204
+ **With Custom Decision Function:**
205
+
206
+ ```typescript
207
+ const customRouter = createDecisionNode({
208
+ id: 'smart-router',
209
+ name: 'Smart Router',
210
+ config: {
211
+ branches: [], // Not used when decide function is provided
212
+ decide: async (state) => {
213
+ const score = state.data.confidence as number;
214
+ if (score > 0.9) return 'high-confidence-handler';
215
+ if (score > 0.5) return 'medium-confidence-handler';
216
+ return 'low-confidence-handler';
217
+ }
218
+ }
219
+ });
220
+ ```
221
+
222
+ ---
223
+
224
+ ### createSwitchNode
225
+
226
+ A switch-case style decision node for routing based on a single field value. Ideal for enumerated routing scenarios.
227
+
228
+ **API:**
229
+
230
+ ```typescript
231
+ function createSwitchNode<TState extends AgentState = AgentState>(options: {
232
+ id: string; // Unique node identifier
233
+ name: string; // Human-readable name
234
+ field: string; // State field to switch on
235
+ cases: Record<string, string>; // Value-to-target mapping
236
+ default?: string; // Default target if no case matches
237
+ nodeConfig?: NodeConfig; // Optional node settings
238
+ }): NodeDefinition<TState>
239
+ ```
240
+
241
+ **Example:**
242
+
243
+ ```typescript
244
+ import { createSwitchNode } from '@wundr.io/langgraph-orchestrator';
245
+
246
+ const typeSwitch = createSwitchNode({
247
+ id: 'type-switch',
248
+ name: 'Message Type Switch',
249
+ field: 'data.messageType',
250
+ cases: {
251
+ 'question': 'question-handler',
252
+ 'command': 'command-handler',
253
+ 'feedback': 'feedback-handler'
254
+ },
255
+ default: 'unknown-handler'
256
+ });
257
+
258
+ graph.addNode('type-switch', typeSwitch);
259
+ ```
260
+
261
+ **Practical Use Case - Support Ticket Routing:**
262
+
263
+ ```typescript
264
+ const ticketRouter = createSwitchNode({
265
+ id: 'ticket-router',
266
+ name: 'Support Ticket Router',
267
+ field: 'data.department',
268
+ cases: {
269
+ 'billing': 'billing-team',
270
+ 'technical': 'tech-support',
271
+ 'sales': 'sales-team',
272
+ 'legal': 'legal-team'
273
+ },
274
+ default: 'general-support'
275
+ });
276
+ ```
277
+
278
+ ---
279
+
280
+ ### createThresholdNode
281
+
282
+ Routes based on numeric thresholds, perfect for confidence scores, priority levels, or any numeric classification.
283
+
284
+ **API:**
285
+
286
+ ```typescript
287
+ function createThresholdNode<TState extends AgentState = AgentState>(options: {
288
+ id: string; // Unique node identifier
289
+ name: string; // Human-readable name
290
+ field: string; // Numeric field to evaluate
291
+ thresholds: Array<{ // Threshold definitions
292
+ value: number; // Threshold value
293
+ target: string; // Target node
294
+ }>;
295
+ default?: string; // Target for values below all thresholds
296
+ nodeConfig?: NodeConfig; // Optional node settings
297
+ }): NodeDefinition<TState>
298
+ ```
299
+
300
+ **Threshold Evaluation:**
301
+
302
+ Thresholds are automatically sorted in descending order. A value matches a threshold if it is greater than or equal to the threshold value but less than the next higher threshold.
303
+
304
+ **Example:**
305
+
306
+ ```typescript
307
+ import { createThresholdNode } from '@wundr.io/langgraph-orchestrator';
308
+
309
+ const confidenceRouter = createThresholdNode({
310
+ id: 'confidence-router',
311
+ name: 'Confidence Router',
312
+ field: 'data.confidence',
313
+ thresholds: [
314
+ { value: 0.9, target: 'high-confidence' }, // >= 0.9
315
+ { value: 0.7, target: 'medium-confidence' }, // >= 0.7 and < 0.9
316
+ { value: 0.5, target: 'low-confidence' } // >= 0.5 and < 0.7
317
+ ],
318
+ default: 'very-low-confidence' // < 0.5
319
+ });
320
+
321
+ graph.addNode('confidence-router', confidenceRouter);
322
+ ```
323
+
324
+ **Practical Use Case - Priority Queue:**
325
+
326
+ ```typescript
327
+ const priorityRouter = createThresholdNode({
328
+ id: 'priority-router',
329
+ name: 'Task Priority Router',
330
+ field: 'data.priority',
331
+ thresholds: [
332
+ { value: 90, target: 'critical-queue' },
333
+ { value: 70, target: 'high-priority-queue' },
334
+ { value: 40, target: 'normal-queue' },
335
+ { value: 10, target: 'low-priority-queue' }
336
+ ],
337
+ default: 'backlog-queue'
338
+ });
339
+ ```
340
+
341
+ ---
342
+
343
+ ### createIfElseNode
344
+
345
+ A simple binary decision node for true/false branching based on a single condition.
346
+
347
+ **API:**
348
+
349
+ ```typescript
350
+ function createIfElseNode<TState extends AgentState = AgentState>(options: {
351
+ id: string; // Unique node identifier
352
+ name: string; // Human-readable name
353
+ condition: EdgeCondition; // Condition to evaluate
354
+ ifTrue: string; // Target node if condition is true
355
+ ifFalse: string; // Target node if condition is false
356
+ nodeConfig?: NodeConfig; // Optional node settings
357
+ }): NodeDefinition<TState>
358
+ ```
359
+
360
+ **Example:**
361
+
362
+ ```typescript
363
+ import { createIfElseNode } from '@wundr.io/langgraph-orchestrator';
364
+
365
+ const errorCheck = createIfElseNode({
366
+ id: 'has-error',
367
+ name: 'Error Check',
368
+ condition: {
369
+ type: 'exists',
370
+ field: 'error'
371
+ },
372
+ ifTrue: 'error-handler',
373
+ ifFalse: 'success-handler'
374
+ });
375
+
376
+ graph.addNode('has-error', errorCheck);
377
+ ```
378
+
379
+ **Practical Use Cases:**
380
+
381
+ ```typescript
382
+ // Authentication check
383
+ const authCheck = createIfElseNode({
384
+ id: 'auth-check',
385
+ name: 'Authentication Check',
386
+ condition: { type: 'exists', field: 'data.userId' },
387
+ ifTrue: 'authenticated-flow',
388
+ ifFalse: 'login-required'
389
+ });
390
+
391
+ // Retry decision
392
+ const retryCheck = createIfElseNode({
393
+ id: 'retry-check',
394
+ name: 'Retry Decision',
395
+ condition: { type: 'less_than', field: 'data.retryCount', value: 3 },
396
+ ifTrue: 'retry-operation',
397
+ ifFalse: 'max-retries-reached'
398
+ });
399
+
400
+ // Feature flag
401
+ const featureFlag = createIfElseNode({
402
+ id: 'feature-flag',
403
+ name: 'New Feature Check',
404
+ condition: { type: 'equals', field: 'data.features.newUI', value: true },
405
+ ifTrue: 'new-ui-flow',
406
+ ifFalse: 'legacy-ui-flow'
407
+ });
408
+ ```
409
+
410
+ ---
411
+
412
+ ### createMultiConditionNode
413
+
414
+ Routes based on multiple conditions combined with AND/OR logic. Ideal for complex business rules.
415
+
416
+ **API:**
417
+
418
+ ```typescript
419
+ function createMultiConditionNode<TState extends AgentState = AgentState>(options: {
420
+ id: string; // Unique node identifier
421
+ name: string; // Human-readable name
422
+ branches: Array<{
423
+ name: string; // Branch identifier
424
+ target: string; // Target node
425
+ conditions: EdgeCondition[]; // Array of conditions
426
+ logic: 'AND' | 'OR'; // How to combine conditions
427
+ priority?: number; // Evaluation order
428
+ }>;
429
+ default?: string; // Default target if no branch matches
430
+ nodeConfig?: NodeConfig; // Optional node settings
431
+ }): NodeDefinition<TState>
432
+ ```
433
+
434
+ **Logic Evaluation:**
435
+
436
+ - `AND`: All conditions must be true for the branch to match
437
+ - `OR`: At least one condition must be true for the branch to match
438
+
439
+ **Example:**
440
+
441
+ ```typescript
442
+ import { createMultiConditionNode } from '@wundr.io/langgraph-orchestrator';
443
+
444
+ const complexRouter = createMultiConditionNode({
445
+ id: 'complex-router',
446
+ name: 'Complex Router',
447
+ branches: [
448
+ {
449
+ name: 'premium-user',
450
+ target: 'premium-flow',
451
+ conditions: [
452
+ { type: 'equals', field: 'data.userType', value: 'premium' },
453
+ { type: 'greater_than', field: 'data.credits', value: 0 }
454
+ ],
455
+ logic: 'AND', // Must be premium AND have credits
456
+ priority: 2
457
+ },
458
+ {
459
+ name: 'needs-upgrade',
460
+ target: 'upgrade-flow',
461
+ conditions: [
462
+ { type: 'equals', field: 'data.userType', value: 'free' },
463
+ { type: 'less_than', field: 'data.credits', value: 1 }
464
+ ],
465
+ logic: 'OR', // Either free user OR out of credits
466
+ priority: 1
467
+ }
468
+ ],
469
+ default: 'standard-flow'
470
+ });
471
+
472
+ graph.addNode('complex-router', complexRouter);
473
+ ```
474
+
475
+ **Practical Use Case - Access Control:**
476
+
477
+ ```typescript
478
+ const accessControl = createMultiConditionNode({
479
+ id: 'access-control',
480
+ name: 'Access Control',
481
+ branches: [
482
+ {
483
+ name: 'admin-access',
484
+ target: 'admin-dashboard',
485
+ conditions: [
486
+ { type: 'equals', field: 'data.role', value: 'admin' },
487
+ { type: 'equals', field: 'data.verified', value: true }
488
+ ],
489
+ logic: 'AND',
490
+ priority: 3
491
+ },
492
+ {
493
+ name: 'power-user',
494
+ target: 'advanced-features',
495
+ conditions: [
496
+ { type: 'equals', field: 'data.role', value: 'power-user' },
497
+ { type: 'greater_than', field: 'data.accountAge', value: 30 }
498
+ ],
499
+ logic: 'AND',
500
+ priority: 2
501
+ },
502
+ {
503
+ name: 'suspended-or-unverified',
504
+ target: 'restricted-access',
505
+ conditions: [
506
+ { type: 'equals', field: 'data.suspended', value: true },
507
+ { type: 'equals', field: 'data.verified', value: false }
508
+ ],
509
+ logic: 'OR',
510
+ priority: 1
511
+ }
512
+ ],
513
+ default: 'standard-user-flow'
514
+ });
515
+ ```
516
+
517
+ ---
518
+
519
+ ## State Management
520
+
521
+ ### Creating Initial State
522
+
523
+ ```typescript
524
+ const result = await graph.execute({
525
+ initialState: {
526
+ data: {
527
+ task: 'Process user request',
528
+ userId: '12345',
529
+ config: { maxRetries: 3 }
530
+ }
531
+ }
532
+ });
533
+ ```
534
+
535
+ ### State Updates in Nodes
536
+
537
+ Nodes return updated state in their `NodeResult`:
538
+
539
+ ```typescript
540
+ const processNode: NodeDefinition = {
541
+ id: 'process',
542
+ name: 'Process Node',
543
+ type: 'transform',
544
+ config: {},
545
+ execute: async (state, context) => {
546
+ // Immutably update state
547
+ const newState = {
548
+ ...state,
549
+ data: {
550
+ ...state.data,
551
+ processed: true,
552
+ result: 'Some computation result'
553
+ }
554
+ };
555
+
556
+ return {
557
+ state: newState,
558
+ next: 'next-node' // Optional: specify next node
559
+ };
560
+ }
561
+ };
562
+ ```
563
+
564
+ ### State History
565
+
566
+ The graph automatically tracks state changes in the `history` array:
567
+
568
+ ```typescript
569
+ // Access execution history
570
+ result.state.history.forEach(entry => {
571
+ console.log(`Step: ${entry.step}`);
572
+ console.log(`Changes: ${JSON.stringify(entry.changes)}`);
573
+ });
574
+ ```
575
+
576
+ ---
577
+
578
+ ## Node Execution Flow
579
+
580
+ ### Node Result
581
+
582
+ Every node returns a `NodeResult`:
583
+
584
+ ```typescript
585
+ interface NodeResult<TState> {
586
+ state: TState; // Updated state
587
+ next?: string | string[]; // Next node(s) - optional
588
+ terminate?: boolean; // Stop execution
589
+ metadata?: {
590
+ duration: number; // Execution time
591
+ tokensUsed?: number; // LLM tokens consumed
592
+ toolCalls?: ToolCall[]; // Tools executed
593
+ retryCount?: number; // Retry attempts
594
+ };
595
+ }
596
+ ```
597
+
598
+ ### Execution Handlers
599
+
600
+ Monitor workflow execution with event handlers:
601
+
602
+ ```typescript
603
+ const result = await graph.execute({
604
+ handlers: {
605
+ onStart: (state) => console.log('Started:', state.id),
606
+ onNodeEnter: (nodeName, state) => console.log(`Entering: ${nodeName}`),
607
+ onNodeExit: (nodeName, result) => console.log(`Exiting: ${nodeName}`),
608
+ onCheckpoint: (checkpoint) => console.log('Checkpoint:', checkpoint.id),
609
+ onError: (error) => console.error('Error:', error.message),
610
+ onComplete: (state) => console.log('Complete:', state.id)
611
+ }
612
+ });
613
+ ```
614
+
615
+ ### Event Emitter
616
+
617
+ The `StateGraph` class extends `EventEmitter` for reactive patterns:
618
+
619
+ ```typescript
620
+ graph.on('execution:start', (state) => {
621
+ console.log('Workflow started');
622
+ });
623
+
624
+ graph.on('node:enter', (nodeName, state) => {
625
+ metrics.trackNodeEntry(nodeName);
626
+ });
627
+
628
+ graph.on('state:updated', (state) => {
629
+ saveToDatabase(state);
630
+ });
631
+ ```
632
+
633
+ ---
634
+
635
+ ## Checkpointing
636
+
637
+ ### Memory Checkpointer
638
+
639
+ For development and testing:
640
+
641
+ ```typescript
642
+ import { MemoryCheckpointer } from '@wundr.io/langgraph-orchestrator';
643
+
644
+ const checkpointer = new MemoryCheckpointer();
645
+ graph.setCheckpointer(checkpointer);
646
+ ```
647
+
648
+ ### File Checkpointer
649
+
650
+ For persistent storage:
651
+
652
+ ```typescript
653
+ import { FileCheckpointer } from '@wundr.io/langgraph-orchestrator';
654
+
655
+ const checkpointer = new FileCheckpointer({
656
+ directory: './checkpoints',
657
+ format: 'json'
658
+ });
659
+ graph.setCheckpointer(checkpointer);
660
+ ```
661
+
662
+ ### Resuming from Checkpoint
663
+
664
+ ```typescript
665
+ // Resume execution from a specific checkpoint
666
+ const result = await graph.execute({
667
+ resumeFrom: 'checkpoint-id-here'
668
+ });
669
+ ```
670
+
671
+ ---
672
+
673
+ ## Complete Example: AI Research Assistant
674
+
675
+ ```typescript
676
+ import {
677
+ StateGraph,
678
+ createLLMNode,
679
+ createToolNode,
680
+ createDecisionNode,
681
+ createIfElseNode,
682
+ createThresholdNode,
683
+ MemoryCheckpointer
684
+ } from '@wundr.io/langgraph-orchestrator';
685
+
686
+ // Define custom state
687
+ interface ResearchState extends AgentState {
688
+ data: {
689
+ query: string;
690
+ searchResults?: string[];
691
+ analysis?: string;
692
+ confidence?: number;
693
+ needsMoreInfo?: boolean;
694
+ };
695
+ }
696
+
697
+ // Create the research workflow
698
+ const researchGraph = new StateGraph<ResearchState>('research-assistant', {
699
+ maxIterations: 20
700
+ });
701
+
702
+ // Add nodes
703
+ researchGraph
704
+ // Initial planning node
705
+ .addNode('planner', createLLMNode({
706
+ id: 'planner',
707
+ name: 'Research Planner',
708
+ config: { systemPrompt: 'Plan the research strategy for the given query.' }
709
+ }))
710
+
711
+ // Search execution
712
+ .addNode('searcher', createToolNode({
713
+ id: 'searcher',
714
+ name: 'Web Searcher',
715
+ config: { tools: [webSearchTool] }
716
+ }))
717
+
718
+ // Analyze search results
719
+ .addNode('analyzer', createLLMNode({
720
+ id: 'analyzer',
721
+ name: 'Result Analyzer',
722
+ config: { systemPrompt: 'Analyze the search results and assess confidence.' }
723
+ }))
724
+
725
+ // Decision: enough information?
726
+ .addNode('confidence-check', createThresholdNode({
727
+ id: 'confidence-check',
728
+ name: 'Confidence Check',
729
+ field: 'data.confidence',
730
+ thresholds: [
731
+ { value: 0.8, target: 'synthesizer' },
732
+ { value: 0.5, target: 'refiner' }
733
+ ],
734
+ default: 'searcher' // Low confidence, search more
735
+ }))
736
+
737
+ // Refine search query
738
+ .addNode('refiner', createLLMNode({
739
+ id: 'refiner',
740
+ name: 'Query Refiner',
741
+ config: { systemPrompt: 'Refine the search query based on gaps identified.' }
742
+ }))
743
+
744
+ // Synthesize final answer
745
+ .addNode('synthesizer', createLLMNode({
746
+ id: 'synthesizer',
747
+ name: 'Answer Synthesizer',
748
+ config: { systemPrompt: 'Synthesize a comprehensive answer from all research.' }
749
+ }))
750
+
751
+ // Define edges
752
+ .addEdge('planner', 'searcher')
753
+ .addEdge('searcher', 'analyzer')
754
+ .addEdge('analyzer', 'confidence-check')
755
+ .addEdge('refiner', 'searcher')
756
+
757
+ // Set entry point and checkpointer
758
+ .setEntryPoint('planner')
759
+ .setCheckpointer(new MemoryCheckpointer());
760
+
761
+ // Execute research
762
+ const result = await researchGraph.execute({
763
+ initialState: {
764
+ data: {
765
+ query: 'What are the latest advances in quantum computing?'
766
+ }
767
+ }
768
+ });
769
+
770
+ console.log('Research complete:', result.state.data.analysis);
771
+ console.log('Confidence:', result.state.data.confidence);
772
+ console.log('Path taken:', result.path.join(' -> '));
773
+ ```
774
+
775
+ ---
776
+
777
+ ## API Reference
778
+
779
+ ### Exports
780
+
781
+ #### Core Classes
782
+
783
+ - `StateGraph` - Main graph class for workflow definition and execution
784
+ - `StateGraphEvents` - Event types emitted by StateGraph
785
+
786
+ #### Node Factories
787
+
788
+ - `createLLMNode` - LLM-based processing node
789
+ - `createToolNode` - Tool execution node
790
+ - `createDecisionNode` - Multi-branch decision node
791
+ - `createSwitchNode` - Switch-case style routing
792
+ - `createThresholdNode` - Numeric threshold routing
793
+ - `createIfElseNode` - Binary decision node
794
+ - `createMultiConditionNode` - Complex multi-condition routing
795
+ - `createHumanNode` - Human-in-the-loop node
796
+
797
+ #### Edge Builders
798
+
799
+ - `conditionalEdge` - Conditional edge builder
800
+ - `loopEdge` - Loop edge builder
801
+ - `createRouter` - Dynamic routing helper
802
+
803
+ #### Checkpointing
804
+
805
+ - `MemoryCheckpointer` - In-memory checkpoint storage
806
+ - `FileCheckpointer` - File-based checkpoint storage
807
+ - `TimeTravelDebugger` - Debug tool for state history
808
+
809
+ #### Prebuilt Graphs
810
+
811
+ - `createAgentWorkflow` - Simple agent with tools
812
+ - `createChatWorkflow` - Conversational workflow
813
+ - `createDecisionTree` - Multi-level decision tree
814
+ - `createPlanExecuteRefineGraph` - Plan-execute-refine pattern
815
+
816
+ ### Types
817
+
818
+ All TypeScript types are exported for use in your projects:
819
+
820
+ ```typescript
821
+ import type {
822
+ AgentState,
823
+ NodeDefinition,
824
+ NodeResult,
825
+ EdgeCondition,
826
+ ConditionType,
827
+ ExecutionResult,
828
+ ExecutionOptions
829
+ } from '@wundr.io/langgraph-orchestrator';
830
+ ```
831
+
832
+ ---
833
+
834
+ ## License
835
+
836
+ MIT
837
+
838
+ ## Links
839
+
840
+ - [Repository](https://github.com/adapticai/wundr)
841
+ - [Issues](https://github.com/adapticai/wundr/issues)
842
+ - [Homepage](https://wundr.io)