How Custom Functions Work
Custom functions provide flexibility by allowing developers to define specific logic for step execution. They can be used topreprocess inputs, call agents, and postprocess outputs.
- executor: Step can be defined with a custom execution function that handles the step logic.
- Integration with Agents and Teams: Custom functions can interact with agents and teams, leveraging their capabilities.
Step, you can specify a custom function as an executor. This function should accept a StepInput object and return a StepOutput object.
StepOutput object.
Run a Workflow non-blocking (in the background)
You can run a workflow as a non-blocking task by passingbackground=True to Workflow.arun(). This will return a WorkflowRunResponse object with a run_id that you can use to poll for the result of the workflow until it is completed.
This feature is only available for async workflows using
.arun().
For long-running workflows, you can poll for the result using result = workflow.get_run(run_id) which returns the updated WorkflowRunResponse.Use .has_completed() to check if the workflow has finished executing. This is particularly useful for workflows that involve time-consuming operations like large-scale data processing, multi-step research tasks, or batch operations that you don’t want to block your main application thread.Early Stopping
Workflows can be terminated early when certain conditions are met, preventing unnecessary processing and ensuring safety gates work properly. Any step can trigger early termination by returningStepOutput(stop=True).

Access Multiple Previous Steps Output
Advanced workflows often need to access data from multiple previous steps, not just the immediate previous step. TheStepInput object provides powerful methods to access any previous step’s output by name or get all previous content.
step_input.get_step_content("step_name")- Get content from specific step by namestep_input.get_all_previous_content()- Get all previous step content combinedstep_input.message- Access the original workflow input messagestep_input.previous_step_content- Get content from immediate previous step
In case of
Parallel step, when you do step_input.get_step_content("parallel_step_name"), it will return a dict with each key as individual_step_name for all the outputs from the steps defined in parallel.
Example:parallel_step_output will be a dict with each key as individual_step_name for all the outputs from the steps defined in parallel.Store Events
Workflows can automatically store all events for later analysis, debugging, or audit purposes. You can also filter out specific event types to reduce noise and storage overhead. You can access these events on theWorkflowRunResponse and in the runs column in your Workflow's Session DB in your configured storage backend (SQLite, PostgreSQL, etc.).
store_events=True: Automatically stores all workflow events in the databaseevents_to_skip=[]: Filter out specific event types to reduce storage and noise
workflow.run_response.events
Available Events to Skip:
- Debugging: Store all events to analyze workflow execution flow
- Audit Trails: Keep records of all workflow activities for compliance
- Performance Analysis: Analyze timing and execution patterns
- Error Investigation: Review event sequences leading to failures
- Noise Reduction: Skip verbose events like
step_startedto focus on results
Additional Data
When to use: When you need to pass metadata, configuration, or contextual information to specific steps without it being part of the main workflow message flow.- Separation of Concerns: Keep workflow logic separate from metadata
- Step-Specific Context: Access additional information in custom functions
- Clean Message Flow: Main message stays focused on content
- Flexible Configuration: Pass user info, priorities, settings, etc.
step_input.additional_data provides dictionary access to all additional data
Structured Inputs
Use Pydantic models for type-safe inputs:Structured IO at Each Step Level
Workflows features a powerful type-safe data flow system where each step in your workflow can:- Receive structured input (Pydantic models, lists, dicts, or raw strings)
- Produce structured output (validated Pydantic models)
- Maintain type safety throughout the entire workflow execution
How Data Flows Between Steps
-
Input Handling:
- The first step receives the workflow’s input message
- Subsequent steps receive the previous step’s structured output
-
Output Processing:
- Each Agent processes the input using its
response_model - The output is automatically validated against the model
- Each Agent processes the input using its
Structured Data Transformation in Custom Functions
Custom functions in workflows can access the structured output of previous steps viastep_input.previous_step_content, which preserves the original Pydantic model type (e.g., ResearchFindings). To transform data:
- Type-Check Inputs: Use
isinstance(step_input.previous_step_content, ModelName)to verify the input structure. - Modify Data: Extract fields (e.g.,
step_input.previous_step_content.topic), process them, and construct a new Pydantic model (e.g., AnalysisReport). - Return Typed Output: Wrap the new model in
StepOutput(content=new_model). This ensures type safety for downstream steps. Example:
Media Input
Workflows seamlessly handle media artifacts (images, videos, audio) throughout the execution pipeline. Media can be provided as input forWorkflow.run() and Workflow.print_response() and is passed through to individual steps (whether Agent, Team or Custom Function).
During execution, media artifacts accumulate across steps - each step receives shared media from previous steps and can
produce additional media outputs. The Step class handles automatic conversion between artifact formats, ensuring compatibility between
workflow components and agent/team executors. All media artifacts are preserved in StepOutput and propagated to
subsequent steps, creating a comprehensive flow where the final WorkflowRunResponse contains all accumulated
images, videos, and audio from the entire execution chain.
Here’s an example of how to pass image as input:
If you are using
Workflow.run(), you need to use WorkflowRunResponse to access the images, videos, and audio.Video and Audio as input.
More Examples: