Workflow and Agent Composition#
Dify allows Workflow-type apps to be published as reusable tools that other workflows or agents can invoke. This is implemented through the ToolProviderType.WORKFLOW provider family, which lives under api/core/tools/workflow_as_tool/. The two core files are:
tool.pyβ theWorkflowToolclass (the actual callable)provider.pyβ theWorkflowToolProviderControllerthat builds and vendsWorkflowToolinstances
Within Agent V2 nodes, the WorkflowAgentDifyToolsBuilder in api/core/workflow/nodes/agent_v2/dify_tools_builder.py is responsible for resolving all configured Dify toolsβincluding WORKFLOW providersβinto the layer configs the Agent backend consumes.
WorkflowTool: How Invocation Works#
WorkflowTool extends Tool and reports ToolProviderType.WORKFLOW . Its _invoke method:
- Resolves the target
AppandWorkflowrecords from the database . - Transforms tool parameters (including file handling) via
_transform_args. - Delegates execution to
WorkflowAppGenerator.generate()withstreaming=Falseandcall_depth + 1. This is what enforces the recursive depth limit. - Raises
ToolInvokeErroron workflow errors; otherwise yields variable, file, text, and JSON messages from the workflow outputs .
Key constraint: The tool explicitly sets pause_state_config=None, meaning workflow-level pausing mechanisms (e.g., HumanInput nodes) are not supported when a workflow runs as a tool . This is separately enforced at publish time by WorkflowToolConfigurationUtils.ensure_no_human_input_nodes.
The tool also carries trace context: set_parent_trace_context and set_trace_session_id attach outer workflow run identifiers for observability without exposing them as LLM-visible parameters .
WorkflowToolProviderController: Building the Tool#
WorkflowToolProviderController is constructed from a WorkflowToolProvider DB record via from_db(). The controller:
- Maps workflow input variable types to
ToolParametertypes viaVARIABLE_TO_PARAMETER_TYPE_MAPPING. Supported types includeTEXT_INPUT,PARAGRAPH,SELECT,NUMBER,CHECKBOX,FILE,FILE_LIST, andJSON_OBJECT. - Derives an
output_schemafrom the workflow's End node outputs . Reserved output keys (json,text,files) are excluded from the schema. - Initializes
WorkflowToolwithworkflow_call_depth=0, indicating this is the outermost invocation layer .
The WorkflowToolProvider model (in api/models/tools.py) stores: app_id, version (the published workflow version), name, label, icon, description, parameter_configuration, tenant_id, and user_id.
ToolManager integration: ToolManager.get_tool_runtime handles ToolProviderType.WORKFLOW by querying WorkflowToolProvider, converting it via ToolTransformService.workflow_provider_to_controller, and forking a WorkflowTool with an empty credentials dict . The ToolProviderType.APP case (for Agent-type app invocation) raises NotImplementedError .
Workflows as Tools in Agent V2 Nodes#
When an Agent V2 node runs, WorkflowAgentDifyToolsBuilder.build_layers() resolves all enabled Dify tools from AgentSoulToolsConfig into two layer configs for the Agent backend:
plugin_tools(DifyPluginToolsLayerConfig) β forPLUGINprovider types (routed to the Plugin Daemon)core_tools(DifyCoreToolsLayerConfig) β forBUILT_IN,API,WORKFLOW, andMCPproviders
WORKFLOW tools are routed to core_tools , which means tool invocations are delegated to POST /inner/api/agent/tools/invoke inside the API process rather than the plugin daemon. This keeps workflow credentials and state within the API boundary.
Provider-level entries (where tool_name is omitted) are expanded to individual tool entries via _expand_provider_entries , which calls WorkflowToolProviderController.from_db to enumerate declared tool names.
The builder's _fetch_tool_runtime wraps ToolManager.get_agent_tool_runtime and maps ToolProviderNotFoundError and ToolProviderCredentialValidationError to typed WorkflowAgentDifyToolsBuildError codes (agent_tool_declaration_not_found, agent_tool_credential_invalid) .
Tool configuration in Agent Soul uses AgentSoulDifyToolConfig, where builtin/api/workflow/mcp providers route through dify.core.tools .
Architectural Constraints and Known Limitations#
Only published, versioned workflows can be used as tools. WorkflowTool._get_workflow rejects draft workflows . Attempting to invoke an unpublished workflow raises ValueError: "workflow not found or not published".
Agent-type apps cannot be invoked as tools. ToolProviderType.APP is unimplemented in ToolManager.get_tool_runtime . A feature request to allow private, unpublished Agent apps to be called from within a workflow as internal sub-modules remains open . Note: this limitation applies to the legacy APP provider type; Agent V2 nodes have their own separate execution path via WorkflowAgentDifyToolsBuilder and the Agent backend.
HumanInput nodes are blocked. Publishing a workflow as a tool will fail with WorkflowToolHumanInputNotSupportedError if the workflow contains HumanInput nodes. Even if bypassed, WorkflowTool._invoke passes pause_state_config=None, dropping any pause semantics at runtime .
No referential integrity on deletion. If a workflow published as a tool is deleted, other workflows referencing it will fail silently at runtime. An open issue tracks adding strict reference validation before deletion is allowed .
Recursive depth tracking. WorkflowTool increments call_depth by 1 on each invocation . The maximum call depth is enforced by WorkflowAppGenerator.