
Dify Workflow Skills
- 219 installs
- 3 repo stars
- Updated January 12, 2026
- mango-svip/dify-workflow-skills
Helps with automation & workflows tasks.
About
dify-workflow-skills is a Claude Code skill for automation & workflows. It helps solo builders move faster with AI-assisted development.
- dify-workflow-skills
- Automation & Workflows
- AI-coding skill
Dify Workflow Skills by the numbers
- 219 all-time installs (skills.sh)
- +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #570 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mango-svip/dify-workflow-skills --skill dify-workflow-skillsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 219 |
|---|---|
| repo stars | ★ 3 |
| Last updated | January 12, 2026 |
| Repository | mango-svip/dify-workflow-skills ↗ |
What it does
Helps with automation & workflows tasks.
Files
Dify Workflow DSL Builder
Build, edit, and validate Dify workflow DSL (Domain-Specific Language) files for creating AI-powered automation workflows.
This skill is based on the Dify open-source platform's workflow engine, which powers both Workflow apps and Advanced Chat apps with a React Flow-based visual editor.
Architecture Overview
Dify workflows use a queue-based, event-driven architecture with:
Backend (Python - Execution Engine)
- GraphEngine: Central orchestrator managing workflow execution
- WorkerPool: Thread pool for parallel node execution
- VariablePool: Centralized variable management across nodes
- EdgeProcessor: Handles conditional routing and branch selection
- Graph Validator: Ensures workflow integrity before execution
Frontend (React/TypeScript - Visual Editor)
- React Flow: Canvas-based node graph editor
- Zustand Store: State management for nodes, edges, and viewport
- WorkflowContext: Provides workflow state to component tree
- BaseNode: Common wrapper for all node types with handles, headers, and interactions
- Panel System: Dynamic configuration panels for each node type
- Hook Injection Pattern: Decouples UI from execution logic (draft sync, workflow run)
Integration Layer
- workflow: Core canvas engine (generic React Flow implementation)
- workflow-app: Application wrapper (business logic, API integration, lifecycle management)
- Draft Management: Auto-save with debouncing and
sendBeaconfor safety - Uses debounced sync to prevent excessive API calls
- Cancels pending syncs on unmount to avoid race conditions
- Tracks loaded state with
isWorkflowDataLoadedflag - Features System: Optional features (file upload, speech-to-text, citations) integrated with workflow
- Trigger Status Management: Separate store for tracking trigger node enable/disable state
- Trigger nodes can be enabled or disabled independently
- Status persists across workflow editor sessions
- Used for webhook, schedule, and plugin triggers
Core Capabilities
1. Create new workflows - Generate complete workflow YAML files from descriptions 2. Edit existing workflows - Add, modify, or remove nodes and connections 3. Validate workflows - Check DSL syntax and structure 4. Design complex flows - Build workflows with LLM nodes, code execution, branching, loops, and error handling 5. Error handling strategies - Implement fail-branch, retry, or default-value error handling
Quick Reference
Node Types: See references/node_types.md for complete list including:
- Core Flow: start, end, answer
- LLM & AI: llm, agent, parameter-extractor
- Logic & Control: if-else, question-classifier, loop, iteration
- Data Processing: code, template-transform, variable-aggregator, assigner, list-operator, document-extractor
- External Integration: http-request, tool, knowledge-retrieval, knowledge-index, datasource
- Advanced: trigger-webhook, trigger-schedule, trigger-plugin, human-input
Edge Types and Connections: See references/edge_types.md for:
- Source handle types (source, true/false, success-branch/fail-branch, loop)
- Edge data properties and validation rules
- Common connection patterns
Node Positioning: See references/node_positioning.md for:
- Canvas coordinate system and spacing guidelines
- Layout patterns (linear, branching, error handling, iteration)
- Position calculation formulas
Common Node Properties: All nodes support these internal properties (prefixed with _):
_runningStatus: Current execution status (running, succeeded, failed)_connectedSourceHandleIds/_connectedTargetHandleIds: Connection tracking_isSingleRun: Single execution mode for debugging_isCandidate: Phantom node during connection drag_children: Child nodes for container types (iteration, loop)_iterationLength/_iterationIndex: Iteration state tracking_loopLength/_loopIndex: Loop state tracking_retryIndex: Current retry attempt_waitingRun: Queued for execution, human-input
Workflow Structure: See references/workflow_structure.md for complete DSL format
Edge Types: See references/edge_types.md for connection patterns and handle types
Node Positioning: See references/node_positioning.md for layout guidelines
Templates: Check assets/ for example workflows
Creating a New Workflow
Step 1: Understand Requirements
Ask the user:
- What should the workflow do?
- What are the inputs and outputs?
- What processing steps are needed?
- Any conditional logic or error handling?
Step 2: Design Node Flow
Plan the workflow structure:
1. Start node - Define input variables 2. Processing nodes - LLM, code, tools, etc. 3. Control flow - if-else for branching, loop for iteration 4. Error handling - Use fail-branch on code nodes 5. Output aggregation - variable-aggregator to merge branches 6. End node - Define outputs
Step 3: Generate Node IDs
Use scripts/generate_id.py to create unique node IDs:
python3 scripts/generate_id.py 5 # Generate 5 unique IDsOr generate in Python/JavaScript:
# Python
import time
node_id = str(int(time.time() * 1000))// JavaScript (used in Dify frontend)
const nodeId = `${Date.now()}`Special ID patterns for container nodes:
- Iteration/Loop start nodes:
${parentNodeId}start - Example: If iteration node ID is
1736668800000, its start node ID is1736668800000start
Step 4: Build the Workflow
Create the complete YAML structure with:
- Top-level metadata (kind, version, app)
- App section (name, description, icon)
- Workflow section with features and graph
- Nodes array with positions
- Edges array connecting nodes
Template to use: Start from assets/simple_llm_workflow.yml for basic flows
Step 5: Validate
Check the workflow structure for common issues:
- All nodes have unique IDs
- All edges reference existing node IDs
- Start and end nodes exist
- Required fields are present
- Variable references use correct syntax:
{{#node_id.field#}}
Common Workflow Patterns
Pattern 1: Simple LLM Flow
Start → LLM → End
Use case: Basic question answering, text generation
Template: assets/simple_llm_workflow.yml
Pattern 2: Error Handling Flow
Start → Code (with fail-branch) → [Success → Aggregator] / [Fail → LLM Recovery → Retry → Aggregator] → End
Use case: Robust data processing with error recovery
Template: assets/error_handling_workflow.yml
Key points:
- Set
error_strategy: fail-branchon code node - Fail branch provides
error_messageanderror_typevariables - Use variable-aggregator to merge success/fail branches
- Reference aggregator output in end node
Pattern 3: Conditional Routing
Start → If-Else → [True → Handler A] / [False → Handler B] → Aggregator → End
Use case: Route requests based on content/type
Template: assets/conditional_workflow.yml
Key points:
- If-else has
trueandfalsesourceHandles - Use comparison operators: contains, starts_with, is_empty, etc.
- Combine conditions with logical_operator: "and" or "or"
Pattern 4: Loop Processing
Start → Loop → [Process Items] → Loop End → End
Use case: Iterative processing with break conditions
Key points:
- Set
loop_countfor maximum iterations - Define
break_conditionsto exit early - Loop maintains state across iterations
Editing Existing Workflows
Adding a Node
1. Read the existing workflow file 2. Generate a new unique node ID 3. Add node to workflow.graph.nodes array with:
- Unique ID and position
- Node type and configuration
- Proper sourcePosition/targetPosition
4. Add edge(s) to workflow.graph.edges connecting the new node 5. Update any dependent nodes (e.g., end node outputs)
Removing a Node
1. Identify the node ID to remove 2. Remove node from workflow.graph.nodes 3. Remove all edges referencing that node ID (as source or target) 4. Update downstream nodes that referenced the removed node's outputs
Modifying Connections
1. Find edge by source and target node IDs 2. Update edge source, target, sourceHandle, or targetHandle 3. Update edge data.sourceType and data.targetType to match actual node types 4. Update edge id to follow naming convention: {source_id}-{handle}-{target_id}-target
Variable Reference Syntax
Variables are managed in a centralized VariablePool and use the format: {{#node_id.field_name#}}
Variable Types (VarType)
Dify supports these variable types for type-safe data flow:
Primitive Types:
string: Text datanumber: Floating-point numbersinteger: Whole numbersboolean: true/false valuessecret: Encrypted sensitive data (API keys, passwords)
Complex Types:
object: JSON objectsfile: Single file referencearray: Generic arrayarray[string]: Array of stringsarray[number]: Array of numbersarray[object]: Array of objectsarray[boolean]: Array of booleansarray[file]: Array of filesarray[any]: Array of mixed typesany: Any type (use sparingly)
Special Types:
contexts: Knowledge retrieval resultsiterator: Iteration input variableloop: Loop input variable
File Upload Configuration
When using file input types (file, files, file-list), you can configure upload settings:
Default File Upload Settings:
allowed_file_upload_methods: ['local_file', 'remote_url']
max_length: 5 # Maximum number of files
allowed_file_types: ['image'] # Options: image, document, audio, video, custom
allowed_file_extensions: [] # e.g., ['.pdf', '.docx']Upload Methods:
local_file: Direct file upload from local systemremote_url: Upload file from URL
File Type Categories:
image: Image files (JPG, PNG, GIF, etc.)document: Document files (PDF, DOCX, TXT, etc.)audio: Audio files (MP3, WAV, etc.)video: Video files (MP4, AVI, etc.)custom: Custom file types (specify extensions)
Input Variable Types (InputVarType)
Start nodes use these input types for user-facing variables:
text-input: Single-line text inputparagraph: Multi-line text inputselect: Dropdown selectionnumber: Numeric inputcheckbox: Boolean checkboxurl: URL input with validationfiles: Multiple file uploadfile: Single file uploadfile-list: Multiple file listjson: JSON input (object or array)json_object: JSON object with schema validationcontexts: Knowledge retrieval contextiterator: Iteration variableloop: Loop variable
Variable Selector Pattern
The variable pattern regex: {{#[a-zA-Z0-9_]{1,50}(?:\.[a-zA-Z_][a-zA-Z0-9_]{0,29}){1,10}#}}
Selector structure: [node_id, variable_name, ...optional_nested_keys]
- First element: node ID that produced the variable
- Second element: variable name or output field
- Additional elements: nested object keys or array indices (for FileSegment/ObjectSegment)
ValueSelector: Array format used in DSL (e.g., ['1732007415808', 'text'])
- Represented as arrays in YAML:
value_selector: ['node_id', 'field_name'] - Converted to template syntax in prompts:
{{#node_id.field_name#}}
System Variables
Available via sys node ID:
{{#sys.query#}}- User query/input{{#sys.files#}}- Uploaded files{{#sys.conversation_id#}}- Current conversation ID{{#sys.user_id#}}- User identifier{{#sys.dialogue_count#}}- Number of dialogue turns{{#sys.app_id#}}- Application ID{{#sys.workflow_id#}}- Workflow ID{{#sys.workflow_run_id#}}- Current execution ID{{#sys.timestamp#}}- Current timestamp
Common Output Fields by Node Type
LLM Node:
{{#node_id.text#}}- Generated text response (type: string){{#node_id.usage#}}- Token usage information (type: object){{#node_id.reasoning_content#}}- Model reasoning (if enabled) (type: string)
Agent Node:
{{#node_id.usage#}}- Token usage information (type: object)
Code Node:
{{#node_id.output_name#}}- Named outputs defined in node config{{#node_id.error_message#}}- Error message (fail-branch only) (type: string){{#node_id.error_type#}}- Error type (fail-branch only) (type: string)
Start Node:
{{#node_id.variable_name#}}- Input variables defined in start node
If-Else Node:
{{#node_id.condition_result#}}- Boolean condition result
Loop Node:
{{#node_id.output#}}- Loop output array{{#node_id.iteration#}}- Current iteration number
HTTP Request Node:
{{#node_id.body#}}- Response body (type: string){{#node_id.status_code#}}- HTTP status code (type: number){{#node_id.headers#}}- Response headers (type: object){{#node_id.files#}}- Downloaded files if response is file (type: array[file])
Tool Node:
{{#node_id.text#}}- Tool output text (type: string){{#node_id.files#}}- Tool output files (type: array[file]){{#node_id.json#}}- Tool output JSON (type: array[object])
Knowledge Retrieval Node:
{{#node_id.result#}}- Retrieved knowledge segments (type: array[object])
Template Transform Node:
{{#node_id.output#}}- Transformed output (type: string)
Question Classifier Node:
{{#node_id.class_name#}}- Classification result (type: string){{#node_id.usage#}}- Token usage information (type: object)
Parameter Extractor Node:
{{#node_id.__is_success#}}- Extraction success indicator (type: number){{#node_id.__reason#}}- Extraction failure reason (type: string){{#node_id.__usage#}}- Token usage information (type: object)- Plus custom extracted parameters defined in node config
Variable Aggregator:
{{#node_id.output#}}- Aggregated output from merged branches
File Object Structure (when file type is used):
name- File name (type: string)size- File size in bytes (type: number)type- File type category (type: string)extension- File extension (type: string)mime_type- MIME type (type: string)transfer_method- Transfer method used (type: string)url- File URL (type: string)related_id- Related resource ID (type: string)
Knowledge Retrieval Result Structure:
{
"content": "",
"title": "",
"url": "",
"icon": "",
"metadata": {
"dataset_id": "",
"dataset_name": "",
"document_id": [],
"document_name": "",
"document_data_source_type": "",
"segment_id": "",
"segment_position": "",
"segment_word_count": "",
"segment_hit_count": "",
"segment_index_node_hash": "",
"score": ""
}
}Environment and Conversation Variables
Environment variables (defined at app level):
- Access via special node ID:
env - Example:
{{#env.API_KEY#}}
Conversation variables (session state):
- Access via special node ID:
conversation - Example:
{{#conversation.user_context#}}
Example Variable Usage
# In LLM prompt template
prompt_template:
- role: system
text: "You are a helpful assistant."
- role: user
text: "Process this input: {{#1732007415808.user_input#}}"
# In code node
variables:
- ["1732007415808", "user_input"]
- ["1732007420123", "processed_data"]
# Accessing nested object fields
text: "File name: {{#upload_node.files.name#}}"
text: "API response status: {{#http_node.status_code#}}"Node Positioning
Position nodes on the canvas for visual clarity:
Layout Constants (from Dify frontend):
NODE_WIDTH: 240 pixelsX_OFFSET: 60 pixels (horizontal spacing)NODE_WIDTH_X_OFFSET: 300 pixels (node width + spacing)Y_OFFSET: 39 pixelsSTART_INITIAL_POSITION: { x: 80, y: 282 }NODE_LAYOUT_HORIZONTAL_PADDING: 60 pixelsNODE_LAYOUT_VERTICAL_PADDING: 60 pixelsNODE_LAYOUT_MIN_DISTANCE: 100 pixels
Container Node Padding:
- Iteration/Loop containers:
- top: 65, right: 16, bottom: 20, left: 16
- Z-index for iteration/loop: 1 (container), 1002 (children)
Horizontal spacing: 300-400 pixels between connected nodes Vertical spacing:
- Same level: same y-coordinate
- Branches: offset by 150-200 pixels
Example positions:
Start: x=80, y=282 # Initial position
LLM: x=380, y=282 # Start + NODE_WIDTH_X_OFFSET
Code: x=680, y=282
End: x=980, y=282For branching:
If-else: x=380, y=300
True branch: x=680, y=200
False branch: x=680, y=450
Aggregator: x=980, y=300Container nodes (Iteration/Loop):
- Start node inside container: { x: 24, y: 68 } (relative to container)
- Children nodes have
parentIdset to container ID - Children have higher z-index (1002) than container (1)
See references/node_positioning.md for detailed layout patterns and formulas.
Visual Editor Integration
The DSL you create is rendered in the Dify visual workflow editor built with React Flow.
How DSL Maps to UI
Nodes → Visual blocks on canvas with:
- Icon and title (from
typeandtitlefields) - Connection handles (based on node type and error_strategy)
- Configuration panel (node-specific form in right sidebar)
- Status indicators (running, succeeded, failed)
Edges → Bezier curves connecting nodes with:
- Visual styling based on state (hovering, selected, running)
- Labels for branching paths (true/false, classification labels)
- Color coding for success/fail branches
Viewport → Canvas view settings:
viewport:
x: 0 # Pan offset X
y: 0 # Pan offset Y
zoom: 1.0 # Zoom level (0.1 to 2.0)Frontend Component Architecture
When your DSL is loaded into the editor:
1. WorkflowContextProvider initializes Zustand store with nodes/edges 2. ReactFlow renders the canvas with custom node components 3. BaseNode wraps each node with common UI (handles, headers) 4. Panel System shows configuration forms when node is selected 5. Hook System manages draft auto-save and execution
UI Features Not in DSL
These UI-only properties are managed by the frontend (don't include in DSL):
_hovering,_connectedNodeIsHovering: Mouse interaction state_connectedSourceHandleIds,_connectedTargetHandleIds: Computed from edges_runningStatus,_singleRunningStatus: Runtime execution state_isCandidate: Temporary phantom node during connection dragselected: Node selection state
Important: Only include persistent properties in your DSL (id, type, title, position, configuration). Runtime UI state is computed by the editor.
Error Handling Strategy
Error Strategies
Dify supports multiple error handling strategies defined in the node configuration:
1. fail-branch (recommended for code/http nodes):
- Creates alternative execution path on error
- Provides
error_messageanderror_typevariables - Uses
sourceHandle: "fail-branch"in edge configuration - Success path uses
sourceHandle: "success-branch" - Requires variable-aggregator to merge with success path before continuing to end node
- Allows graceful error recovery and custom error handling logic
2. default-value:
- Returns a predefined default value on error
- Continues main execution path without branching
- Simpler but less robust than fail-branch
- Good for non-critical operations where fallback values are acceptable
3. abort (default):
- Stops the entire workflow execution on failure
- No additional configuration needed
- Used when errors are unrecoverable
4. retry:
- Configurable retry logic with max retries and intervals
- Defined in node's
retry_configsection - Useful for transient failures (network issues, rate limits)
- Supported on: LLM, Tool, HTTP Request, Code nodes only
Retry Configuration Structure:
retry_config:
max_retries: 3 # Maximum retry attempts (default: 3)
retry_interval: 100 # Interval between retries in ms (default: 100)Implementing Fail-Branch Error Handling
In node configuration:
error_strategy: fail-branchIn edges array:
# Success path
- id: code_node-success-branch-next_node-target
source: code_node_id
target: aggregator_id
sourceHandle: success-branch
targetHandle: target
# Failure path
- id: code_node-fail-branch-error_handler-target
source: code_node_id
target: error_handler_id
sourceHandle: fail-branch
targetHandle: targetAvailable error variables in fail-branch:
{{#node_id.error_message#}}- Human-readable error description{{#node_id.error_type#}}- Error type classification
Validation Checklist
Before finalizing a workflow, verify:
Structural Validation
- [ ] All node IDs are unique and properly formatted (numeric strings or valid identifiers)
- [ ] Workflow has exactly one root node (start, datasource, or trigger node)
- [ ] Workflow has at least one end node
- [ ] All edges reference valid source and target node IDs that exist in the nodes array
- [ ] No circular dependencies that would create infinite loops (except intentional loop nodes)
- [ ] Root node is correctly identified and accessible
Edge and Connection Validation
- [ ] All edge IDs are unique
- [ ] Edge
sourceHandlevalues match node types: - Standard nodes:
"source"(default) - If-else/Question-classifier:
"true","false", or classification label - Code/HTTP with error handling:
"success-branch"or"fail-branch" - Loop nodes:
"loop"for continuation - [ ] Edge
targetHandleis typically"target"for most nodes - [ ] Edge
data.sourceTypematches the actual source node type - [ ] Edge
data.targetTypematches the actual target node type - [ ] Branching paths (from if-else, question-classifier) have edges for all possible outcomes
- [ ] Fail-branch edges exist when
error_strategy: fail-branchis used
Variable and Data Flow Validation
- [ ] Variable references use correct syntax:
{{#node_id.field#}} - [ ] All referenced variables exist in upstream nodes (nodes that execute before current node)
- [ ] Variable selectors match actual output fields of referenced nodes
- [ ] System variables use correct
sysprefix:{{#sys.query#}} - [ ] Environment variables use
envprefix if needed - [ ] No undefined variable references that would cause runtime errors
Error Handling Validation
- [ ] Nodes with
error_strategy: fail-branchhave both success and fail edges - [ ] Branching paths merge at variable-aggregator before reaching end node
- [ ] Variable aggregator receives inputs from all branch paths
- [ ] Default values are provided when using
error_strategy: default-value - [ ] Retry configurations are valid (max retries, intervals) if using retry strategy
Node Configuration Validation
- [ ] Required fields are present for each node type (see node_types.md)
- [ ] Node positions are set for visual layout (x, y coordinates)
- [ ] LLM nodes have valid model configurations (provider, name, mode)
- [ ] Code nodes specify valid language (python3 or javascript)
- [ ] HTTP request nodes have valid URLs and methods
- [ ] Loop nodes have valid
loop_countand break conditions - [ ] If-else nodes have properly structured conditions with valid operators
Workflow Execution Validation
- [ ] No nodes are unreachable (all nodes can be reached from root node)
- [ ] All execution paths eventually lead to an end node or answer node
- [ ] Container nodes (iteration, loop) have proper start/end node pairs
- [ ] Human-input nodes are used appropriately (workflow will pause for input)
- [ ] Trigger nodes (webhook, schedule) are not mixed with standard start nodes
Node Execution Types
Dify categorizes nodes by their execution behavior. Understanding these types helps design correct workflows:
EXECUTABLE (Standard Logic Nodes)
Execute logic and produce outputs. Most common node type.
- Examples: llm, code, http-request, knowledge-retrieval, template-transform
- Behavior: Execute when all input dependencies are satisfied
- Source Handle:
"source"(or"success-branch"/"fail-branch"with error handling) - Outputs: Defined by node type (see Variable Reference section)
BRANCH (Conditional Routing Nodes)
Control flow by choosing between multiple paths based on conditions.
- Examples: if-else, question-classifier
- Behavior: Evaluate conditions and activate one or more output edges
- Source Handles:
- If-else:
"true","false" - Question-classifier: classification labels (custom)
- Important: Unselected paths are marked as "skipped" and don't execute downstream
CONTAINER (Sub-graph Management)
Manage nested execution contexts with iterations or loops.
- Examples: iteration, loop
- Behavior: Execute internal sub-graph multiple times
- Components:
- Parent container node
- Internal start node (iteration-start, loop-start)
- Internal end node (iteration-end, loop-end)
- Source Handle:
"loop"for iteration/loop continuation - State Management: Maintain loop variables and iteration counts
RESPONSE (Output Streaming)
Stream outputs to users in real-time.
- Examples: answer, end
- Behavior: Output results and potentially complete workflow
- Usage: Answer nodes can appear mid-workflow; End nodes terminate execution
ROOT (Entry Points)
Serve as workflow entry points.
- Examples: start, datasource, trigger-webhook, trigger-schedule, trigger-plugin
- Behavior: First node to execute; no incoming edges
- Important: Only ONE root node per workflow
- Constraint: Standard start nodes and trigger nodes cannot coexist
Nodes That Support Output Variables
The following node types can produce output variables that other nodes can reference:
- Start, TriggerWebhook, TriggerPlugin
- LLM, Agent
- KnowledgeRetrieval
- Code
- TemplateTransform
- HttpRequest
- Tool
- VariableAssigner, VariableAggregator
- QuestionClassifier
- ParameterExtractor
- Iteration, Loop
- DocumentExtractor (DocExtractor)
- ListFilter (list-operator)
- DataSource
Note: Nodes not in this list (like if-else, end, answer) typically don't produce reusable output variables, though some may have limited internal state.
Workflow Execution Model
Execution Flow
1. Initialization: GraphEngine enqueues root node into ReadyQueue 2. Worker Execution: Worker threads pull nodes from ReadyQueue and execute them 3. Event Emission: Workers push events (started, succeeded, failed) to event_queue 4. Edge Processing: Dispatcher processes events and identifies downstream nodes 5. Dependency Resolution: Downstream nodes added to ReadyQueue when dependencies satisfied 6. Parallel Execution: Multiple workers execute independent nodes concurrently 7. Completion: Workflow ends when End node executes or execution fails
Edge States
Edges can be in three states during execution:
- UNKNOWN: Initial state, not yet evaluated
- TAKEN: Edge is traversed (condition met or path selected)
- SKIPPED: Edge is not traversed (condition failed or alternative path chosen)
Workflow Execution Status
Workflows progress through these states:
- SCHEDULED: Queued but not yet started
- RUNNING: Currently executing
- SUCCEEDED: Completed without errors
- FAILED: Terminated due to unhandled error
- PARTIAL_SUCCEEDED: Completed with handled errors (via fail-branch or default-value)
- STOPPED: Manually stopped or aborted
- PAUSED: Waiting for human input (human-input node)
Node Running Status
Individual nodes track their execution state with these statuses:
- NOT_START: Node hasn't started execution yet
- WAITING: Node is queued and waiting to execute
- LISTENING: Node is listening for events (trigger nodes)
- RUNNING: Node is currently executing
- SUCCEEDED: Node completed successfully
- FAILED: Node failed with unhandled error
- EXCEPTION: Node failed but error was handled
- RETRY: Node is retrying after failure
- STOPPED: Node execution was stopped
Important UI State Properties (runtime only, not in DSL):
_runningStatus: Current execution status_singleRunningStatus: Status when running single node_waitingRun: Node is queued for execution_retryIndex: Current retry attempt number_isSingleRun: Node is in single-run debug mode
Best Practices for Execution
1. Parallel vs Sequential: Independent nodes execute in parallel automatically. Use edges to enforce sequence when needed. 2. Error Boundaries: Use fail-branch on critical nodes to prevent workflow abortion 3. Merge Points: Always use variable-aggregator to merge branches before convergence 4. Loop Safety: Set reasonable loop_count limits and clear break conditions 5. Human Input: Plan for paused state when using human-input nodes
Resources
scripts/
generate_id.py- Generate unique node IDs for workflowsvalidate_workflow.py- Validate workflow DSL syntax (requires PyYAML)
references/
node_types.md- Complete reference of all Dify node types with examplesworkflow_structure.md- Detailed DSL structure and format specification
assets/
simple_llm_workflow.yml- Basic start→LLM→end templateerror_handling_workflow.yml- Template with fail-branch error handlingconditional_workflow.yml- Template with if-else branching
Tips for Success
1. Start simple: Begin with basic flows, add complexity incrementally 2. Use templates: Adapt existing templates rather than starting from scratch 3. Validate early: Check structure before adding many nodes 4. Plan error handling: Consider what can fail and how to handle it 5. Name clearly: Use descriptive node titles for maintainability 6. Reference docs: Consult node_types.md for field requirements 7. Test incrementally: Build and test workflows step by step
kind: app
version: 0.1.4
app:
name: Conditional Workflow
description: Workflow with if-else conditional branching
mode: workflow
icon: 🔀
icon_background: '#E5F5FF'
use_icon_as_answer_icon: false
workflow:
conversation_variables: []
environment_variables: []
features:
file_upload:
enabled: false
opening_statement: ''
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
graph:
nodes:
- id: '3000000000001'
type: custom
data:
type: start
title: Start
desc: ''
selected: false
variables:
- variable: user_query
label: User Query
type: paragraph
required: true
max_length: 10000
position:
x: 100
y: 300
positionAbsolute:
x: 100
y: 300
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '3000000000002'
type: custom
data:
type: if-else
title: Check Query Type
desc: ''
selected: false
logical_operator: and
conditions:
- variable_selector:
- '3000000000001'
- user_query
comparison_operator: contains
value: code
position:
x: 400
y: 300
positionAbsolute:
x: 400
y: 300
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '3000000000003'
type: custom
data:
type: llm
title: Code Assistant
desc: ''
selected: false
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
mode: chat
completion_params:
temperature: 0.3
prompt_template:
- id: code-system
role: system
text: You are a coding expert. Provide code solutions.
- id: code-user
role: user
text: '{{#3000000000001.user_query#}}'
context:
enabled: false
variable_selector: []
vision:
enabled: false
position:
x: 700
y: 200
positionAbsolute:
x: 700
y: 200
width: 244
height: 98
selected: false
sourcePosition: right
targetPosition: left
- id: '3000000000004'
type: custom
data:
type: llm
title: General Assistant
desc: ''
selected: false
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
mode: chat
completion_params:
temperature: 0.7
prompt_template:
- id: general-system
role: system
text: You are a helpful general assistant.
- id: general-user
role: user
text: '{{#3000000000001.user_query#}}'
context:
enabled: false
variable_selector: []
vision:
enabled: false
position:
x: 700
y: 400
positionAbsolute:
x: 700
y: 400
width: 244
height: 98
selected: false
sourcePosition: right
targetPosition: left
- id: '3000000000005'
type: custom
data:
type: variable-aggregator
title: Merge Responses
desc: ''
selected: false
output_type: string
variables:
- - '3000000000003'
- text
- - '3000000000004'
- text
position:
x: 1000
y: 300
positionAbsolute:
x: 1000
y: 300
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '3000000000006'
type: custom
data:
type: end
title: End
desc: ''
selected: false
outputs:
- variable: response
value_selector:
- '3000000000005'
- output
position:
x: 1300
y: 300
positionAbsolute:
x: 1300
y: 300
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
edges:
- id: 3000000000001-source-3000000000002-target
source: '3000000000001'
sourceHandle: source
target: '3000000000002'
targetHandle: target
type: custom
data:
sourceType: start
targetType: if-else
isInIteration: false
zIndex: 0
selected: false
- id: 3000000000002-true-3000000000003-target
source: '3000000000002'
sourceHandle: 'true'
target: '3000000000003'
targetHandle: target
type: custom
data:
sourceType: if-else
targetType: llm
isInIteration: false
zIndex: 0
selected: false
- id: 3000000000002-false-3000000000004-target
source: '3000000000002'
sourceHandle: 'false'
target: '3000000000004'
targetHandle: target
type: custom
data:
sourceType: if-else
targetType: llm
isInIteration: false
zIndex: 0
selected: false
- id: 3000000000003-source-3000000000005-target
source: '3000000000003'
sourceHandle: source
target: '3000000000005'
targetHandle: target
type: custom
data:
sourceType: llm
targetType: variable-aggregator
isInIteration: false
zIndex: 0
selected: false
- id: 3000000000004-source-3000000000005-target
source: '3000000000004'
sourceHandle: source
target: '3000000000005'
targetHandle: target
type: custom
data:
sourceType: llm
targetType: variable-aggregator
isInIteration: false
zIndex: 0
selected: false
- id: 3000000000005-source-3000000000006-target
source: '3000000000005'
sourceHandle: source
target: '3000000000006'
targetHandle: target
type: custom
data:
sourceType: variable-aggregator
targetType: end
isInIteration: false
zIndex: 0
selected: false
viewport:
x: 0
y: 0
zoom: 0.9
kind: app
version: 0.1.4
app:
name: Error Handling Workflow
description: Workflow demonstrating error handling with fail-branch and variable aggregator
mode: workflow
icon: 🛡️
icon_background: '#FFE5E5'
use_icon_as_answer_icon: false
workflow:
conversation_variables: []
environment_variables: []
features:
file_upload:
enabled: false
opening_statement: ''
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
graph:
nodes:
- id: '2000000000001'
type: custom
data:
type: start
title: Start
desc: ''
selected: false
variables:
- variable: input_data
label: Input Data
type: paragraph
required: true
max_length: 10000
position:
x: 100
y: 250
positionAbsolute:
x: 100
y: 250
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '2000000000002'
type: custom
data:
type: code
title: Process Data
desc: ''
selected: false
code: |
def main(input_data: str) -> dict:
# This may fail if input is invalid
result = json.loads(input_data)
return {'output': result}
code_language: python3
error_strategy: fail-branch
variables:
- variable: input_data
value_selector:
- '2000000000001'
- input_data
outputs:
output:
type: object
children: null
position:
x: 400
y: 250
positionAbsolute:
x: 400
y: 250
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '2000000000003'
type: custom
data:
type: llm
title: Error Handler
desc: ''
selected: false
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
mode: chat
completion_params:
temperature: 0.7
prompt_template:
- id: error-handler-system
role: system
text: You are an error recovery assistant.
- id: error-handler-user
role: user
text: 'Fix this error: {{#2000000000002.error_message#}} (Type: {{#2000000000002.error_type#}}). Original input: {{#2000000000001.input_data#}}'
context:
enabled: false
variable_selector: []
vision:
enabled: false
position:
x: 700
y: 400
positionAbsolute:
x: 700
y: 400
width: 244
height: 98
selected: false
sourcePosition: right
targetPosition: left
- id: '2000000000004'
type: custom
data:
type: code
title: Retry Process
desc: ''
selected: false
code: |
def main(fixed_data: str) -> dict:
result = json.loads(fixed_data)
return {'output': result}
code_language: python3
variables:
- variable: fixed_data
value_selector:
- '2000000000003'
- text
outputs:
output:
type: object
children: null
position:
x: 1000
y: 400
positionAbsolute:
x: 1000
y: 400
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '2000000000005'
type: custom
data:
type: variable-aggregator
title: Merge Results
desc: ''
selected: false
output_type: object
variables:
- - '2000000000002'
- output
- - '2000000000004'
- output
position:
x: 1300
y: 250
positionAbsolute:
x: 1300
y: 250
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '2000000000006'
type: custom
data:
type: end
title: End
desc: ''
selected: false
outputs:
- variable: final_result
value_selector:
- '2000000000005'
- output
position:
x: 1600
y: 250
positionAbsolute:
x: 1600
y: 250
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
edges:
- id: 2000000000001-source-2000000000002-target
source: '2000000000001'
sourceHandle: source
target: '2000000000002'
targetHandle: target
type: custom
data:
sourceType: start
targetType: code
isInIteration: false
zIndex: 0
selected: false
- id: 2000000000002-source-2000000000005-target
source: '2000000000002'
sourceHandle: source
target: '2000000000005'
targetHandle: target
type: custom
data:
sourceType: code
targetType: variable-aggregator
isInIteration: false
zIndex: 0
selected: false
- id: 2000000000002-fail-branch-2000000000003-target
source: '2000000000002'
sourceHandle: fail-branch
target: '2000000000003'
targetHandle: target
type: custom
data:
sourceType: code
targetType: llm
isInIteration: false
zIndex: 0
selected: false
- id: 2000000000003-source-2000000000004-target
source: '2000000000003'
sourceHandle: source
target: '2000000000004'
targetHandle: target
type: custom
data:
sourceType: llm
targetType: code
isInIteration: false
zIndex: 0
selected: false
- id: 2000000000004-source-2000000000005-target
source: '2000000000004'
sourceHandle: source
target: '2000000000005'
targetHandle: target
type: custom
data:
sourceType: code
targetType: variable-aggregator
isInIteration: false
zIndex: 0
selected: false
- id: 2000000000005-source-2000000000006-target
source: '2000000000005'
sourceHandle: source
target: '2000000000006'
targetHandle: target
type: custom
data:
sourceType: variable-aggregator
targetType: end
isInIteration: false
zIndex: 0
selected: false
viewport:
x: 0
y: 0
zoom: 0.8
kind: app
version: 0.1.4
app:
name: Simple LLM Workflow
description: A basic workflow with start, LLM, and end nodes
mode: workflow
icon: 🤖
icon_background: '#FFEAD5'
use_icon_as_answer_icon: false
workflow:
conversation_variables: []
environment_variables: []
features:
file_upload:
enabled: false
opening_statement: ''
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
graph:
nodes:
- id: '1000000000001'
type: custom
data:
type: start
title: Start
desc: ''
selected: false
variables:
- variable: user_input
label: User Input
type: paragraph
required: true
max_length: 10000
position:
x: 100
y: 200
positionAbsolute:
x: 100
y: 200
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '1000000000002'
type: custom
data:
type: llm
title: LLM
desc: ''
selected: false
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
mode: chat
completion_params:
temperature: 0.7
prompt_template:
- id: system-prompt-1
role: system
text: You are a helpful assistant.
- id: user-prompt-1
role: user
text: '{{#1000000000001.user_input#}}'
context:
enabled: false
variable_selector: []
vision:
enabled: false
position:
x: 400
y: 200
positionAbsolute:
x: 400
y: 200
width: 244
height: 98
selected: false
sourcePosition: right
targetPosition: left
- id: '1000000000003'
type: custom
data:
type: end
title: End
desc: ''
selected: false
outputs:
- variable: result
value_selector:
- '1000000000002'
- text
position:
x: 700
y: 200
positionAbsolute:
x: 700
y: 200
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
edges:
- id: 1000000000001-source-1000000000002-target
source: '1000000000001'
sourceHandle: source
target: '1000000000002'
targetHandle: target
type: custom
data:
sourceType: start
targetType: llm
isInIteration: false
zIndex: 0
selected: false
- id: 1000000000002-source-1000000000003-target
source: '1000000000002'
sourceHandle: source
target: '1000000000003'
targetHandle: target
type: custom
data:
sourceType: llm
targetType: end
isInIteration: false
zIndex: 0
selected: false
viewport:
x: 0
y: 0
zoom: 1
Dify Workflow Edge Types and Connections
This document describes edge (connection) types, handles, and routing behavior in Dify workflows.
Edge Structure
Every edge in the workflow DSL has this structure:
id: source_node_id-source_handle-target_node_id-target_handle
source: source_node_id
target: target_node_id
sourceHandle: handle_name
targetHandle: target
data:
sourceType: source_node_type # BlockEnum value
targetType: target_node_type # BlockEnum valueSource Handles by Node Type
Source handles determine which output path from a node is being connected.
Standard Nodes (EXECUTABLE type)
Handle: source (default)
Used by most processing nodes:
- llm, code, http-request, knowledge-retrieval
- template-transform, variable-aggregator, assigner
- tool, parameter-extractor, document-extractor
- list-operator, agent
Example:
- id: "1732007415808-source-1732007420123-target"
source: "1732007415808"
target: "1732007420123"
sourceHandle: source
targetHandle: targetError Handling Nodes
Handles: success-branch, fail-branch
Used when error_strategy: fail-branch is set on code or http-request nodes.
Success path (normal execution):
- id: "code_node-success-branch-next_node-target"
source: code_node_id
target: next_node_id
sourceHandle: success-branch
targetHandle: targetFail path (on error):
- id: "code_node-fail-branch-error_handler-target"
source: code_node_id
target: error_handler_id
sourceHandle: fail-branch
targetHandle: targetConditional Routing Nodes (BRANCH type)
If-Else Node
Handles: true, false
Evaluates conditions and routes to one of two paths:
# True branch
- id: "ifelse_node-true-handler_a-target"
source: ifelse_node_id
target: handler_a_id
sourceHandle: "true"
targetHandle: target
# False branch
- id: "ifelse_node-false-handler_b-target"
source: ifelse_node_id
target: handler_b_id
sourceHandle: "false"
targetHandle: targetQuestion Classifier Node
Handles: Custom classification labels
Routes based on classification result. Each classification class becomes a handle:
# Define classes in node config
classes:
- id: support_request
name: Support Request
- id: sales_inquiry
name: Sales Inquiry
- id: general_question
name: General Question
# Create edges for each class
- id: "classifier-support_request-support_handler-target"
source: classifier_id
target: support_handler_id
sourceHandle: support_request
targetHandle: target
- id: "classifier-sales_inquiry-sales_handler-target"
source: classifier_id
target: sales_handler_id
sourceHandle: sales_inquiry
targetHandle: targetContainer Nodes (Iteration/Loop)
Handles: loop, source
Container nodes have two output paths:
Loop continuation (during iteration):
- id: "iteration_node-loop-iteration_end-target"
source: iteration_node_id
target: iteration_end_id
sourceHandle: loop
targetHandle: targetCompletion (after all iterations):
- id: "iteration_node-source-next_node-target"
source: iteration_node_id
target: next_node_id
sourceHandle: source
targetHandle: targetTarget Handles
Target handles are typically target for all nodes. This is the standard input connection point.
Exception: Container internal nodes (iteration-start, loop-start) use target but are only connected from their parent container.
Edge Data Properties
Internal State Properties (prefixed with _)
These properties are managed by the React Flow UI and track runtime state:
_hovering: Mouse is hovering over edge_connectedNodeIsHovering: Connected node is being hovered_connectedNodeIsSelected: Connected node is selected_isBundled: Edge is part of a bundled group_sourceRunningStatus: Source node execution status_targetRunningStatus: Target node execution status_waitingRun: Edge is waiting for execution_isTemp: Temporary edge during drag operation
Required Data Properties
sourceType: BlockEnum type of source node (e.g., "llm", "code")targetType: BlockEnum type of target node
Container Context Properties
isInIteration: Edge is inside an iteration containeriteration_id: Parent iteration node IDisInLoop: Edge is inside a loop containerloop_id: Parent loop node ID
Edge States During Execution
Edges have three possible states during workflow execution:
1. UNKNOWN: Initial state, not yet evaluated 2. TAKEN: Edge is traversed (condition met or path selected) 3. SKIPPED: Edge is not traversed (condition failed or alternative path chosen)
State Transitions
Standard edges: UNKNOWN → TAKEN (when source node completes)
Conditional edges:
- If-else: One of (true/false) → TAKEN, the other → SKIPPED
- Question-classifier: Matching class → TAKEN, others → SKIPPED
Error handling edges:
- Success-branch: TAKEN on success, SKIPPED on error
- Fail-branch: SKIPPED on success, TAKEN on error
Common Edge Patterns
Sequential Flow
Start → Node A → Node B → End
edges:
- id: start-source-nodeA-target
source: start_id
sourceHandle: source
target: nodeA_id
targetHandle: target
- id: nodeA-source-nodeB-target
source: nodeA_id
sourceHandle: source
target: nodeB_id
targetHandle: target
- id: nodeB-source-end-target
source: nodeB_id
sourceHandle: source
target: end_id
targetHandle: targetBranching and Merging
Start → If-Else → [True → Handler A] → Aggregator → End
→ [False → Handler B] →
edges:
- id: start-source-ifelse-target
source: start_id
sourceHandle: source
target: ifelse_id
targetHandle: target
- id: ifelse-true-handlerA-target
source: ifelse_id
sourceHandle: "true"
target: handlerA_id
targetHandle: target
- id: ifelse-false-handlerB-target
source: ifelse_id
sourceHandle: "false"
target: handlerB_id
targetHandle: target
- id: handlerA-source-aggregator-target
source: handlerA_id
sourceHandle: source
target: aggregator_id
targetHandle: target
- id: handlerB-source-aggregator-target
source: handlerB_id
sourceHandle: source
target: aggregator_id
targetHandle: target
- id: aggregator-source-end-target
source: aggregator_id
sourceHandle: source
target: end_id
targetHandle: targetError Handling Flow
Start → Code → [Success → Next] → Aggregator → End
→ [Fail → Recovery] →
edges:
- id: start-source-code-target
source: start_id
sourceHandle: source
target: code_id
targetHandle: target
- id: code-success-branch-next-target
source: code_id
sourceHandle: success-branch
target: next_id
targetHandle: target
- id: code-fail-branch-recovery-target
source: code_id
sourceHandle: fail-branch
target: recovery_id
targetHandle: target
- id: next-source-aggregator-target
source: next_id
sourceHandle: source
target: aggregator_id
targetHandle: target
- id: recovery-source-aggregator-target
source: recovery_id
sourceHandle: source
target: aggregator_id
targetHandle: targetIteration Loop
Start → Iteration → Iteration-Start → Process → Iteration-End → Next
↑ ↓
└─────────────────────────────────────────────┘
edges:
- id: start-source-iteration-target
source: start_id
sourceHandle: source
target: iteration_id
targetHandle: target
- id: iteration-loop-iteration_start-target
source: iteration_id
sourceHandle: loop
target: iteration_start_id
targetHandle: target
data:
isInIteration: true
iteration_id: iteration_id
- id: iteration_start-source-process-target
source: iteration_start_id
sourceHandle: source
target: process_id
targetHandle: target
data:
isInIteration: true
iteration_id: iteration_id
- id: process-source-iteration_end-target
source: process_id
sourceHandle: source
target: iteration_end_id
targetHandle: target
data:
isInIteration: true
iteration_id: iteration_id
- id: iteration-source-next-target
source: iteration_id
sourceHandle: source
target: next_id
targetHandle: targetEdge Validation Rules
When creating or modifying edges, ensure:
1. Unique IDs: Each edge must have a unique ID 2. Valid References: Source and target must reference existing node IDs 3. Handle Compatibility: sourceHandle must match source node's available handles 4. Type Matching: data.sourceType and data.targetType must match actual node types 5. No Self-Loops: An edge cannot connect a node to itself (except for container loop logic) 6. Branch Completeness: Branching nodes should have edges for all possible outcomes 7. Error Handling Pairs: Nodes with fail-branch must have both success and fail edges 8. Container Context: Edges inside iteration/loop must have proper iteration_id/loop_id 9. Root Node Isolation: Root nodes (start, trigger nodes) cannot have incoming edges 10. Convergence: Branching paths should converge at a variable-aggregator before merging to single path
Handle Naming Convention
Standard handle names follow these patterns:
- Default:
sourceandtarget - Conditions:
true,false - Error handling:
success-branch,fail-branch - Container:
loop - Custom: Classification labels (question-classifier)
Always use lowercase with hyphens for multi-word handles.
Dify Workflow Node Positioning Guide
This document describes best practices for positioning nodes on the visual workflow canvas.
Coordinate System
Dify workflows use a standard 2D canvas coordinate system:
- X-axis: Horizontal position (left to right)
- Y-axis: Vertical position (top to bottom)
- Origin: Top-left corner (0, 0)
- Units: Pixels
Node Dimensions
Standard Node Sizes
Based on the React Flow implementation, nodes have these approximate dimensions:
Default Node: 240px width × 90-120px height (varies by content)
Note: The actual node width constant in source code is NODE_WIDTH = 240.
Specific Node Types (width is 240px for all standard nodes):
- Start Node: 240px × 90px
- End Node: 240px × 100px
- LLM Node: 240px × 120px (with model display)
- Code Node: 240px × 110px
- If-Else Node: 240px × 130px
- Question Classifier: 240px × 150px
- Iteration/Loop Container: Variable (parent node) + internal graph area
- HTTP Request: 240px × 110px
- Knowledge Retrieval: 240px × 120px
- Variable Aggregator: 240px × 90px
Note: Actual rendered sizes may vary based on:
- Node title length
- Number of variables/configurations displayed
- Error messages or status indicators
- Custom node content
Spacing Guidelines
Official Layout Constants
From the Dify source code (workflow/constants.ts):
- NODE_WIDTH: 240px (standard node width)
- X_OFFSET: 60px (horizontal offset between nodes)
- NODE_WIDTH_X_OFFSET: 300px (NODE_WIDTH + X_OFFSET)
- Y_OFFSET: 39px (vertical offset)
- START_INITIAL_POSITION:
{ x: 80, y: 282 }(default start node position) - AUTO_LAYOUT_OFFSET:
{ x: -42, y: 243 }(auto-layout adjustment offset)
Horizontal Spacing
Standard gap between sequential nodes: 300px (NODE_WIDTH_X_OFFSET)
- Node width: 240px
- Gap between nodes: 60px
This provides comfortable visual separation and room for edge labels.
Node A (x=80) → 300px total → Node B (x=380)Recommended horizontal spacing:
- Standard: 300px (recommended, matches NODE_WIDTH_X_OFFSET)
- Minimal: 260px (tight layout, NODE_WIDTH + 20px clearance)
- Spacious: 350-400px (for complex workflows with many labels)
Vertical Spacing
Same level nodes: Same y-coordinate
Branch separation: 150-250 pixels vertical offset
Main path: y=300
True branch: y=200 (100px above)
False branch: y=450 (150px below)Recommended vertical spacing for branches:
- Two branches: ±150px from main path
- Three branches: ±200px from main path
- Four+ branches: ±250px from main path
Canvas Margins
Initial node position: Start with generous margins
- X margin: 100-200px from left edge
- Y margin: 200-300px from top edge
This ensures nodes aren't cut off and provides room for expansion.
Common Layout Patterns
1. Linear Flow (Sequential)
Horizontal left-to-right flow (using official constants):
Start: { x: 80, y: 282 } # START_INITIAL_POSITION
LLM: { x: 380, y: 282 } # 80 + 300 (NODE_WIDTH_X_OFFSET)
Code: { x: 680, y: 282 } # 380 + 300
End: { x: 980, y: 282 } # 680 + 300Visual representation:
Start → LLM → Code → End2. Binary Branching (If-Else)
Y-shaped pattern with two branches converging:
Start: { x: 80, y: 282 } # START_INITIAL_POSITION
If-Else: { x: 380, y: 282 } # 80 + 300
True-Path: { x: 680, y: 200 } # 82px above center
False-Path: { x: 680, y: 400 } # 118px below center
Aggregator: { x: 980, y: 282 } # Back to center
End: { x: 1280, y: 282 }Visual representation:
┌─ True-Path ─┐
Start → If ───┤ ├─ Aggregator → End
└─ False-Path ┘3. Multi-Branch Classification
Fan-out pattern with multiple paths:
Start: { x: 100, y: 350 }
Classifier: { x: 450, y: 350 }
Branch-1: { x: 800, y: 150 } # 200px above
Branch-2: { x: 800, y: 300 } # 50px above
Branch-3: { x: 800, y: 450 } # 100px below
Branch-4: { x: 800, y: 600 } # 250px below
Aggregator: { x: 1150, y: 350 }
End: { x: 1500, y: 350 }4. Error Handling Pattern
Success/fail branches with recovery:
Start: { x: 100, y: 300 }
Code: { x: 450, y: 300 }
Success: { x: 800, y: 250 } # 50px above
Fail: { x: 800, y: 400 } # 100px below
Recovery: { x: 1150, y: 400 } # Aligned with fail path
Aggregator: { x: 1500, y: 300 }
End: { x: 1850, y: 300 }Visual representation:
┌─ Success ────────────┐
Start → Code ─┤ ├─ Aggregator → End
└─ Fail → Recovery ────┘5. Iteration/Loop Container
Container with internal graph:
# Parent nodes
Start: { x: 100, y: 300 }
Iteration: { x: 450, y: 300 }
End: { x: 1400, y: 300 }
# Internal iteration nodes (offset within container)
Iter-Start: { x: 550, y: 380 } # Inside container
Process-1: { x: 800, y: 380 }
Process-2: { x: 1050, y: 380 }
Iter-End: { x: 1300, y: 380 }Container sizing: Parent iteration node should be positioned to visually contain internal nodes, typically with 50px padding.
6. Vertical Flow (Top to Bottom)
Alternative layout for narrow canvases:
Start: { x: 400, y: 100 }
LLM: { x: 400, y: 300 } # 200px below
Code: { x: 400, y: 500 } # 200px below
End: { x: 400, y: 700 } # 200px belowPosition Calculation Formulas
Sequential Horizontal Layout
# Using official Dify constants
NODE_WIDTH = 240
X_OFFSET = 60
NODE_WIDTH_X_OFFSET = 300 # NODE_WIDTH + X_OFFSET
START_INITIAL_POSITION = {'x': 80, 'y': 282}
node_positions = []
x = START_INITIAL_POSITION['x']
y = START_INITIAL_POSITION['y']
for i, node in enumerate(nodes):
node_positions.append({
'x': x + i * NODE_WIDTH_X_OFFSET,
'y': y
})
# Result: [80, 380, 680, 980, ...]Binary Branch Layout
# Main path (using START_INITIAL_POSITION)
main_y = 282
# If-else node
ifelse_x = 80 + 300 # 380
ifelse_y = main_y
# Branches
branch_x = ifelse_x + 300 # 680
true_y = main_y - 82 # 200 (above)
false_y = main_y + 118 # 400 (below)
# Aggregator (convergence)
agg_x = branch_x + 300 # 980
agg_y = main_y # 282 (back to center)Multi-Branch Spread
num_branches = 4
main_y = 350
total_spread = 450 # Total vertical range
branch_spacing = total_spread / (num_branches - 1)
branch_positions = []
for i in range(num_branches):
y = (main_y - total_spread / 2) + (i * branch_spacing)
branch_positions.append({'x': 800, 'y': y})Grid Alignment
For cleaner layouts, consider snapping nodes to a grid:
Grid size: 50 pixels
def snap_to_grid(position, grid_size=50):
return {
'x': round(position['x'] / grid_size) * grid_size,
'y': round(position['y'] / grid_size) * grid_size
}Example:
Unaligned: { x: 437, y: 289 }
Snapped: { x: 450, y: 300 }Auto-Layout Considerations
The Dify visual editor uses React Flow's layout features. When generating DSL programmatically:
1. Calculate bounds: Ensure all nodes fit within a reasonable canvas size 2. Avoid overlaps: Check for node collisions before finalizing positions 3. Balance branching: Spread branches evenly around main path 4. Maintain flow direction: Keep consistent left-to-right or top-to-bottom flow 5. Reserve space: Leave room for user to add nodes manually
Canvas Size Recommendations
Minimum canvas: 1200px × 800px (for simple flows) Standard canvas: 2000px × 1200px (for moderate complexity) Large canvas: 3000px × 1600px (for complex multi-branch flows)
Position Validation
Before finalizing node positions, check:
- [ ] No nodes have negative x or y coordinates
- [ ] No nodes overlap (minimum 50px clearance)
- [ ] All nodes are visible within reasonable zoom levels (0.5x to 2x)
- [ ] Edges between nodes don't cross unnecessarily
- [ ] Visual hierarchy is clear (main path is obvious)
- [ ] Branches are symmetrically distributed
Example: Complete Workflow Layout
# Linear flow with branching
nodes:
- id: "1732007415808"
position: { x: 100, y: 300 }
type: start
- id: "1732007420123"
position: { x: 450, y: 300 }
type: llm
- id: "1732007425456"
position: { x: 800, y: 300 }
type: if-else
- id: "1732007430789"
position: { x: 1150, y: 200 } # True branch
type: code
- id: "1732007435012"
position: { x: 1150, y: 450 } # False branch
type: http-request
- id: "1732007440234"
position: { x: 1500, y: 300 } # Convergence
type: variable-aggregator
- id: "1732007445567"
position: { x: 1850, y: 300 }
type: endTips for Manual Adjustments
When users manually adjust positions in the visual editor:
1. Preserve alignment: Keep related nodes vertically aligned 2. Maintain spacing: Try to keep consistent horizontal gaps 3. Group related nodes: Position error handlers near their source 4. Use whitespace: Don't overcrowd the canvas 5. Think hierarchically: Main path should be most prominent
React Flow Specifics
The Dify visual editor uses React Flow with these defaults:
- Default zoom: 1.0 (100%)
- Min zoom: 0.1 (10%)
- Max zoom: 2.0 (200%)
- Snap to grid: Optional (usually disabled for flexibility)
- Auto-pan: Enabled (canvas pans when dragging nodes near edge)
Initial viewport:
viewport:
x: 0
y: 0
zoom: 1.0When generating workflows programmatically, you can set viewport to center on the graph:
# Calculate center of all nodes
center_x = (min_x + max_x) / 2
center_y = (min_y + max_y) / 2
# Set viewport to center on graph
viewport = {
'x': -center_x + canvas_width / 2,
'y': -center_y + canvas_height / 2,
'zoom': 1.0
}Dify Workflow Node Types Reference
This document describes all available node types in Dify workflow DSL based on the actual Dify codebase implementation.
Node Type Classification
Nodes are classified by execution type:
- EXECUTABLE: Standard logic nodes (llm, code, http-request, etc.)
- BRANCH: Conditional routing nodes (if-else, question-classifier)
- CONTAINER: Sub-graph management (iteration, loop)
- RESPONSE: Output streaming (answer, end)
- ROOT: Entry points (start, datasource, trigger-webhook, trigger-schedule, trigger-plugin)
Core Node Types
1. start
Purpose: Entry point of the workflow, defines input variables
Required fields:
type: "start"title: Display namevariables: Array of input variable definitions
Variable fields:
variable: Variable namelabel: Display labeltype: Variable type (text, paragraph, number, select, etc.)required: Booleanmax_length: Maximum length (for text types)options: Array of options (for select type)
Example:
type: start
title: Start
variables:
- label: User Input
max_length: 10000
required: true
type: paragraph
variable: user_input2. end
Purpose: Terminal node that defines workflow outputs
Required fields:
type: "end"title: Display nameoutputs: Array of output variable selectors
Output fields:
variable: Output variable namevalue_selector: Array path to source value (e.g., ['node_id', 'field_name'])
Example:
type: end
title: End
outputs:
- variable: result
value_selector:
- '1733478262179'
- text
- variable: error_message
value_selector:
- '1733478343153'
- error_message3. llm
Purpose: Large Language Model inference node
Required fields:
type: "llm"title: Display namemodel: Model configurationprompt_template: Array of message templates
Model fields:
provider: Model provider (anthropic, openai, etc.)name: Model name (claude-3-5-sonnet-20241022, gpt-4, etc.)mode: chat or completioncompletion_params: Parameters like temperature
Prompt template fields:
id: Unique identifierrole: system, user, or assistanttext: Template text with variable references {{#node_id.field#}}
Context fields (optional):
enabled: Booleanvariable_selector: Array of context variable paths
Vision fields (optional):
enabled: Boolean
Example:
type: llm
title: Generate Response
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
mode: chat
completion_params:
temperature: 0.7
prompt_template:
- id: unique-id-1
role: system
text: You are a helpful assistant.
- id: unique-id-2
role: user
text: "{{#1732007415808.user_input#}}"
context:
enabled: false
variable_selector: []
vision:
enabled: false4. code
Purpose: Execute Python or JavaScript code
Execution Type: EXECUTABLE
Required fields:
type: "code"title: Display namecode: Code stringcode_language: "python3" or "javascript" (CodeLanguage enum)variables: Input variables arrayoutputs: Output type definitions
Variable fields:
- Array of variable selectors:
[node_id, field_name]
Output fields:
[output_name]: Output variable nametype: Data type from SegmentType (string, number, object, boolean, array_string, array_number, array_object, array_boolean)children: Nested structure for object types (recursive)
Error handling fields (optional):
error_strategy: "fail-branch" or "default-value" (ErrorStrategy enum)dependencies: Optional array of package dependenciesname: Package nameversion: Package version
Source Handles:
- Default:
"source"(success path) - With fail-branch:
"success-branch"(success),"fail-branch"(error)
Outputs:
- Named outputs as defined in
outputsconfig - On fail-branch:
error_message,error_type
Example:
type: code
title: Process Data
code: |
def main(input_text: str) -> dict:
result = input_text.upper()
return {'output': result}
code_language: python3
variables:
- - '1733478262179'
- text
outputs:
output:
type: string
children: null
error_strategy: fail-branch
dependencies:
- name: requests
version: "2.31.0"5. variable-aggregator
Purpose: Merge multiple conditional branch outputs into a single variable
Required fields:
type: "variable-aggregator"title: Display namevariables: Array of variable selector paths to aggregateoutput_type: Data type (string, number, object, array)
Usage: Required when downstream nodes need to reference values from multiple possible upstream branches (e.g., success/fail branches)
Example:
type: variable-aggregator
title: Merge Results
output_type: object
variables:
- - '1733478343153'
- result
- - '17334785192390'
- result6. if-else
Purpose: Conditional branching based on conditions
Execution Type: BRANCH
Required fields:
type: "if-else"title: Display namecases: Array of Case objects (new format) ORconditions+logical_operator(legacy)
Case structure (recommended):
case_id: Unique case identifierlogical_operator: "and" or "or"conditions: Array of Condition objects
Condition fields:
variable_selector: Array path to value[node_id, field_name]comparison_operator: SupportedComparisonOperator- String/Array: "contains", "not contains", "start with", "end with", "is", "is not", "empty", "not empty", "in", "not in", "all of"
- Number: "=", "≠", ">", "<", "≥", "≤", "null", "not null"
- File: "exists", "not exists"
value: Comparison value (string, array of strings, or boolean)sub_variable_condition: Optional nested conditions for objectslogical_operator: "and" or "or"conditions: Array of SubCondition objects
Source Handles:
"true": Condition(s) met"false": Condition(s) not met
Outputs:
condition_result: Boolean result
Example:
type: if-else
title: Check Condition
cases:
- case_id: case_1
logical_operator: and
conditions:
- variable_selector:
- '1733478262179'
- text
comparison_operator: contains
value: "error"
- variable_selector:
- '1733478262179'
- status
comparison_operator: "="
value: "failed"7. iteration
Purpose: Loop over an array/list
Required fields:
type: "iteration"title: Display nameinput_selector: Array path to iterableoutput_selector: Array path to aggregated output
Example:
type: iteration
title: Process Items
input_selector:
- '1733478262179'
- items8. knowledge-retrieval
Purpose: Retrieve information from knowledge base
Required fields:
type: "knowledge-retrieval"title: Display namedataset_ids: Array of dataset IDsquery_variable_selector: Array path to query text
Example:
type: knowledge-retrieval
title: Search Knowledge
dataset_ids:
- dataset-id-1
query_variable_selector:
- '1732007415808'
- user_query9. http-request
Purpose: Make HTTP API calls
Execution Type: EXECUTABLE
Required fields:
type: "http-request"title: Display namemethod: "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS" (case insensitive)url: Request URL (supports variable substitution)authorization: Authorization configurationheaders: Request headers stringparams: Query parameters string
Authorization structure:
type: "no-auth" or "api-key"config: HttpRequestNodeAuthorizationConfig (when type is "api-key")type: "basic", "bearer", or "custom"api_key: API key valueheader: Custom header name (for custom type)
Body structure (for POST/PUT/PATCH):
type: "none", "form-data", "x-www-form-urlencoded", "raw-text", "json", or "binary"data: Array of BodyData objectskey: Field keytype: "file" or "text"value: Field value (text type)file: File variable selector (file type)
Timeout structure (optional):
connect: Connect timeout in seconds (default from config)read: Read timeout in seconds (default from config)write: Write timeout in seconds (default from config)
Other fields:
ssl_verify: Boolean, verify SSL certificates (default from config)error_strategy: "fail-branch" or "default-value" (optional)
Source Handles:
- Default:
"source" - With fail-branch:
"success-branch","fail-branch"
Outputs:
body: Response body (text or file)status_code: HTTP status code (integer)headers: Response headers (object)files: Downloaded file(s) if response is file type- On fail-branch:
error_message,error_type
Example:
type: http-request
title: API Call
method: POST
url: "https://api.example.com/endpoint"
authorization:
type: api-key
config:
type: bearer
api_key: "{{#env.API_KEY#}}"
headers: |
Content-Type: application/json
Accept: application/json
params: ""
body:
type: json
data:
- key: "input"
type: text
value: "{{#1732007415808.user_input#}}"
timeout:
connect: 10
read: 30
write: 30
ssl_verify: true
error_strategy: fail-branch10. template-transform
Purpose: Transform variables using Jinja2 templates
Required fields:
type: "template-transform"title: Display nametemplate: Jinja2 template stringvariables: Input variables
Example:
type: template-transform
title: Format Output
template: "Hello {{name}}, your score is {{score}}"
variables:
- variable: name
value_selector:
- '1732007415808'
- user_name
- variable: score
value_selector:
- '1733478343153'
- resultAdditional Node Types
11. answer
Purpose: Output answer to user in chatflow/workflow
Required fields:
type: "answer"title: Display nameanswer: Answer template string with variable references
Example:
type: answer
title: Answer
answer: "The result is: {{#1733478262179.text#}}"12. loop
Purpose: Execute a set of nodes multiple times with break conditions
Execution Type: CONTAINER
Required fields:
type: "loop"title: Display nameloop_count: Maximum number of iterations (integer)break_conditions: Array of Condition objects to break the looplogical_operator: "and" or "or" for combining break conditionsloop_variables: Optional array of LoopVariableData for loop stateoutputs: Output configuration (dictionary)
Loop Variable structure:
label: Variable label/namevar_type: SegmentType (string, number, object, boolean, array types)value_type: "variable" or "constant"value: Initial value (variable selector or constant value)
Break condition fields: (same as if-else conditions)
variable_selector: Array path to valuecomparison_operator: SupportedComparisonOperatorvalue: Comparison value
Source Handles:
"loop": Continue loop iteration"source": Exit loop (after completion or break)
Outputs:
output: Array of outputs from each iterationiteration: Current iteration number- On completion: metadata includes
completed_reason("loop_break" or "loop_completed")
Internal nodes:
- Loop creates internal sub-graph with loop-start and loop-end nodes
- Nodes between loop-start and loop-end execute on each iteration
Example:
type: loop
title: Loop Processing
loop_count: 10
logical_operator: or
break_conditions:
- variable_selector:
- '1733478262179'
- is_complete
comparison_operator: is
value: "true"
- variable_selector:
- '1733478262179'
- error_count
comparison_operator: ≥
value: "3"
loop_variables:
- label: counter
var_type: number
value_type: constant
value: 0
- label: accumulated_result
var_type: array_object
value_type: constant
value: []
outputs:
result:
type: array_object13. tool
Purpose: Call external tools or plugins
Required fields:
type: "tool"title: Display nameprovider_id: Tool provider identifierprovider_type: Tool provider typetool_name: Name of the tooltool_parameters: Tool-specific parameters
Example:
type: tool
title: Call API Tool
provider_id: provider-123
provider_type: api
tool_name: fetch_data
tool_parameters:
endpoint: "{{#1732007415808.api_endpoint#}}"14. parameter-extractor
Purpose: Extract structured parameters from LLM output
Required fields:
type: "parameter-extractor"title: Display namemodel: Model configurationquery: Input text to extract fromparameters: Parameter schema definitions
Example:
type: parameter-extractor
title: Extract Parameters
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
query:
variable_selector:
- '1732007415808'
- user_input
parameters:
- name: date
type: string
description: Extract the date mentioned
required: true15. variable-assigner
Purpose: Assign or transform variables
Required fields:
type: "variable-assigner"title: Display namevariables: Variable assignments
Example:
type: variable-assigner
title: Assign Variables
variables:
- variable: output_var
value_selector:
- '1733478262179'
- text16. question-classifier
Purpose: Classify user questions into categories
Required fields:
type: "question-classifier"title: Display namemodel: Model configurationquery_variable_selector: Input queryclasses: Classification categories
Example:
type: question-classifier
title: Classify Question
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
query_variable_selector:
- '1732007415808'
- user_query
classes:
- name: technical
description: Technical questions
- name: general
description: General questions17. document-extractor
Purpose: Extract content from documents
Required fields:
type: "document-extractor"title: Display namevariable_selector: Document input
Example:
type: document-extractor
title: Extract Document
variable_selector:
- '1732007415808'
- document_file18. list-operator
Purpose: Perform operations on lists (filter, map, reduce)
Execution Type: EXECUTABLE
Required fields:
type: "list-operator"title: Display nameoperation: Operation typevariable_selector: Input list selector
Outputs:
- Depends on operation type
Example:
type: list-operator
title: Filter List
operation: filter
variable_selector:
- '1733478262179'
- itemsAdvanced Node Types
19. agent
Purpose: Agent-based autonomous task execution
Execution Type: EXECUTABLE
Required fields:
type: "agent"title: Display namemodel: Model configurationtools: Available tools for agentprompt: Agent instructions
Example:
type: agent
title: Research Agent
model:
provider: anthropic
name: claude-3-5-sonnet-20241022
tools:
- web_search
- calculator
prompt: "Research and provide information about {{#1732007415808.topic#}}"20. trigger-webhook
Purpose: Webhook trigger for workflow execution
Execution Type: ROOT
Required fields:
type: "trigger-webhook"title: Display namevariables: Input variable definitions
Important: Cannot coexist with standard start nodes
Example:
type: trigger-webhook
title: Webhook Trigger
variables:
- variable: payload
type: object
required: true21. trigger-schedule
Purpose: Scheduled/cron-based workflow execution
Execution Type: ROOT
Required fields:
type: "trigger-schedule"title: Display nameschedule: Cron expression or schedule config
Important: Cannot coexist with standard start nodes
Example:
type: trigger-schedule
title: Daily Schedule
schedule:
cron: "0 9 * * *"
timezone: "UTC"22. trigger-plugin
Purpose: Plugin-based workflow trigger
Execution Type: ROOT
Required fields:
type: "trigger-plugin"title: Display nameplugin_id: Plugin identifier
Important: Cannot coexist with standard start nodes
23. human-input
Purpose: Pause workflow for human input
Execution Type: EXECUTABLE
Required fields:
type: "human-input"title: Display namevariables: Input variable definitions
Behavior:
- Workflow status changes to PAUSED
- Execution resumes when human provides input
Example:
type: human-input
title: Request Approval
variables:
- variable: approval_decision
type: select
options:
- approve
- reject
required: true24. datasource
Purpose: Data source integration as workflow entry point
Execution Type: ROOT
Required fields:
type: "datasource"title: Display namedatasource_config: Data source configuration
Important: Can serve as root node (alternative to start node)
25. knowledge-index
Purpose: Index content into knowledge base
Execution Type: EXECUTABLE
Required fields:
type: "knowledge-index"title: Display namedataset_id: Target dataset identifiercontent_selector: Content to index
Example:
type: knowledge-index
title: Index Content
dataset_id: "dataset-123"
content_selector:
- '1732007415808'
- processed_text26. assigner (variable-assigner)
Purpose: Assign or transform variables with expressions
Execution Type: EXECUTABLE
Note: Different from variable-aggregator (which merges branches)
Required fields:
type: "assigner"title: Display namevariables: Variable assignment configurations
Example:
type: assigner
title: Transform Variables
variables:
- output_var: transformed_text
expression: "upper(input_text)"Variable References
Variables are referenced using the syntax: {{#node_id.field_name#}}
Examples:
{{#1732007415808.user_input#}}- Reference user_input from start node{{#1733478262179.text#}}- Reference text output from LLM node{{#1733478343153.result#}}- Reference result from code node{{#1733478343153.error_message#}}- Reference error_message from failed code node{{#1733478343153.error_type#}}- Reference error_type from failed code node
Error Handling
Fail Branch Strategy
Code and HTTP request nodes support error_strategy: "fail-branch" which creates an alternative execution path when the node fails.
Source Handles:
"success-branch": Normal execution path (success)"fail-branch": Alternative error path (failure)
Available error variables (in fail-branch path):
error_message: Human-readable error description (string)error_type: Error type/classification (string)
Edge Configuration:
Success edge:
- id: code_node-success-branch-next_node-target
source: code_node_id
target: next_node_id
sourceHandle: success-branch
targetHandle: target
data:
sourceType: code
targetType: llm # or other target node typeFail edge:
- id: code_node-fail-branch-error_handler-target
source: code_node_id
target: error_handler_id
sourceHandle: fail-branch
targetHandle: target
data:
sourceType: code
targetType: llm # error handler node typeRequired convergence: Both branches typically merge into a variable-aggregator before reaching the end node.
Example workflow with error handling:
Start → Code (fail-branch) → [Success → Aggregator] / [Fail → LLM Recovery → Aggregator] → EndDefault Value Strategy
Alternative simpler error handling:
error_strategy: default-value
default_value:
output: "default_text"- Returns predefined default value on error
- Continues on main execution path (no branching)
- Less robust than fail-branch
Retry Strategy
Configure automatic retries for transient failures:
retry_config:
max_retries: 3
retry_interval: 1000 # millisecondsAbort Strategy (Default)
No configuration needed. Workflow stops on error:
# No error_strategy field = abort on error (default)Node Type Summary
All 26+ available node types:
ROOT (Entry Points): 1. start - Standard workflow entry 2. datasource - Data source entry 3. trigger-webhook - Webhook trigger 4. trigger-schedule - Scheduled trigger 5. trigger-plugin - Plugin trigger
EXECUTABLE (Logic Nodes): 6. llm - Language model inference 7. code - Python/JavaScript execution 8. http-request - HTTP API calls 9. knowledge-retrieval - Query knowledge base 10. knowledge-index - Index to knowledge base 11. template-transform - Jinja2 templating 12. tool - External tool integration 13. parameter-extractor - LLM-based extraction 14. document-extractor - Document parsing 15. list-operator - List operations 16. agent - Autonomous agent 17. human-input - Pause for human input 18. assigner - Variable transformation
BRANCH (Conditional): 19. if-else - Conditional routing 20. question-classifier - Question classification
CONTAINER (Loops): 21. iteration - Array iteration 22. loop - Conditional looping
RESPONSE (Output): 23. answer - Mid-workflow output 24. end - Workflow termination
UTILITY: 25. variable-aggregator - Branch merging 26. variable-assigner - Legacy variable assignment (use assigner instead)
Dify Workflow DSL Structure
This document describes the overall structure of a Dify workflow DSL file.
Top-Level Structure
A complete Dify workflow DSL file has the following top-level structure:
kind: app
version: 0.1.4
app:
# App metadata
workflow:
# Workflow definitionApp Section
Defines application-level metadata.
app:
name: Workflow Name
description: Detailed description of the workflow
mode: workflow # Always "workflow" for workflow apps
icon: 🔨 # Emoji icon
icon_background: '#FFEAD5' # Hex color
use_icon_as_answer_icon: falseWorkflow Section
Contains the complete workflow definition with features, graph, and configuration.
Structure
workflow:
features:
# UI and interaction features
environment_variables: []
conversation_variables: []
graph:
nodes: []
edges: []
viewport:
# Canvas view settingsFeatures Configuration
File Upload
features:
file_upload:
enabled: true/false
allowed_file_types:
- image
- document
allowed_file_extensions:
- .JPG
- .PNG
- .PDF
allowed_file_upload_methods:
- local_file
- remote_url
number_limits: 3
fileUploadConfig:
file_size_limit: 15
image_file_size_limit: 5
audio_file_size_limit: 50
video_file_size_limit: 100
batch_count_limit: 5
workflow_file_upload_limit: 10Other Features
features:
opening_statement: 'Welcome message'
suggested_questions: []
suggested_questions_after_answer:
enabled: false
speech_to_text:
enabled: false
text_to_speech:
enabled: false
language: ''
voice: ''
retriever_resource:
enabled: true
sensitive_word_avoidance:
enabled: falseGraph Structure
The graph contains the visual workflow representation.
Nodes Array
Each node has:
nodes:
- id: '1732007415808' # Unique numeric string ID
type: custom # Always "custom"
data:
type: start # Actual node type (start, end, llm, code, etc.)
title: Node Title
desc: Optional description
selected: false
# Type-specific configuration
position:
x: 100.5
y: 200.0
positionAbsolute:
x: 100.5
y: 200.0
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: leftEdges Array
Edges define connections between nodes.
edges:
- id: 1732007415808-source-1733478262179-target
source: '1732007415808' # Source node ID
target: '1733478262179' # Target node ID
sourceHandle: source # Or "fail-branch" for error paths
targetHandle: target
type: custom
data:
sourceType: start # Source node type
targetType: llm # Target node type
isInIteration: false
zIndex: 0
selected: falseEdge naming convention: {source_id}-{handle}-{target_id}-target
Common handles:
source: Normal output flowfail-branch: Error handling flow (for code nodes)true: True branch (for if-else nodes)false: False branch (for if-else nodes)
Viewport
Canvas view settings for the visual editor.
viewport:
x: 190.28
y: 286.09
zoom: 0.33ID Generation
Node IDs are typically Unix timestamps in milliseconds as strings:
- Example:
'1732007415808' - Generate in Python:
str(int(time.time() * 1000)) - Must be unique within the workflow
Complete Minimal Example
kind: app
version: 0.1.4
app:
name: Simple Workflow
description: A basic workflow
mode: workflow
icon: 🚀
icon_background: '#FFEAD5'
use_icon_as_answer_icon: false
workflow:
conversation_variables: []
environment_variables: []
features:
file_upload:
enabled: false
opening_statement: ''
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
graph:
nodes:
- id: '1732007415808'
type: custom
data:
type: start
title: Start
variables:
- variable: user_input
label: User Input
type: paragraph
required: true
max_length: 1000
position:
x: 100
y: 100
positionAbsolute:
x: 100
y: 100
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
- id: '1732007415809'
type: custom
data:
type: end
title: End
outputs:
- variable: result
value_selector:
- '1732007415808'
- user_input
position:
x: 400
y: 100
positionAbsolute:
x: 400
y: 100
width: 244
height: 90
selected: false
sourcePosition: right
targetPosition: left
edges:
- id: 1732007415808-source-1732007415809-target
source: '1732007415808'
sourceHandle: source
target: '1732007415809'
targetHandle: target
type: custom
data:
sourceType: start
targetType: end
isInIteration: false
zIndex: 0
viewport:
x: 0
y: 0
zoom: 1Best Practices
1. Unique IDs: Always generate unique IDs for nodes and ensure edge IDs reference valid node IDs 2. Consistent Positioning: Space nodes adequately (300-400 pixels apart horizontally) 3. Edge Validation: Ensure source and target nodes exist before creating edges 4. Type Matching: Ensure edge data.sourceType and data.targetType match actual node types 5. Variable References: Always use the format {{#node_id.field_name#}} for variable references 6. Error Handling: Use variable-aggregator to merge success/fail branches before end node
#!/usr/bin/env python3
"""
Generate unique node IDs for Dify workflows.
Node IDs are Unix timestamps in milliseconds as strings.
"""
import time
import sys
def generate_node_id() -> str:
"""Generate a unique node ID based on current timestamp."""
return str(int(time.time() * 1000))
def generate_multiple_ids(count: int) -> list:
"""Generate multiple unique node IDs."""
ids = []
for i in range(count):
ids.append(str(int(time.time() * 1000) + i))
# Small delay to ensure uniqueness
if i < count - 1:
time.sleep(0.001)
return ids
def main():
if len(sys.argv) > 1:
try:
count = int(sys.argv[1])
ids = generate_multiple_ids(count)
for node_id in ids:
print(node_id)
except ValueError:
print("Usage: python generate_id.py [count]")
sys.exit(1)
else:
print(generate_node_id())
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Dify Workflow DSL Validator
Validates the structure and syntax of Dify workflow DSL YAML files.
"""
import yaml
import sys
from typing import Dict, List, Any, Tuple
class WorkflowValidator:
def __init__(self):
self.errors = []
self.warnings = []
def validate(self, file_path: str) -> Tuple[bool, List[str], List[str]]:
"""
Validate a Dify workflow DSL file.
Returns:
Tuple of (is_valid, errors, warnings)
"""
self.errors = []
self.warnings = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
except FileNotFoundError:
self.errors.append(f"File not found: {file_path}")
return False, self.errors, self.warnings
except yaml.YAMLError as e:
self.errors.append(f"YAML parsing error: {str(e)}")
return False, self.errors, self.warnings
# Validate top-level structure
self._validate_top_level(data)
# Validate app section
if 'app' in data:
self._validate_app(data['app'])
# Validate workflow section
if 'workflow' in data:
self._validate_workflow(data['workflow'])
is_valid = len(self.errors) == 0
return is_valid, self.errors, self.warnings
def _validate_top_level(self, data: Dict):
"""Validate top-level structure."""
required_fields = ['kind', 'version', 'app', 'workflow']
for field in required_fields:
if field not in data:
self.errors.append(f"Missing required top-level field: {field}")
if data.get('kind') != 'app':
self.errors.append(f"Invalid 'kind' value: expected 'app', got '{data.get('kind')}'")
def _validate_app(self, app: Dict):
"""Validate app section."""
required_fields = ['name', 'mode']
for field in required_fields:
if field not in app:
self.errors.append(f"Missing required app field: {field}")
if app.get('mode') != 'workflow':
self.errors.append(f"Invalid app mode: expected 'workflow', got '{app.get('mode')}'")
def _validate_workflow(self, workflow: Dict):
"""Validate workflow section."""
required_fields = ['graph']
for field in required_fields:
if field not in workflow:
self.errors.append(f"Missing required workflow field: {field}")
return
graph = workflow['graph']
# Validate graph structure
if 'nodes' not in graph:
self.errors.append("Missing 'nodes' in graph")
return
if 'edges' not in graph:
self.errors.append("Missing 'edges' in graph")
return
nodes = graph['nodes']
edges = graph['edges']
# Validate nodes
node_ids = set()
has_start = False
has_end = False
for i, node in enumerate(nodes):
node_id = node.get('id')
if not node_id:
self.errors.append(f"Node at index {i} missing 'id'")
continue
if node_id in node_ids:
self.errors.append(f"Duplicate node ID: {node_id}")
node_ids.add(node_id)
# Check node type
if 'data' not in node:
self.errors.append(f"Node {node_id} missing 'data'")
continue
node_type = node['data'].get('type')
if not node_type:
self.errors.append(f"Node {node_id} missing type")
continue
if node_type == 'start':
has_start = True
self._validate_start_node(node_id, node['data'])
elif node_type == 'end':
has_end = True
self._validate_end_node(node_id, node['data'])
elif node_type == 'llm':
self._validate_llm_node(node_id, node['data'])
elif node_type == 'code':
self._validate_code_node(node_id, node['data'])
elif node_type == 'variable-aggregator':
self._validate_aggregator_node(node_id, node['data'])
elif node_type == 'if-else':
self._validate_ifelse_node(node_id, node['data'])
if not has_start:
self.errors.append("Workflow must have at least one 'start' node")
if not has_end:
self.errors.append("Workflow must have at least one 'end' node")
# Validate edges
for i, edge in enumerate(edges):
source = edge.get('source')
target = edge.get('target')
if not source:
self.errors.append(f"Edge at index {i} missing 'source'")
elif source not in node_ids:
self.errors.append(f"Edge references non-existent source node: {source}")
if not target:
self.errors.append(f"Edge at index {i} missing 'target'")
elif target not in node_ids:
self.errors.append(f"Edge references non-existent target node: {target}")
def _validate_start_node(self, node_id: str, data: Dict):
"""Validate start node."""
if 'variables' not in data:
self.warnings.append(f"Start node {node_id} has no variables defined")
else:
for var in data['variables']:
if 'variable' not in var:
self.errors.append(f"Variable in start node {node_id} missing 'variable' name")
if 'type' not in var:
self.errors.append(f"Variable '{var.get('variable', '?')}' in start node {node_id} missing 'type'")
def _validate_end_node(self, node_id: str, data: Dict):
"""Validate end node."""
if 'outputs' not in data:
self.warnings.append(f"End node {node_id} has no outputs defined")
else:
for output in data['outputs']:
if 'variable' not in output:
self.errors.append(f"Output in end node {node_id} missing 'variable' name")
if 'value_selector' not in output:
self.errors.append(f"Output '{output.get('variable', '?')}' in end node {node_id} missing 'value_selector'")
def _validate_llm_node(self, node_id: str, data: Dict):
"""Validate LLM node."""
if 'model' not in data:
self.errors.append(f"LLM node {node_id} missing 'model' configuration")
else:
model = data['model']
required = ['provider', 'name', 'mode']
for field in required:
if field not in model:
self.errors.append(f"LLM node {node_id} model missing '{field}'")
if 'prompt_template' not in data:
self.errors.append(f"LLM node {node_id} missing 'prompt_template'")
elif not data['prompt_template']:
self.warnings.append(f"LLM node {node_id} has empty prompt_template")
def _validate_code_node(self, node_id: str, data: Dict):
"""Validate code node."""
required = ['code', 'code_language']
for field in required:
if field not in data:
self.errors.append(f"Code node {node_id} missing '{field}'")
if 'code_language' in data and data['code_language'] not in ['python3', 'javascript']:
self.errors.append(f"Code node {node_id} has invalid code_language: {data['code_language']}")
if 'outputs' not in data:
self.warnings.append(f"Code node {node_id} has no outputs defined")
def _validate_aggregator_node(self, node_id: str, data: Dict):
"""Validate variable aggregator node."""
if 'variables' not in data:
self.errors.append(f"Variable aggregator node {node_id} missing 'variables'")
elif not data['variables']:
self.warnings.append(f"Variable aggregator node {node_id} has empty variables list")
if 'output_type' not in data:
self.errors.append(f"Variable aggregator node {node_id} missing 'output_type'")
def _validate_ifelse_node(self, node_id: str, data: Dict):
"""Validate if-else node."""
if 'conditions' not in data:
self.errors.append(f"If-else node {node_id} missing 'conditions'")
elif not data['conditions']:
self.warnings.append(f"If-else node {node_id} has empty conditions list")
def main():
if len(sys.argv) < 2:
print("Usage: python validate_workflow.py <workflow.yml>")
sys.exit(1)
file_path = sys.argv[1]
validator = WorkflowValidator()
is_valid, errors, warnings = validator.validate(file_path)
if warnings:
print("⚠️ Warnings:")
for warning in warnings:
print(f" - {warning}")
print()
if errors:
print("❌ Validation failed with errors:")
for error in errors:
print(f" - {error}")
sys.exit(1)
else:
print("✅ Workflow validation passed!")
sys.exit(0)
if __name__ == '__main__':
main()