flowcraft 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +9 -0
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/config/tsconfig.json +21 -0
- package/config/tsup.config.ts +11 -0
- package/config/vitest.config.ts +11 -0
- package/docs/.vitepress/config.ts +105 -0
- package/docs/api-reference/builder.md +158 -0
- package/docs/api-reference/fn.md +142 -0
- package/docs/api-reference/index.md +38 -0
- package/docs/api-reference/workflow.md +126 -0
- package/docs/guide/advanced-guides/cancellation.md +117 -0
- package/docs/guide/advanced-guides/composition.md +68 -0
- package/docs/guide/advanced-guides/custom-executor.md +180 -0
- package/docs/guide/advanced-guides/error-handling.md +135 -0
- package/docs/guide/advanced-guides/logging.md +106 -0
- package/docs/guide/advanced-guides/middleware.md +106 -0
- package/docs/guide/advanced-guides/observability.md +175 -0
- package/docs/guide/best-practices/debugging.md +182 -0
- package/docs/guide/best-practices/state-management.md +120 -0
- package/docs/guide/best-practices/sub-workflow-data.md +95 -0
- package/docs/guide/best-practices/testing.md +187 -0
- package/docs/guide/builders.md +157 -0
- package/docs/guide/functional-api.md +133 -0
- package/docs/guide/index.md +178 -0
- package/docs/guide/recipes/creating-a-loop.md +113 -0
- package/docs/guide/recipes/data-processing-pipeline.md +123 -0
- package/docs/guide/recipes/fan-out-fan-in.md +112 -0
- package/docs/guide/recipes/index.md +15 -0
- package/docs/guide/recipes/resilient-api-call.md +110 -0
- package/docs/guide/tooling/graph-validation.md +160 -0
- package/docs/guide/tooling/mermaid.md +156 -0
- package/docs/index.md +56 -0
- package/eslint.config.js +16 -0
- package/package.json +40 -0
- package/pnpm-workspace.yaml +2 -0
- package/sandbox/1.basic/README.md +45 -0
- package/sandbox/1.basic/package.json +16 -0
- package/sandbox/1.basic/src/flow.ts +17 -0
- package/sandbox/1.basic/src/main.ts +22 -0
- package/sandbox/1.basic/src/nodes.ts +112 -0
- package/sandbox/1.basic/src/utils.ts +35 -0
- package/sandbox/1.basic/tsconfig.json +3 -0
- package/sandbox/2.research/README.md +46 -0
- package/sandbox/2.research/package.json +16 -0
- package/sandbox/2.research/src/flow.ts +14 -0
- package/sandbox/2.research/src/main.ts +31 -0
- package/sandbox/2.research/src/nodes.ts +108 -0
- package/sandbox/2.research/src/utils.ts +45 -0
- package/sandbox/2.research/src/visualize.ts +29 -0
- package/sandbox/2.research/tsconfig.json +3 -0
- package/sandbox/3.parallel/README.md +65 -0
- package/sandbox/3.parallel/package.json +16 -0
- package/sandbox/3.parallel/src/main.ts +45 -0
- package/sandbox/3.parallel/src/nodes.ts +43 -0
- package/sandbox/3.parallel/src/utils.ts +25 -0
- package/sandbox/3.parallel/tsconfig.json +3 -0
- package/sandbox/4.dag/README.md +179 -0
- package/sandbox/4.dag/data/1.blog-post/100.json +60 -0
- package/sandbox/4.dag/data/1.blog-post/README.md +25 -0
- package/sandbox/4.dag/data/2.job-application/200.json +103 -0
- package/sandbox/4.dag/data/2.job-application/201.json +31 -0
- package/sandbox/4.dag/data/2.job-application/202.json +31 -0
- package/sandbox/4.dag/data/2.job-application/README.md +58 -0
- package/sandbox/4.dag/data/3.customer-review/300.json +141 -0
- package/sandbox/4.dag/data/3.customer-review/301.json +31 -0
- package/sandbox/4.dag/data/3.customer-review/302.json +28 -0
- package/sandbox/4.dag/data/3.customer-review/README.md +71 -0
- package/sandbox/4.dag/data/4.content-moderation/400.json +161 -0
- package/sandbox/4.dag/data/4.content-moderation/401.json +47 -0
- package/sandbox/4.dag/data/4.content-moderation/402.json +46 -0
- package/sandbox/4.dag/data/4.content-moderation/403.json +31 -0
- package/sandbox/4.dag/data/4.content-moderation/README.md +83 -0
- package/sandbox/4.dag/package.json +19 -0
- package/sandbox/4.dag/src/main.ts +73 -0
- package/sandbox/4.dag/src/nodes.ts +134 -0
- package/sandbox/4.dag/src/registry.ts +87 -0
- package/sandbox/4.dag/src/types.ts +25 -0
- package/sandbox/4.dag/src/utils.ts +42 -0
- package/sandbox/4.dag/tsconfig.json +3 -0
- package/sandbox/5.distributed/.env.example +1 -0
- package/sandbox/5.distributed/README.md +88 -0
- package/sandbox/5.distributed/data/1.blog-post/100.json +59 -0
- package/sandbox/5.distributed/data/1.blog-post/README.md +25 -0
- package/sandbox/5.distributed/data/2.job-application/200.json +103 -0
- package/sandbox/5.distributed/data/2.job-application/201.json +30 -0
- package/sandbox/5.distributed/data/2.job-application/202.json +30 -0
- package/sandbox/5.distributed/data/2.job-application/README.md +58 -0
- package/sandbox/5.distributed/data/3.customer-review/300.json +141 -0
- package/sandbox/5.distributed/data/3.customer-review/301.json +31 -0
- package/sandbox/5.distributed/data/3.customer-review/302.json +57 -0
- package/sandbox/5.distributed/data/3.customer-review/README.md +71 -0
- package/sandbox/5.distributed/data/4.content-moderation/400.json +173 -0
- package/sandbox/5.distributed/data/4.content-moderation/401.json +47 -0
- package/sandbox/5.distributed/data/4.content-moderation/402.json +46 -0
- package/sandbox/5.distributed/data/4.content-moderation/403.json +31 -0
- package/sandbox/5.distributed/data/4.content-moderation/README.md +83 -0
- package/sandbox/5.distributed/package.json +20 -0
- package/sandbox/5.distributed/src/client.ts +124 -0
- package/sandbox/5.distributed/src/executor.ts +69 -0
- package/sandbox/5.distributed/src/nodes.ts +136 -0
- package/sandbox/5.distributed/src/registry.ts +101 -0
- package/sandbox/5.distributed/src/types.ts +45 -0
- package/sandbox/5.distributed/src/utils.ts +69 -0
- package/sandbox/5.distributed/src/worker.ts +217 -0
- package/sandbox/5.distributed/tsconfig.json +3 -0
- package/sandbox/6.rag/.env.example +1 -0
- package/sandbox/6.rag/README.md +60 -0
- package/sandbox/6.rag/data/README.md +31 -0
- package/sandbox/6.rag/data/rag.json +58 -0
- package/sandbox/6.rag/documents/sample-cascade.txt +11 -0
- package/sandbox/6.rag/package.json +18 -0
- package/sandbox/6.rag/src/main.ts +52 -0
- package/sandbox/6.rag/src/nodes/GenerateEmbeddingsNode.ts +54 -0
- package/sandbox/6.rag/src/nodes/LLMProcessNode.ts +48 -0
- package/sandbox/6.rag/src/nodes/LoadAndChunkNode.ts +40 -0
- package/sandbox/6.rag/src/nodes/StoreInVectorDBNode.ts +36 -0
- package/sandbox/6.rag/src/nodes/VectorSearchNode.ts +53 -0
- package/sandbox/6.rag/src/nodes/index.ts +28 -0
- package/sandbox/6.rag/src/registry.ts +23 -0
- package/sandbox/6.rag/src/types.ts +44 -0
- package/sandbox/6.rag/src/utils.ts +77 -0
- package/sandbox/6.rag/tsconfig.json +3 -0
- package/sandbox/tsconfig.json +13 -0
- package/src/builder/collection.test.ts +287 -0
- package/src/builder/collection.ts +269 -0
- package/src/builder/graph.test.ts +406 -0
- package/src/builder/graph.ts +336 -0
- package/src/builder/graph.types.ts +104 -0
- package/src/builder/index.ts +3 -0
- package/src/context.ts +111 -0
- package/src/errors.ts +34 -0
- package/src/executor.ts +29 -0
- package/src/executors/in-memory.test.ts +93 -0
- package/src/executors/in-memory.ts +140 -0
- package/src/functions.test.ts +191 -0
- package/src/functions.ts +117 -0
- package/src/index.ts +5 -0
- package/src/logger.ts +41 -0
- package/src/types.ts +75 -0
- package/src/utils/graph.test.ts +144 -0
- package/src/utils/graph.ts +182 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/mermaid.test.ts +239 -0
- package/src/utils/mermaid.ts +133 -0
- package/src/utils/sleep.ts +20 -0
- package/src/workflow.test.ts +622 -0
- package/src/workflow.ts +561 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { AbstractNode, Flow } from '../workflow'
|
|
2
|
+
import { DEFAULT_ACTION, FILTER_FAILED } from '../types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Converts a special action symbol to a user-friendly string for the graph label.
|
|
6
|
+
* @param action The action symbol or string.
|
|
7
|
+
* @returns A string label for the Mermaid edge.
|
|
8
|
+
*/
|
|
9
|
+
function getActionLabel(action: string | symbol): string {
|
|
10
|
+
if (action === DEFAULT_ACTION)
|
|
11
|
+
return ''
|
|
12
|
+
|
|
13
|
+
if (action === FILTER_FAILED)
|
|
14
|
+
return '"filter failed"'
|
|
15
|
+
|
|
16
|
+
// Sanitize labels to prevent breaking Mermaid syntax
|
|
17
|
+
const sanitizedAction = action.toString().replace(/"/g, '')
|
|
18
|
+
return `"${sanitizedAction}"`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generates a descriptive label for a node to be used in the Mermaid graph.
|
|
23
|
+
* @param node The node to generate a label for.
|
|
24
|
+
* @param uniqueId The unique ID assigned to this node instance in the graph.
|
|
25
|
+
* @returns A formatted string for the Mermaid node definition.
|
|
26
|
+
*/
|
|
27
|
+
function getNodeLabel(node: AbstractNode, uniqueId: string): string {
|
|
28
|
+
if ((node as any).isParallelContainer)
|
|
29
|
+
return ` ${uniqueId}{Parallel Block}`
|
|
30
|
+
|
|
31
|
+
if (node.constructor.name === 'InputMappingNode')
|
|
32
|
+
return ` ${uniqueId}(("Inputs"))`
|
|
33
|
+
|
|
34
|
+
if (node.constructor.name === 'OutputMappingNode')
|
|
35
|
+
return ` ${uniqueId}(("Outputs"))`
|
|
36
|
+
|
|
37
|
+
if (node.graphData) {
|
|
38
|
+
const type = node.graphData.type
|
|
39
|
+
const id = node.graphData.id.split(':').pop()
|
|
40
|
+
return ` ${uniqueId}["${id} (${type})"]`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return ` ${uniqueId}[${node.constructor.name}]`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generates a unique, readable ID for a node instance.
|
|
48
|
+
* @param node The node instance.
|
|
49
|
+
* @returns A unique string ID.
|
|
50
|
+
*/
|
|
51
|
+
function getUniqueNodeId(node: AbstractNode, nameCounts: Map<string, number>, idMap: Map<AbstractNode, string>): string {
|
|
52
|
+
if (idMap.has(node))
|
|
53
|
+
return idMap.get(node)!
|
|
54
|
+
|
|
55
|
+
let baseName: string
|
|
56
|
+
if ((node as any).isParallelContainer) {
|
|
57
|
+
baseName = 'ParallelBlock'
|
|
58
|
+
}
|
|
59
|
+
else if (node.graphData) {
|
|
60
|
+
baseName = node.graphData.id
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
baseName = node.constructor.name
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Sanitize the name for Mermaid ID
|
|
67
|
+
const sanitizedBaseName = baseName.replace(/:/g, '_').replace(/\W/g, '')
|
|
68
|
+
const count = nameCounts.get(sanitizedBaseName) || 0
|
|
69
|
+
const uniqueId = `${sanitizedBaseName}_${count}`
|
|
70
|
+
nameCounts.set(sanitizedBaseName, count + 1)
|
|
71
|
+
idMap.set(node, uniqueId)
|
|
72
|
+
return uniqueId
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generates a Mermaid graph definition from a `Flow` instance.
|
|
77
|
+
* ...
|
|
78
|
+
*/
|
|
79
|
+
export function generateMermaidGraph(flow: Flow): string {
|
|
80
|
+
if (!flow.startNode)
|
|
81
|
+
return 'graph TD\n %% Empty Flow'
|
|
82
|
+
|
|
83
|
+
const nodes = new Set<string>()
|
|
84
|
+
const edges = new Set<string>()
|
|
85
|
+
const visited = new Set<AbstractNode>()
|
|
86
|
+
const queue: AbstractNode[] = [flow.startNode]
|
|
87
|
+
const idMap = new Map<AbstractNode, string>()
|
|
88
|
+
const nameCounts = new Map<string, number>()
|
|
89
|
+
|
|
90
|
+
visited.add(flow.startNode)
|
|
91
|
+
getUniqueNodeId(flow.startNode, nameCounts, idMap)
|
|
92
|
+
|
|
93
|
+
while (queue.length > 0) {
|
|
94
|
+
const currentNode = queue.shift()!
|
|
95
|
+
const sourceId = getUniqueNodeId(currentNode, nameCounts, idMap)
|
|
96
|
+
|
|
97
|
+
nodes.add(getNodeLabel(currentNode, sourceId))
|
|
98
|
+
|
|
99
|
+
if ((currentNode as any).isParallelContainer) {
|
|
100
|
+
const container = currentNode as any as { nodesToRun: AbstractNode[] }
|
|
101
|
+
for (const internalNode of container.nodesToRun) {
|
|
102
|
+
const targetId = getUniqueNodeId(internalNode, nameCounts, idMap)
|
|
103
|
+
edges.add(` ${sourceId} --> ${targetId}`)
|
|
104
|
+
if (!visited.has(internalNode)) {
|
|
105
|
+
visited.add(internalNode)
|
|
106
|
+
queue.push(internalNode)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const [action, successorNode] of currentNode.successors.entries()) {
|
|
112
|
+
const targetId = getUniqueNodeId(successorNode, nameCounts, idMap)
|
|
113
|
+
const label = getActionLabel(action)
|
|
114
|
+
const edge = label
|
|
115
|
+
? ` ${sourceId} -- ${label} --> ${targetId}`
|
|
116
|
+
: ` ${sourceId} --> ${targetId}`
|
|
117
|
+
edges.add(edge)
|
|
118
|
+
|
|
119
|
+
if (!visited.has(successorNode)) {
|
|
120
|
+
visited.add(successorNode)
|
|
121
|
+
queue.push(successorNode)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const mermaidLines = [
|
|
127
|
+
'graph TD',
|
|
128
|
+
...Array.from(nodes),
|
|
129
|
+
...Array.from(edges),
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
return mermaidLines.join('\n')
|
|
133
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AbortError } from '../errors'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An abortable `sleep` utility that pauses execution for a specified duration.
|
|
5
|
+
* It will reject with an `AbortError` if the provided `AbortSignal` is triggered.
|
|
6
|
+
* @param ms The number of milliseconds to sleep.
|
|
7
|
+
* @param signal An optional `AbortSignal` to listen for cancellation.
|
|
8
|
+
*/
|
|
9
|
+
export async function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
if (signal?.aborted)
|
|
12
|
+
return reject(new AbortError())
|
|
13
|
+
|
|
14
|
+
const timeoutId = setTimeout(resolve, ms)
|
|
15
|
+
signal?.addEventListener('abort', () => {
|
|
16
|
+
clearTimeout(timeoutId)
|
|
17
|
+
reject(new AbortError())
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
}
|