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,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "extract_skills",
|
|
5
|
+
"type": "llm-process",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Extract a list of all technical skills from this resume. Format as a simple comma-separated string:\n\n{{resume}}",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"resume": "resume"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "analyze_tone",
|
|
15
|
+
"type": "llm-process",
|
|
16
|
+
"data": {
|
|
17
|
+
"promptTemplate": "Analyze the tone of this cover letter. Respond with a single word: \"professional\", \"casual\", or \"unprofessional\".\n\n{{coverLetter}}",
|
|
18
|
+
"inputs": {
|
|
19
|
+
"coverLetter": "coverLetter"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "check_qualifications",
|
|
25
|
+
"type": "llm-condition",
|
|
26
|
+
"data": {
|
|
27
|
+
"promptTemplate": "Does this list of skills `{{skills}}` contain all of the following required skills: `TypeScript, Node.js, SQL`? Also, is the cover letter tone `{{tone}}` professional? Respond only 'true' if both conditions are met, otherwise 'false'.",
|
|
28
|
+
"inputs": {
|
|
29
|
+
"skills": "extract_skills",
|
|
30
|
+
"tone": "analyze_tone"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "send_interview_email",
|
|
36
|
+
"type": "sub-workflow",
|
|
37
|
+
"data": {
|
|
38
|
+
"workflowId": 201,
|
|
39
|
+
"outputs": {
|
|
40
|
+
"send_interview_email": "final_output"
|
|
41
|
+
},
|
|
42
|
+
"inputs": {
|
|
43
|
+
"applicantName": "applicantName"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "send_rejection_email",
|
|
49
|
+
"type": "sub-workflow",
|
|
50
|
+
"data": {
|
|
51
|
+
"workflowId": 202,
|
|
52
|
+
"outputs": {
|
|
53
|
+
"send_rejection_email": "final_output"
|
|
54
|
+
},
|
|
55
|
+
"inputs": {
|
|
56
|
+
"applicantName": "applicantName"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "final_output",
|
|
62
|
+
"type": "output",
|
|
63
|
+
"data": {
|
|
64
|
+
"promptTemplate": "Final email sent to {{applicantName}}:\n\n{{email_body}}",
|
|
65
|
+
"inputs": {
|
|
66
|
+
"applicantName": "applicantName",
|
|
67
|
+
"email_body": [
|
|
68
|
+
"send_interview_email",
|
|
69
|
+
"send_rejection_email"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"edges": [
|
|
76
|
+
{
|
|
77
|
+
"source": "extract_skills",
|
|
78
|
+
"target": "check_qualifications"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"source": "analyze_tone",
|
|
82
|
+
"target": "check_qualifications"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"source": "check_qualifications",
|
|
86
|
+
"target": "send_interview_email",
|
|
87
|
+
"action": "true"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"source": "check_qualifications",
|
|
91
|
+
"target": "send_rejection_email",
|
|
92
|
+
"action": "false"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"source": "send_interview_email",
|
|
96
|
+
"target": "final_output"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"source": "send_rejection_email",
|
|
100
|
+
"target": "final_output"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "gen_interview_email",
|
|
5
|
+
"type": "llm-process",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Write a polite and professional email inviting {{applicantName}} to an interview. Mention you were impressed with their resume.",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"applicantName": "applicantName"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "output_email",
|
|
15
|
+
"type": "output",
|
|
16
|
+
"data": {
|
|
17
|
+
"promptTemplate": "{{result}}",
|
|
18
|
+
"inputs": {
|
|
19
|
+
"result": "gen_interview_email"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"edges": [
|
|
25
|
+
{
|
|
26
|
+
"source": "gen_interview_email",
|
|
27
|
+
"target": "output_email"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "gen_rejection_email",
|
|
5
|
+
"type": "llm-process",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Write a polite and professional email to {{applicantName}} informing them that you will not be moving forward with their application at this time. Thank them for their interest.",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"applicantName": "applicantName"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "output_email",
|
|
15
|
+
"type": "output",
|
|
16
|
+
"data": {
|
|
17
|
+
"promptTemplate": "{{result}}",
|
|
18
|
+
"inputs": {
|
|
19
|
+
"result": "gen_rejection_email"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"edges": [
|
|
25
|
+
{
|
|
26
|
+
"source": "gen_rejection_email",
|
|
27
|
+
"target": "output_email"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Use Case 2: Job Application Screener
|
|
2
|
+
|
|
3
|
+
This workflow demonstrates a more complex Directed Acyclic Graph (DAG) for screening a job applicant. It showcases parallel processing, data convergence, and conditional branching based on an LLM's evaluation.
|
|
4
|
+
|
|
5
|
+
## Main Workflow ID: 200
|
|
6
|
+
|
|
7
|
+
### Description
|
|
8
|
+
|
|
9
|
+
1. **Parallel Analysis**: The flow begins by analyzing the applicant's `resume` and `coverLetter` simultaneously.
|
|
10
|
+
- `extract_skills`: Pulls a list of technical skills from the resume.
|
|
11
|
+
- `analyze_tone`: Determines the professionalism of the cover letter.
|
|
12
|
+
2. **Conditional Check**: The results from the parallel steps are fed into the `check_qualifications` node. This LLM-powered node evaluates if the applicant meets the required criteria (e.g., has specific skills and a professional tone). It returns `true` or `false`.
|
|
13
|
+
3. **Branching**: Based on the result of the check, the flow executes one of two sub-workflows:
|
|
14
|
+
- **On `true`**: It calls the `Send Interview Email` sub-workflow (ID: 201).
|
|
15
|
+
- **On `false`**: It calls the `Send Rejection Email` sub-workflow (ID: 202).
|
|
16
|
+
4. **Convergence**: The output from whichever branch was executed is passed to the `final_output` node, which formats the concluding message.
|
|
17
|
+
|
|
18
|
+
### Visual Graph
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
graph TD
|
|
22
|
+
subgraph "Job Application Screener (ID: 200)"
|
|
23
|
+
A(Resume) --> B[extract_skills]
|
|
24
|
+
C(Cover Letter) --> D[analyze_tone]
|
|
25
|
+
B --> E{check_qualifications}
|
|
26
|
+
D --> E
|
|
27
|
+
E -- true --> F["Sub-Workflow: Send Interview (201)"]
|
|
28
|
+
E -- false --> G["Sub-Workflow: Send Rejection (202)"]
|
|
29
|
+
F --> H[final_output]
|
|
30
|
+
G --> H
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Sub-Workflows
|
|
37
|
+
|
|
38
|
+
### Sub-Workflow ID: 201 (Send Interview Email)
|
|
39
|
+
|
|
40
|
+
A simple, linear workflow that generates a polite email inviting the candidate to an interview.
|
|
41
|
+
|
|
42
|
+
```mermaid
|
|
43
|
+
graph TD
|
|
44
|
+
subgraph "Generate Interview Email (ID: 201)"
|
|
45
|
+
A[gen_interview_email] --> B[output_email]
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Sub-Workflow ID: 202 (Send Rejection Email)
|
|
50
|
+
|
|
51
|
+
A simple, linear workflow that generates a polite rejection email.
|
|
52
|
+
|
|
53
|
+
```mermaid
|
|
54
|
+
graph TD
|
|
55
|
+
subgraph "Generate Rejection Email (ID: 202)"
|
|
56
|
+
A[gen_rejection_email] --> B[output_email]
|
|
57
|
+
end
|
|
58
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "summarize",
|
|
5
|
+
"type": "llm-process",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Summarize this review in one sentence: {{review}}",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"review": "initial_review"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "categorize",
|
|
15
|
+
"type": "llm-process",
|
|
16
|
+
"data": {
|
|
17
|
+
"promptTemplate": "Categorize this review into ONLY ONE of: 'Bug Report', 'Feature Request', 'General Feedback'. Review: {{review}}",
|
|
18
|
+
"inputs": {
|
|
19
|
+
"review": "initial_review"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "check_sentiment",
|
|
25
|
+
"type": "llm-condition",
|
|
26
|
+
"data": {
|
|
27
|
+
"promptTemplate": "Is the sentiment of this summary positive? '{{summary}}'. Respond only 'true' or 'false'.",
|
|
28
|
+
"inputs": {
|
|
29
|
+
"summary": "summarize"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "positive_branch",
|
|
35
|
+
"type": "sub-workflow",
|
|
36
|
+
"data": {
|
|
37
|
+
"workflowId": 301,
|
|
38
|
+
"inputs": {
|
|
39
|
+
"summary": "summarize"
|
|
40
|
+
},
|
|
41
|
+
"outputs": {
|
|
42
|
+
"final_positive_reply": "final_output"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "negative_branch",
|
|
48
|
+
"type": "sub-workflow",
|
|
49
|
+
"data": {
|
|
50
|
+
"workflowId": 302,
|
|
51
|
+
"inputs": {
|
|
52
|
+
"summary": "summarize",
|
|
53
|
+
"category": "categorize"
|
|
54
|
+
},
|
|
55
|
+
"outputs": {
|
|
56
|
+
"generated_ticket": "ticket_payload",
|
|
57
|
+
"generated_reply": "customer_message"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "send_to_ticketing_system",
|
|
63
|
+
"type": "llm-process",
|
|
64
|
+
"data": {
|
|
65
|
+
"promptTemplate": "--- SIMULATING API CALL ---\nPOST /api/tickets\nPayload: {{ticket_data}}",
|
|
66
|
+
"inputs": {
|
|
67
|
+
"ticket_data": "generated_ticket"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "send_email_to_customer",
|
|
73
|
+
"type": "llm-process",
|
|
74
|
+
"data": {
|
|
75
|
+
"promptTemplate": "--- SIMULATING EMAIL SENT ---\nTo: customer@example.com\nBody: {{email_body}}",
|
|
76
|
+
"inputs": {
|
|
77
|
+
"email_body": "generated_reply"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "final_step",
|
|
83
|
+
"type": "output",
|
|
84
|
+
"data": {
|
|
85
|
+
"outputKey": "final_output",
|
|
86
|
+
"promptTemplate": "--- Final Actions Taken ---\nTicket System: {{ticket_action}}\n\nCustomer Email: {{email_action}}\n\nPositive Flow Result: {{positive_result}}",
|
|
87
|
+
"inputs": {
|
|
88
|
+
"ticket_action": [
|
|
89
|
+
"send_to_ticketing_system"
|
|
90
|
+
],
|
|
91
|
+
"email_action": [
|
|
92
|
+
"send_email_to_customer"
|
|
93
|
+
],
|
|
94
|
+
"positive_result": [
|
|
95
|
+
"final_positive_reply"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"edges": [
|
|
102
|
+
{
|
|
103
|
+
"source": "summarize",
|
|
104
|
+
"target": "check_sentiment"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"source": "categorize",
|
|
108
|
+
"target": "check_sentiment"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"source": "check_sentiment",
|
|
112
|
+
"target": "positive_branch",
|
|
113
|
+
"action": "true"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"source": "check_sentiment",
|
|
117
|
+
"target": "negative_branch",
|
|
118
|
+
"action": "false"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"source": "negative_branch",
|
|
122
|
+
"target": "send_to_ticketing_system"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"source": "negative_branch",
|
|
126
|
+
"target": "send_email_to_customer"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"source": "positive_branch",
|
|
130
|
+
"target": "final_step"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"source": "send_to_ticketing_system",
|
|
134
|
+
"target": "final_step"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"source": "send_email_to_customer",
|
|
138
|
+
"target": "final_step"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "gen_thanks",
|
|
5
|
+
"type": "llm-process",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Write a friendly \"thank you\" email to a customer for their positive feedback. Mention that you appreciate them highlighting: \"{{summary}}\".",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"summary": "summary"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "output_thanks",
|
|
15
|
+
"type": "output",
|
|
16
|
+
"data": {
|
|
17
|
+
"outputKey": "final_output",
|
|
18
|
+
"promptTemplate": "{{result}}",
|
|
19
|
+
"inputs": {
|
|
20
|
+
"result": "gen_thanks"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"edges": [
|
|
26
|
+
{
|
|
27
|
+
"source": "gen_thanks",
|
|
28
|
+
"target": "output_thanks"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "gen_ticket_data",
|
|
5
|
+
"type": "llm-process",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Generate a Jira-style ticket in JSON format. The JSON should have two keys: 'title' and 'description'. Title: 'Customer Feedback: {{category}}'. Description: 'User reported an issue summarized as: {{summary}}'.",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"category": "category",
|
|
10
|
+
"summary": "summary"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "gen_customer_reply",
|
|
16
|
+
"type": "llm-process",
|
|
17
|
+
"data": {
|
|
18
|
+
"promptTemplate": "Write a polite, reassuring email to a customer. Inform them that you have received their feedback regarding '{{summary}}' and have created a ticket for the team to review. Do not include a subject line.",
|
|
19
|
+
"inputs": {
|
|
20
|
+
"summary": "summary"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "output_ticket",
|
|
26
|
+
"type": "output",
|
|
27
|
+
"data": {
|
|
28
|
+
"outputKey": "ticket_payload",
|
|
29
|
+
"promptTemplate": "{{result}}",
|
|
30
|
+
"inputs": {
|
|
31
|
+
"result": "gen_ticket_data"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "output_reply",
|
|
37
|
+
"type": "output",
|
|
38
|
+
"data": {
|
|
39
|
+
"outputKey": "customer_message",
|
|
40
|
+
"promptTemplate": "{{result}}",
|
|
41
|
+
"inputs": {
|
|
42
|
+
"result": "gen_customer_reply"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"edges": [
|
|
48
|
+
{
|
|
49
|
+
"source": "gen_ticket_data",
|
|
50
|
+
"target": "output_ticket"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"source": "gen_customer_reply",
|
|
54
|
+
"target": "output_reply"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Use Case 3: Customer Review Analysis
|
|
2
|
+
|
|
3
|
+
This workflow demonstrates an advanced Directed Acyclic Graph (DAG) that includes parallel processing at the start, conditional branching, and a "fan-out" where a single node triggers multiple parallel downstream tasks.
|
|
4
|
+
|
|
5
|
+
## Main Workflow ID: 300
|
|
6
|
+
|
|
7
|
+
### Description
|
|
8
|
+
|
|
9
|
+
1. **Parallel Analysis**: The flow starts by running two tasks on the `initial_review` in parallel:
|
|
10
|
+
- `summarize`: Creates a one-sentence summary.
|
|
11
|
+
- `categorize`: Classifies the review (e.g., 'Bug Report').
|
|
12
|
+
2. **Conditional Check**: The `summary` is passed to `check_sentiment`, which uses an LLM to determine if the review is positive. It returns `true` or `false`. The `category` is also passed along for the negative path.
|
|
13
|
+
3. **Branching**:
|
|
14
|
+
- **On `true`**: The flow executes the `positive_branch` (Sub-Workflow 301) to thank the customer.
|
|
15
|
+
- **On `false`**: The flow executes the `negative_branch` (Sub-Workflow 302) to create a support ticket and a customer reply.
|
|
16
|
+
4. **Mid-Flow Fan-Out**: The `negative_branch` is a powerful example of fan-out. After it completes, its multiple outputs are used to trigger two separate, parallel actions:
|
|
17
|
+
- `send_to_ticketing_system`
|
|
18
|
+
- `send_email_to_customer`
|
|
19
|
+
5. **Convergence**: All paths eventually lead to the `final_step` node, which aggregates the results from whichever branch was executed.
|
|
20
|
+
|
|
21
|
+
### Visual Graph
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
graph TD
|
|
25
|
+
subgraph "Customer Review Analysis (ID: 300)"
|
|
26
|
+
A(Initial Review) --> B[summarize]
|
|
27
|
+
A --> C[categorize]
|
|
28
|
+
B --> D{check_sentiment}
|
|
29
|
+
C --> D
|
|
30
|
+
D -- true --> E["Sub-Workflow: Positive Reply (301)"]
|
|
31
|
+
D -- false --> F["Sub-Workflow: Create Ticket & Reply (302)"]
|
|
32
|
+
|
|
33
|
+
subgraph "Negative Path (Parallel Fan-Out)"
|
|
34
|
+
F --> G[send_to_ticketing_system]
|
|
35
|
+
F --> H[send_email_to_customer]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
E --> Z[final_step]
|
|
39
|
+
G --> Z
|
|
40
|
+
H --> Z
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Sub-Workflows
|
|
47
|
+
|
|
48
|
+
### Sub-Workflow ID: 301 (Send Thanks)
|
|
49
|
+
|
|
50
|
+
A simple workflow to generate a thank-you message for positive feedback.
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
graph TD
|
|
54
|
+
subgraph "Send Thanks (ID: 301)"
|
|
55
|
+
A[gen_thanks] --> B[output_thanks]
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Sub-Workflow ID: 302 (Create Ticket and Reply)
|
|
60
|
+
|
|
61
|
+
This sub-workflow itself runs tasks in parallel. It generates the data for a support ticket and a customer-facing reply simultaneously. These outputs are then passed back to the main flow to be used in the parallel fan-out step.
|
|
62
|
+
|
|
63
|
+
```mermaid
|
|
64
|
+
graph TD
|
|
65
|
+
subgraph "Create Ticket & Reply (ID: 302)"
|
|
66
|
+
A[gen_ticket_data] --> B[output_ticket]
|
|
67
|
+
C[gen_customer_reply] --> D[output_reply]
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": [
|
|
3
|
+
{
|
|
4
|
+
"id": "check_for_pii",
|
|
5
|
+
"type": "llm-condition",
|
|
6
|
+
"data": {
|
|
7
|
+
"promptTemplate": "Does the following text contain any Personally Identifiable Information (PII) like an email address, phone number, or physical address? Respond only with 'true' or 'false'.\n\nText: \"{{userPost}}\"",
|
|
8
|
+
"inputs": {
|
|
9
|
+
"userPost": "userPost"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "check_for_hate_speech",
|
|
15
|
+
"type": "llm-process",
|
|
16
|
+
"data": {
|
|
17
|
+
"promptTemplate": "Analyze the following text for hate speech. Respond with only ONE of the following severity levels: 'none', 'moderate', or 'severe'.\n\nText: \"{{userPost}}\"",
|
|
18
|
+
"inputs": {
|
|
19
|
+
"userPost": "userPost"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "check_for_spam",
|
|
25
|
+
"type": "llm-condition",
|
|
26
|
+
"data": {
|
|
27
|
+
"promptTemplate": "Does the following text appear to be commercial spam, containing suspicious links or promotional language? Respond only with 'true' or 'false'.\n\nText: \"{{userPost}}\"",
|
|
28
|
+
"inputs": {
|
|
29
|
+
"userPost": "userPost"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "triage_post",
|
|
35
|
+
"type": "llm-router",
|
|
36
|
+
"data": {
|
|
37
|
+
"promptTemplate": "Given the following analysis of a user post, decide on the single most appropriate action. Respond with ONLY ONE of the following action codes: 'action_ban', 'action_redact', 'action_delete_spam', 'action_approve'.\n\n- Hate Speech Level: {{hate_speech_level}}\n- PII Found: {{pii_found}}\n- Spam Detected: {{spam_detected}}\n\nRules:\n1. If Hate Speech is 'severe', the action is 'action_ban'.\n2. If PII is 'true' (and hate speech is not 'severe'), the action is 'action_redact'.\n3. If Spam is 'true' (and the above are false), the action is 'action_delete_spam'.\n4. Otherwise, the action is 'action_approve'.",
|
|
38
|
+
"inputs": {
|
|
39
|
+
"hate_speech_level": "check_for_hate_speech",
|
|
40
|
+
"pii_found": "check_for_pii",
|
|
41
|
+
"spam_detected": "check_for_spam"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "ban_user_branch",
|
|
47
|
+
"type": "sub-workflow",
|
|
48
|
+
"data": {
|
|
49
|
+
"workflowId": 401,
|
|
50
|
+
"inputs": {
|
|
51
|
+
"userId": "userId",
|
|
52
|
+
"reason": "check_for_hate_speech"
|
|
53
|
+
},
|
|
54
|
+
"outputs": {
|
|
55
|
+
"moderation_result": "ban_summary"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "redact_post_branch",
|
|
61
|
+
"type": "sub-workflow",
|
|
62
|
+
"data": {
|
|
63
|
+
"workflowId": 402,
|
|
64
|
+
"inputs": {
|
|
65
|
+
"userPost": "userPost",
|
|
66
|
+
"userId": "userId"
|
|
67
|
+
},
|
|
68
|
+
"outputs": {
|
|
69
|
+
"moderation_result": "redaction_summary"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "delete_spam_branch",
|
|
75
|
+
"type": "sub-workflow",
|
|
76
|
+
"data": {
|
|
77
|
+
"workflowId": 403,
|
|
78
|
+
"inputs": {
|
|
79
|
+
"userId": "userId"
|
|
80
|
+
},
|
|
81
|
+
"outputs": {
|
|
82
|
+
"moderation_result": "spam_deletion_summary"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "approve_post_branch",
|
|
88
|
+
"type": "llm-process",
|
|
89
|
+
"data": {
|
|
90
|
+
"promptTemplate": "The user post from {{userId}} was analyzed and approved with no issues. Create a simple log message confirming this.",
|
|
91
|
+
"inputs": {
|
|
92
|
+
"userId": "userId"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "final_log",
|
|
98
|
+
"type": "output",
|
|
99
|
+
"data": {
|
|
100
|
+
"outputKey": "final_output",
|
|
101
|
+
"promptTemplate": "--- CONTENT MODERATION COMPLETE ---\n\nFinal Action Log:\n{{final_action_taken}}",
|
|
102
|
+
"inputs": {
|
|
103
|
+
"final_action_taken": [
|
|
104
|
+
"moderation_result",
|
|
105
|
+
"approve_post_branch"
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"edges": [
|
|
112
|
+
{
|
|
113
|
+
"source": "check_for_pii",
|
|
114
|
+
"target": "triage_post",
|
|
115
|
+
"action": "true"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"source": "check_for_pii",
|
|
119
|
+
"target": "triage_post",
|
|
120
|
+
"action": "false"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"source": "check_for_hate_speech",
|
|
124
|
+
"target": "triage_post"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"source": "check_for_spam",
|
|
128
|
+
"target": "triage_post",
|
|
129
|
+
"action": "true"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"source": "check_for_spam",
|
|
133
|
+
"target": "triage_post",
|
|
134
|
+
"action": "false"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"source": "triage_post",
|
|
138
|
+
"target": "ban_user_branch",
|
|
139
|
+
"action": "action_ban"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"source": "triage_post",
|
|
143
|
+
"target": "redact_post_branch",
|
|
144
|
+
"action": "action_redact"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"source": "triage_post",
|
|
148
|
+
"target": "delete_spam_branch",
|
|
149
|
+
"action": "action_delete_spam"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"source": "triage_post",
|
|
153
|
+
"target": "approve_post_branch",
|
|
154
|
+
"action": "action_approve"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"source": "ban_user_branch",
|
|
158
|
+
"target": "final_log"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"source": "redact_post_branch",
|
|
162
|
+
"target": "final_log"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"source": "delete_spam_branch",
|
|
166
|
+
"target": "final_log"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"source": "approve_post_branch",
|
|
170
|
+
"target": "final_log"
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|