Skip to main content

How Custom Functions Work

Custom functions provide flexibility by allowing developers to define specific logic for step execution. They can be used to preprocess 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.
While defining a Step, you can specify a custom function as an executor. This function should accept a StepInput object and return a StepOutput object.
Just make sure to follow this structure and return the output as a StepOutput object.
More Examples:

Run a Workflow non-blocking (in the background)

You can run a workflow as a non-blocking task by passing background=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 returning StepOutput(stop=True). Early Stop Workflows
More Examples:

Access Multiple Previous Steps Output

Advanced workflows often need to access data from multiple previous steps, not just the immediate previous step. The StepInput object provides powerful methods to access any previous step’s output by name or get all previous content.
Key Methods:
  • step_input.get_step_content("step_name") - Get content from specific step by name
  • step_input.get_all_previous_content() - Get all previous step content combined
  • step_input.message - Access the original workflow input message
  • step_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.
More Examples:

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 the WorkflowRunResponse 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 database
  • events_to_skip=[]: Filter out specific event types to reduce storage and noise
Access all stored events via workflow.run_response.events Available Events to Skip:
When to use:
  • 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_started to focus on results
Example Use Cases:
More Examples:

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.
Access Pattern: step_input.additional_data provides dictionary access to all additional data
More Examples:

Structured Inputs

Use Pydantic models for type-safe inputs:
More Examples:

Structured IO at Each Step Level

Workflows features a powerful type-safe data flow system where each step in your workflow can:
  1. Receive structured input (Pydantic models, lists, dicts, or raw strings)
  2. Produce structured output (validated Pydantic models)
  3. Maintain type safety throughout the entire workflow execution

How Data Flows Between Steps

  1. Input Handling:
    • The first step receives the workflow’s input message
    • Subsequent steps receive the previous step’s structured output
  2. Output Processing:
    • Each Agent processes the input using its response_model
    • The output is automatically validated against the model

Structured Data Transformation in Custom Functions

Custom functions in workflows can access the structured output of previous steps via step_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:
More Examples:

Media Input

Workflows seamlessly handle media artifacts (images, videos, audio) throughout the execution pipeline. Media can be provided as input for Workflow.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.
Similarly, you can pass Video and Audio as input. More Examples: