@@ -21,36 +21,36 @@ flexible control flow patterns.
2121### Basic Flow
2222
2323``` typescript
24- import { ai , AxFlow } from " @ax-llm/ax" ;
24+ import { ai , flow } from " @ax-llm/ax" ;
2525
2626// Create AI instance
2727const llm = ai ({ name: " openai" , apiKey: process .env .OPENAI_APIKEY ! });
2828
29- // Create a simple flow
30- const flow = new AxFlow <{ userInput: string }, { responseText: string }>()
29+ // Create a simple flow (factory function)
30+ const wf = flow <{ userInput: string }, { responseText: string }>()
3131 .node (" testNode" , " userInput:string -> responseText:string" )
3232 .execute (" testNode" , (state ) => ({ userInput: state .userInput }))
33- .map ((state ) => ({ responseText: state .testNodeResult .responseText }));
33+ .returns ((state ) => ({ responseText: state .testNodeResult .responseText }));
3434
3535// Execute the flow
36- const result = await flow .forward (llm , { userInput: " Hello world" });
36+ const result = await wf .forward (llm , { userInput: " Hello world" });
3737console .log (result .responseText );
3838```
3939
40- ### Constructor Options
40+ ### Factory Options
4141
4242``` typescript
43- // Basic constructor
44- const flow = new AxFlow ();
43+ // Basic factory
44+ const wf = flow ();
4545
4646// With options
47- const flow = new AxFlow ({ autoParallel: false });
47+ const wf = flow ({ autoParallel: false });
4848
4949// With explicit typing
50- const flow = new AxFlow <InputType , OutputType >();
50+ const wf = flow <InputType , OutputType >();
5151
5252// With options and typing
53- const flow = new AxFlow <InputType , OutputType >({
53+ const wf = flow <InputType , OutputType >({
5454 autoParallel: true ,
5555 batchSize: 5 ,
5656});
@@ -403,7 +403,7 @@ flow.derive("upperText", "inputText", (text) => text.toUpperCase());
403403### 1. Sequential Processing
404404
405405``` typescript
406- const sequentialFlow = new AxFlow <{ input: string }, { finalResult: string }>()
406+ const sequentialFlow = flow <{ input: string }, { finalResult: string }>()
407407 .node (" step1" , " input:string -> intermediate:string" )
408408 .node (" step2" , " intermediate:string -> output:string" )
409409 .execute (" step1" , (state ) => ({ input: state .input }))
@@ -417,7 +417,7 @@ const sequentialFlow = new AxFlow<{ input: string }, { finalResult: string }>()
417417### 2. Conditional Processing
418418
419419``` typescript
420- const conditionalFlow = new AxFlow <
420+ const conditionalFlow = flow <
421421 { query : string ; isComplex : boolean },
422422 { response : string }
423423> ()
@@ -438,7 +438,7 @@ const conditionalFlow = new AxFlow<
438438### 3. Iterative Processing
439439
440440``` typescript
441- const iterativeFlow = new AxFlow <
441+ const iterativeFlow = flow <
442442 { content : string },
443443 { finalContent : string }
444444> ()
@@ -466,7 +466,7 @@ const iterativeFlow = new AxFlow<
466466AxFlow automatically detects independent operations and runs them in parallel:
467467
468468``` typescript
469- const autoParallelFlow = new AxFlow <
469+ const autoParallelFlow = flow <
470470 { text : string },
471471 { combinedAnalysis : string }
472472> ()
@@ -498,7 +498,7 @@ AxFlow supports asynchronous transformations in map operations, enabling API
498498calls, database queries, and other async operations within your flow:
499499
500500``` typescript
501- const asyncFlow = new AxFlow <
501+ const asyncFlow = flow <
502502 { userQuery : string },
503503 { enrichedData : string ; apiCallTime : number }
504504> ()
@@ -543,7 +543,7 @@ const asyncFlow = new AxFlow<
543543You can mix synchronous and asynchronous operations seamlessly:
544544
545545``` typescript
546- const mixedFlow = new AxFlow <{ rawData: string }, { result: string }>()
546+ const mixedFlow = flow <{ rawData: string }, { result: string }>()
547547 // Sync preprocessing
548548 .map ((state ) => ({
549549 ... state ,
589589### 6. Self-Healing with Feedback Loops
590590
591591``` typescript
592- const selfHealingFlow = new AxFlow <{ input: string }, { output: string }>()
592+ const selfHealingFlow = flow <{ input: string }, { output: string }>()
593593 .node (" processor" , " input:string -> output:string, confidence:number" )
594594 .node (" validator" , " output:string -> isValid:boolean, issues:string[]" )
595595 .node (" fixer" , " output:string, issues:string[] -> fixedOutput:string" )
@@ -669,7 +669,7 @@ parallel:
669669
670670``` typescript
671671// Disable auto-parallelization globally
672- const sequentialFlow = new AxFlow ({ autoParallel: false });
672+ const sequentialFlow = flow ({ autoParallel: false });
673673
674674// Disable for specific execution
675675const result = await flow .forward (llm , input , { autoParallel: false });
695695### 3. Batch Processing with Derive
696696
697697``` typescript
698- const batchFlow = new AxFlow <{ items: string [] }, { processedItems: string [] }>(
698+ const batchFlow = flow <{ items: string [] }, { processedItems: string [] }>(
699699 {
700700 autoParallel: true ,
701701 batchSize: 3 , // Process 3 items at a time
@@ -828,15 +828,175 @@ flow
828828 }));
829829```
830830
831+ ## When to Use Each Feature
832+
833+ ### Data shaping vs. AI calls: map() vs execute()
834+
835+ Use ` map() ` for synchronous/async data transformations without calling AI; use
836+ ` execute() ` to invoke a previously defined AI node.
837+
838+ ``` typescript
839+ const wf = flow <{ raw: string }, { answer: string }>()
840+ // Preprocess user input (no AI call)
841+ .map ((s ) => ({ cleaned: s .raw .trim ().toLowerCase () }))
842+ // Call an AI node you defined earlier (requires execute)
843+ .node (" qa" , " question:string -> answer:string" )
844+ .execute (" qa" , (s ) => ({ question: s .cleaned }))
845+ .map ((s ) => ({ answer: s .qaResult .answer }));
846+ ```
847+
848+ ### Conditional routing: branch()/when()/merge()
849+
850+ Use when you need different paths for different conditions, then converge.
851+
852+ ``` typescript
853+ const wf = flow <{ query: string ; expertMode: boolean }, { response: string }>()
854+ .node (" simple" , " query:string -> response:string" )
855+ .node (" expert" , " query:string -> response:string" )
856+ .branch ((s ) => s .expertMode )
857+ .when (true )
858+ .execute (" expert" , (s ) => ({ query: s .query }))
859+ .when (false )
860+ .execute (" simple" , (s ) => ({ query: s .query }))
861+ .merge ()
862+ .map ((s ) => ({
863+ response: s .expertResult ?.response ?? s .simpleResult ?.response ,
864+ }));
865+ ```
866+
867+ ### Iteration/retries: while()/endWhile()
868+
869+ Use to repeat work until a goal is met or a cap is hit.
870+
871+ ``` typescript
872+ const wf = flow <{ draft: string }, { final: string }>()
873+ .node (" grader" , " text:string -> score:number" )
874+ .node (" improver" , " text:string -> improved:string" )
875+ .map ((s ) => ({ current: s .draft , attempts: 0 }))
876+ .while ((s ) => s .attempts < 3 )
877+ .execute (" grader" , (s ) => ({ text: s .current }))
878+ .branch ((s ) => s .graderResult .score >= 0.8 )
879+ .when (true )
880+ .map ((s ) => ({ attempts: 3 }))
881+ .when (false )
882+ .execute (" improver" , (s ) => ({ text: s .current }))
883+ .map ((s ) => ({
884+ current: s .improverResult .improved ,
885+ attempts: s .attempts + 1 ,
886+ }))
887+ .merge ()
888+ .endWhile ()
889+ .map ((s ) => ({ final: s .current }));
890+ ```
891+
892+ ### Extend node contracts: nx()
893+
894+ Use ` nx() ` to augment inputs/outputs (e.g., add internal reasoning or
895+ confidence) without changing original signature text.
896+
897+ ``` typescript
898+ const wf = flow <{ question: string }, { answer: string ; confidence: number }>()
899+ .nx (" answerer" , " question:string -> answer:string" , {
900+ appendOutputs: [{ name: " confidence" , type: f .number (" 0-1" ) }],
901+ })
902+ .execute (" answerer" , (s ) => ({ question: s .question }))
903+ .map ((s ) => ({
904+ answer: s .answererResult .answer ,
905+ confidence: s .answererResult .confidence ,
906+ }));
907+ ```
908+
909+ ### Batch/array processing: derive()
910+
911+ Use to fan out work over arrays with built-in batching and merging.
912+
913+ ``` typescript
914+ const wf = flow <{ items: string [] }, { processed: string [] }>({ batchSize: 3 })
915+ .derive (" processed" , " items" , (item ) => item .toUpperCase (), { batchSize: 2 });
916+ ```
917+
918+ ### Free speedups: auto-parallelization
919+
920+ Independent executes run in parallel automatically. Avoid creating unnecessary
921+ dependencies.
922+
923+ ``` typescript
924+ const wf = flow <{ text: string }, { combined: string }>()
925+ .node (" a" , " text:string -> x:string" )
926+ .node (" b" , " text:string -> y:string" )
927+ .execute (" a" , (s ) => ({ text: s .text }))
928+ .execute (" b" , (s ) => ({ text: s .text }))
929+ .map ((s ) => ({ combined: ` ${s .aResult .x }|${s .bResult .y } ` }));
930+ ```
931+
932+ ### Multi-model strategy: dynamic AI context
933+
934+ Override AI per execute to route tasks to the best model.
935+
936+ ``` typescript
937+ const wf = flow <{ text: string }, { out: string }>()
938+ .node (" fast" , " text:string -> out:string" )
939+ .node (" smart" , " text:string -> out:string" )
940+ .execute (" fast" , (s ) => ({ text: s .text }), { ai: ai ({ name: " groq" }) })
941+ .execute (" smart" , (s ) => ({ text: s .text }), {
942+ ai: ai ({ name: " anthropic" }),
943+ })
944+ .map ((s ) => ({ out: s .smartResult ?.out ?? s .fastResult .out }));
945+ ```
946+
947+ ### Final typing and shape: returns()/r()
948+
949+ Use to lock in the exact output type and get full TypeScript inference.
950+
951+ ``` typescript
952+ const wf = flow <{ input: string }>()
953+ .map ((s ) => ({ upper: s .input .toUpperCase (), length: s .input .length }))
954+ .returns ((s ) => ({ upper: s .upper , isLong: s .length > 20 }));
955+ ```
956+
957+ ### Quality loops: label()/feedback()
958+
959+ Use to jump back to a label when a condition holds, with optional caps.
960+
961+ ``` typescript
962+ const wf = flow <{ prompt: string }, { result: string }>()
963+ .node (" gen" , " prompt:string -> result:string, quality:number" )
964+ .map ((s ) => ({ tries: 0 }))
965+ .label (" retry" )
966+ .execute (" gen" , (s ) => ({ prompt: s .prompt }))
967+ .feedback ((s ) => s .genResult .quality < 0.9 && s .tries < 2 , " retry" )
968+ .map ((s ) => ({ result: s .genResult .result }));
969+ ```
970+
971+ ### Parallel async transforms: map([ ...] , { parallel: true })
972+
973+ Use to run multiple independent async transforms concurrently.
974+
975+ ``` typescript
976+ const wf = flow <{ url: string }, { title: string ; sentiment: string }>()
977+ .map ([
978+ async (s ) => ({ html: await fetchHTML (s .url ) }),
979+ async (s ) => ({ meta: await fetchMetadata (s .url ) }),
980+ ], { parallel: true })
981+ .node (" title" , " html:string -> title:string" )
982+ .node (" sent" , " html:string -> sentiment:string" )
983+ .execute (" title" , (s ) => ({ html: s .html }))
984+ .execute (" sent" , (s ) => ({ html: s .html }))
985+ .returns ((s ) => ({
986+ title: s .titleResult .title ,
987+ sentiment: s .sentResult .sentiment ,
988+ }));
989+ ```
990+
831991## Examples
832992
833993### Extended Node Patterns with ` nx `
834994
835995``` typescript
836- import { AxFlow , f } from " @ax-llm/ax" ;
996+ import { f , flow } from " @ax-llm/ax" ;
837997
838998// Chain-of-thought reasoning pattern
839- const reasoningFlow = new AxFlow <{ question: string }, { answer: string }>()
999+ const reasoningFlow = flow <{ question: string }, { answer: string }>()
8401000 .nx (" reasoner" , " question:string -> answer:string" , {
8411001 prependOutputs: [
8421002 {
@@ -849,7 +1009,7 @@ const reasoningFlow = new AxFlow<{ question: string }, { answer: string }>()
8491009 .map ((state ) => ({ answer: state .reasonerResult .answer }));
8501010
8511011// Confidence scoring pattern
852- const confidenceFlow = new AxFlow <
1012+ const confidenceFlow = flow <
8531013 { input : string },
8541014 { result : string ; confidence : number }
8551015> ()
@@ -865,7 +1025,7 @@ const confidenceFlow = new AxFlow<
8651025 }));
8661026
8671027// Contextual processing pattern
868- const contextualFlow = new AxFlow <
1028+ const contextualFlow = flow <
8691029 { query : string ; context ?: string },
8701030 { response : string }
8711031> ()
@@ -910,7 +1070,7 @@ console.log(result.keywords); // Fully typed
9101070### Quality-Driven Content Creation
9111071
9121072``` typescript
913- const contentCreator = new AxFlow <
1073+ const contentCreator = flow <
9141074 { topic : string ; targetQuality : number },
9151075 { finalContent : string ; iterations : number }
9161076> ()
@@ -955,7 +1115,7 @@ const contentCreator = new AxFlow<
9551115### Async Data Enrichment Pipeline
9561116
9571117``` typescript
958- const enrichmentPipeline = new AxFlow <
1118+ const enrichmentPipeline = flow <
9591119 { userQuery : string },
9601120 { finalResult : string ; metadata : object }
9611121> ()
@@ -1007,7 +1167,7 @@ const enrichmentPipeline = new AxFlow<
10071167### Real-time Data Processing with Async Maps
10081168
10091169``` typescript
1010- const realTimeProcessor = new AxFlow <
1170+ const realTimeProcessor = flow <
10111171 { streamData : string [] },
10121172 { processedResults : string []; stats : object }
10131173> ()
@@ -1066,7 +1226,7 @@ const realTimeProcessor = new AxFlow<
10661226### Multi-Model Research System
10671227
10681228``` typescript
1069- const researchSystem = new AxFlow <
1229+ const researchSystem = flow <
10701230 { query : string },
10711231 { answer : string ; sources : string []; confidence : number }
10721232> ()
0 commit comments