Logic blocks

You can use logic blocks to include lightweight, limited Python logic in your agentic workflow to transform data and apply deterministic business rules. Logic blocks are intentionally restricted and do not provide a full Python execution environment.

When the agentic workflow reaches a logic block, it runs your logic in a restricted Python environment then continues to the next downstream node. You can use a logic block to process data transformations, implement custom logic or format messages before they continue to the next step in the agentic workflow. The logic block processing is fast, predictable, and free of any side effects.

The logic block acts as a programmable decision or transformation point in your agentic workflow, giving you greater flexibility and control.

Tip:
  • Logic blocks support only inline logic and simple data transformations. If you need external libraries, API calls, reusable logic, or long‑running computation, use a tool instead of a logic block.

Logic block capabilities

The main capabilities of logic blocks are:

  • Predictable performance: Logic blocks run consistently so that workflows behave the same way each time.

  • Safe operation: Logic blocks run in a controlled environment that reduces security and stability risks.

  • Reliable scaling: Logic blocks work dependably even when workload or complexity increases.

You can use logic blocks for the following:

  • Initializing variables

  • Transforming data

  • Applying business logic

  • Message formatting

  • File processing

For more information about these use cases and examples, see Use cases and examples.

Capabilities excluded from logic blocks

Logic blocks run in a fixed, restricted Python environment. A logic block must not be used for the following:

  • Importing external Python libraries

  • Running arbitrary or long‑running logic

  • Calling external services or APIs

  • Acting as a reusable function or shared library

  • Replacing a Python‑based tool or microservice

These limits are intentional to ensure a secure, predictable, and efficient workflow environment.

Why logic blocks are constrained

Logic blocks run inline as part of a workflow. Using unrestricted Python creates the following risks:

  • Slows the workflow

  • Introduces security risks

  • Workflows are harder to debug and scale

To avoid these issues, logic blocks support fast, predictable logic, while tools can handle heavier processing or external operations.

Supported Python libraries

Within a logic block, you can read variables from different parts of the agentic workflow, apply custom logic, and define output variables for use by subsequent nodes.

Only the following standard libraries are available in logic blocks to ensure a safe and efficient workflow environment:

  • array

  • calendar

  • collections

  • datetime

  • enum

  • json

  • math

  • random

  • re

  • string

  • time

  • yaml

  • zoneinfo

Support for Python functions

Certain Python features are intentionally excluded from logic blocks to mitigate security risks and avoid unpredictable behavior.

The following functions are not available in logic blocks, but you can use the recommended and safer alternatives instead:

  • The eval() function is not supported. Use (my_str == "True") instead of eval(my_str), where my_str is either "True" or "False".

  • For yaml module only safe_load(), safe_load_all(), safe dump() and safe_dump_all() API functions are supported.

  • For string module string.Formatter is not supported. Use string.safe_format() instead of string.Formatter.format().

  • For str function str.format() is not supported. Any xxx.format() or xxx.format_map() API functions are not supported.

  • Some built-in Python functions are not supported, including import, class, and type(). To check types, use safe_type() instead of type().

Tip:
  • The list of available Python modules is fixed and cannot be changed or extended.

  • Certain libraries in the Python modules might not be available.

  • Variables can be referenced by using either ["varname"] or .varname. Using ["varname"] allows for the use of spaces and commas.

Python dictionary objects

Agentic workflow builder uses Python programming practices, therefore flow, self, and parent are treated as Python dictionaries. If you get an exception while reading from or assigning to a self-object, for example: self["input"]["customer"]["discount_rate"], it is likely because one of the containing objects (like "customer") does not exist yet. In that case, you need to first initialize the containing objects.

When a logic block runs, it uses Python dictionary objects to represent the inputs and outputs. You can use expression syntax to reference or set these values as needed. For more information about the syntax, see the Flow expressions section of the IBM wastonsx Orchestrate ADK.

Here is an example agentic workflow dictionary:

# flow represents the inputs and outputs of the outermost flow
flow = {
  "input": {
    "birthday": "1-10-2000",
    "first_name": "John",
    "last_name": "Doe",
    "address": {
      "street": "123 ABC Street",
      "city": "NY",
      "state": "NY",
      "country": "USA"
    },
    "customer_status": "Bronze"
  },
  "output": {
    "current_date": "<current date>",
    "current_datetime": "<current datetime>",
    "age": <number>,
    "address": <str>
  }
}

# self represents the inputs and outputs of the current node
self = {
  "input": {
    "customer_status": "Bronze",
    "price": 1000
  },
  "output": {
    "discount_rate": <number>
  }
}

Data types

The following table lists the data types that are supported in agentic workflows, along with their equivalent Python and JSON types used in expressions and logic blocks.

Flow data types

Python data types

JSON data types

String

Str

String

Integer

Int

Integer

Number

Float

Number

Boolean

Bool

Boolean

Date

Str

String

Object

Dict

Object

File

WxoFile

String

Note:

  • To define an object data type in agentic workflows, you must adhere to the JSON schema standard. For more information, see the JSON Schema documentation.

  • In Python, dates are strings that use the ISO 8601 date format. JSON dates are strings with a “date” format, which uses the ISO 8601 date format, for example:

"startDate": {
 "type": "string",
 "description": "Employee's start date",
 "format": "date"
}

For more information, see Date/time format for code blocks and expressions.

Data type conversion

Data type conversions allow transforming a value from one format to another. The following logic blocks provide Python expression examples of different types of data conversion.

Examples

String conversion

A logic block that converts a string into an integer.
my_str = str("10")

my_int = int(my_str)

test_results += "my_str: " + my_str + " converted to my_int: " + str(my_int) + "\n"
A logic block that converts a string into a number.
my_str = str("1.5")

my_num = float(my_str)

test_results += "my_str: " + my_str + " converted to my_num: " + str(my_num) + "\n"
A logic block that converts a string into a boolean.
my_str = str("False")
my_bool = (my_str == "True")
test_results += "my_str: " + my_str + " converted to my_bool: " + str(my_bool) + "\n"
my_str = str("True")
my_bool = (my_str == "True")
test_results += "my_str: " + my_str + " converted to my_bool: " + str(my_bool) + "\n"
A logic block that converts a string into a date.
Note: Python dates are strings that conform to the ISO 8601 date format: "%Y-%m-%d"
my_str = str("2021-01-01")

my_date_str = my_str

test_results += "my_str: " + my_str + " converted to my_date_str: " + my_date_str + "\n"
A logic block that converts a string into a date by using datetime package.
my_date_date = datetime.datetime.strptime(my_date_str, "%Y-%m-%d").date()

my_date_date = my_date_date + datetime.timedelta(days=1)

my_date_str = my_date_date.strftime("%Y-%m-%d")

test_results += "added one day to my_date_str: " + my_date_str + "\n"

Integer conversion

A logic block that converts an integer into a string.
my_int = int(10)
my_str = str(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_str: " + my_str + "\n"
A logic block that converts an integer into a number.
my_int = int(10)
my_num = float(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_num: " + str(my_num) + "\n"
A logic block that converts an integer into a boolean.
my_int = int(0)
my_bool = bool(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_bool: " + str(my_bool) + "\n"
my_int = int(1)
my_bool = bool(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_bool: " + str(my_bool) + "\n"
A logic block that converts an integer into a date.
my_int = int(739470)
my_date_date = datetime.date.fromordinal(my_int)
my_date_str_isoformat = my_date_date.isoformat()
test_results += "my_int: " + str(my_int) + " converted to my_date_date: " + my_date_str_isoformat + "\n"

Number conversion

A logic block that converts a number into a string.
my_num = float(1.5)
my_str = str(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_str: " + my_str + "\n"
A logic block that converts a number into an integer.
my_num = float(1.5)
my_int = int(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_int: " + str(my_int) + "\n"
my_int = math.ceil(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_int using math.ceil(): " + str(my_int) + "\n"
my_int = math.floor(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_int using math.floor(): " + str(my_int) + "\n"
A logic block that converts a number into a boolean.
Note: In this logic block example, 0 is converted to False, and 1 is converted to True.
my_num = float(0)
my_bool = bool(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_bool: " + str(my_bool) + "\n"
my_num = float(1)
my_bool = bool(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_bool: " + str(my_bool) + "\n"
A logic block that converts a number into a date.
my_num = float(739470)
my_date_date = datetime.date.fromordinal(int(my_num))
my_date_str_isoformat = my_date_date.isoformat()
test_results += "my_num: " + str(my_num) + " converted to my_date_date: " + my_date_str_isoformat + "\n"

Boolean conversion

A logic block that converts a boolean into a string.
my_bool = bool(True)
my_str = str(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_str: " + my_str + "\n"
my_bool = bool(False)
my_str = str(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_str: " + my_str + "\n"
A logic block that converts a boolean into an integer.
my_bool = bool(True)
my_int = int(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_int: " + str(my_int) + "\n"
my_bool = bool(False)
my_int = int(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_int: " + str(my_int) + "\n"
A logic block that converts a boolean into a number.
my_bool = bool(True)
my_num = float(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_num: " + str(my_num) + "\n"
my_bool = bool(False)
my_num = float(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_num: " + str(my_num) + "\n"

Date conversions

A logic block that converts a date into a string.
Note: For more information about date format codes, see [Python `strftime` and `strptime` format codes].
my_date_date = datetime.date.today()
my_date_str_isoformat = my_date_date.isoformat()
my_date_str = my_date_date.strftime("%a %d %B %Y")
test_results += "my_date_date: " + my_date_str_isoformat + " converted to my_date_str: " + my_date_str + "\n"
 
A logic block that converts a date into an integer.
my_date_date = datetime.date.today()
my_date_str_isoformat = my_date_date.isoformat()
my_int = int(my_date_date.toordinal())
test_results += "my_date_date: " + my_date_str_isoformat + " converted to my_int: " + str(my_int) + "\n"
 
A logic block that converts a date into a number.
my_date_date = datetime.date.today()
my_date_str_isoformat = my_date_date.isoformat()
my_num = float(my_date_date.toordinal())
test_results += "my_date_date: " + my_date_str_isoformat + " converted to my_num: " + str(my_num) + "\n"
self.output.test_results = test_results
 

Use cases and examples

You can use the logic block node to customize how your agentic workflow handles data. It is useful when standard nodes do not meet your specific needs. The following are some common use cases.

Message formatting

You can format or restructure messages as needed, while also moving information to the next node. The following logic block example shows a practical application of message formatting.

Examples

A logic block that formats address input and displays it as a single string separated by commas.
# Format address
addr = flow["input"]["address"]
formatted_address = f"{addr['street']}, {addr['city']}, {addr['state']}, {addr['country']}"
self["output"]["address"] = formatted_address
 

Flexible integration

You can place the logic block node anywhere in the agentic workflow to integrate with other nodes and tools. The following logic block sample shows a practical application of flexible integration.

Examples

A logic block that defines a variable that is called todays_date as a Date data type and initializes todays_date and sets the value to the current date.
today = datetime.date.today()
self.output.todays_date = today.isoformat()

A logic block that initializes a set of output variables by using primitive data types: string, integer, number, and boolean.

The output variables are:

  • name : String
  • age : Integer
  • salary : Number
  • isActive : Boolean

In the logic block, each variable is initialized with a sample value.

self.output.name = "John Doe"
self.output.age = 37
self.output.salary = 12345.67
self.output.isActive = True
 

Custom data transformation

You can write Python logic to transform, filter, initialize variables, or enhance existing data as it passes through the agentic workflow. The following logic block examples show a practical application of custom data transformation.

Examples

A logic block that parses a JSON string containing address fields into a Python dictionary.
address_json = '''

{
  "street": "123 ABC Street",
  "city": "NY",
  "state": "NY",
  "country": "USA"
}

'''

address = json.loads(address_json)
A logic block that assigns the state value from the address dictionary to the input address in the agentic workflow.
flow["input"]["address"]["state"] = address["state"]
A logic block sets and initializes an output variable that is called employee as an object data type.

{
    "employee": {
    "type": "object",
    "description": "",
    "properties": {
      "id": {
        "type": "integer",
        "description": "Unique identifier for the employee"
      },
      "firstName": {
        "type": "string",
        "description": "Employee's first name"
      },
      "lastName": {
        "type": "string",
        "description": "Employee's last name"
      },
      "email": {
        "type": "string",
        "description": "Employee's email address"
      },
      "department": {
        "type": "string",
        "description": "Employee's department"
      },
      "salary": {
        "type": "number",
        "description": "Employee's salary"
      },
      "isActive": {
        "type": "boolean",
        "description": "Whether the employee is currently active"
      },
      "startDate": {
        "type": "string",
        "format": "date",
        "description": "Employee's start date"
      },
      "badges": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "Employee's badges"
      },
      "address": {
        "type": "object",
        "description": "",
        "properties": {
          "street": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "zipCode": {
            "type": "string"
          }
        },
        "default": ""
      }
    }
    }
}

 

Business logic implementation

You can apply conditional logic, compute calculations and decisions, or run custom operations as needed in the agentic workflow. The following logic block examples show a practical application of business logic implementation.

Examples

A logic block that calculates and displays the user's age based on the date of birth input.
# Get today's date
today = datetime.date.today()
# Get the user's birthday from the output of the previous user activity
birthday_str = flow["User activity 1"]["Ask for date of birth"].output.value
# Convert the user's birthday into a date object
birthday = datetime.datetime.strptime(birthday_str, "%Y-%m-%d").date()
# Calculate the user's age
age = today.year - birthday.year - ((today.month, today.day) < (birthday.month, birthday.day))
# Return the user's age
self.output.age = age
 
A logic block that checks the product type and age to determine whether to accept payment.
product = flow.input.product

age = flow.input.age

if product == "Alcohol" and age < 18:
    self.output.accept_payment = False
elif product == "Fireworks" and age < 18:
    self.output.accept_payment = False
else:
    self.output.accept_payment = True
A logic block that displays a discount rate based on input variables price and customer_status.
# Ensure customer_status and price are available

status = flow["input"].get("customer_status")
price = flow["input"].get("price", 0)

discount_rate = 0

if status == "Bronze":
    if price < 100:
        discount_rate = 0
    elif price < 500:
        discount_rate = 0.01

    elif price < 900:
        discount_rate = 0.02
    else:
        discount_rate = 0.03
elif status == "Silver":
    if price < 100:
        discount_rate = 0.02
    elif price < 500:
        discount_rate = 0.04
    elif price < 900:
        discount_rate = 0.05
    else:
        discount_rate = 0.07
elif status == "Gold":
    if price < 100:
        discount_rate = 0
    elif price < 500:
        discount_rate = 0.05
    elif price < 900:
        discount_rate = 0.07
    else:
        discount_rate = 0.10

self["output"]["discount_rate"] = discount_rate
 
A logic block that formats the address input into a string by using if-then logic.
# Format address using if-then logic

addr = flow["input"]["address"]
formatted_address = addr["street"]

if addr.get("city"):
    formatted_address += f", {addr['city']}"
if addr.get("state"):
    formatted_address += f", {addr['state']}"
if addr.get("country"):
    formatted_address += f", {addr['country']}"

self["output"]["address"] = formatted_address
A logic block that calculates the date three days earlier from the current date, and stores it in the variable thee_days_ago.
today = datetime.date.today()

three_days_ago = today - datetime.timedelta(days=3)

self.output.three_days_ago = three_days_ago.isoformat()

File processing

You can use the logic block node to manage file operations such as handling single or multiple file uploads, extracting or transforming file contents, and generating files for download. The following example demonstrates a practical file processing scenario.

Available functions for file processing

You can use system file functions or WxoFile instance methods to run file operations.

System File Functions

Following are the system.file methods to work with files:

  • system.file.get_name(file) – Returns the file name
  • system.file.get_type(file) – Returns the file type
  • system.file.get_size(file) – Returns the file size
  • system.file.get_content(file) – Returns the file content as bytes

WxoFile Instance Methods

Following are the instance methods available on a WxoFile object:

  • file.get_name() – Returns the file name
  • file.get_type() – Returns the file type
  • file.get_size() – Returns the file size
  • file.get_content() – Returns the file content as bytes

Following are practical examples that demonstrate how to access files from flow inputs, user activity forms and user activity file upload and retrieve their URL, name, type, and size by using both system.file functions and WxoFile instance methods.

A logic block that accesses a file from the flow input and retrieves its URL, name, and size by using system.file method.

input_file = flow.input.externalFile
self.output.file_name = system.file.get_name(input_file)
self.output.file_type = system.file.get_type(input_file)
self.output.file_size = system.file.get_size(input_file)
self.output.file_content = system.file.get_content(input_file)
 
A logic block that accesses a file from the flow input and retrieves its URL, name, and size by using WxoFile instance method.

input_file = flow.input.externalFile
self.output.file_name = input_file.get_name()
self.output.file_type = input_file.get_type()
self.output.file_size = input_file.get_size()
self.output.file_content = input_file.get_content()
 
A logic block that accesses a single file from a user activity form (with ‘Allow Multiple Files’ disabled) and retrieves its URL, name, and size by using the system.file method.

form_single_file = flow["User activity 1"]["Form 1"].output["single file upload"]
self.output.file_name = system.file.get_name(form_single_file)
self.output.file_type = system.file.get_type(form_single_file)
self.output.file_size = system.file.get_size(form_single_file)
self.output.file_content = system.file.get_content(form_single_file)
 
A logic block that accesses a single file from a user activity form (with ‘Allow Multiple Files’ disabled) and retrieves its URL, name, and size by using WxoFile instance method.

form_single_file = flow["User activity 1"]["Form 1"].output["single file upload"]
self.output.file_name = form_single_file.get_name()
self.output.file_type = form_single_file.get_type()
self.output.file_size = form_single_file.get_size()
self.output.file_content = form_single_file.get_content()
 
A logic block that accesses a multiple file from a user activity form (with ‘Allow Multiple Files’ enabled) and retrieves its URL, name, and size by using the system.file method.

form_multi_file = flow["User activity 1"]["Form 1"].output["Multi file upload"]
self.output.file_count = len(form_multi_file)
self.output.first_file_name = system.file.get_name(form_multi_file[0])
self.output.first_file_type = system.file.get_type(form_multi_file[0])
self.output.first_file_size = system.file.get_size(form_multi_file[0])
self.output.first_file_content = system.file.get_content(form_multi_file[0])
 
A logic block that accesses a multiple file from a user activity form (with ‘Allow Multiple Files’ enabled) and retrieves its URL, name, and size by using WxoFile instance method.

form_multi_file = flow["User activity 1"]["Form 1"].output["Multi file upload"]
self.output.file_count = len(form_multi_file)
self.output.first_file_name = form_multi_file[0].get_name()
self.output.first_file_type = form_multi_file[0].get_type()
self.output.first_file_size = form_multi_file[0].get_size()
self.output.first_file_content = form_multi_file[0].get_content()
 
A logic block that accesses a single nonform file from user activity and retrieves its URL, name, and size by using the system.file method.

normal_single_file = flow["User activity 1"]["File upload 1"].output.value
self.output.file_name = system.file.get_name(normal_single_file)
self.output.file_type = system.file.get_type(normal_single_file)
self.output.file_size = system.file.get_size(normal_single_file)
self.output.file_content = system.file.get_content(normal_single_file)
 
A logic block that accesses a single nonform file from a user activity and retrieves its URL, name, and size by using WxoFile instance method.

normal_single_file = flow["User activity 1"]["File upload 1"].output.value
self.output.file_name = normal_single_file.get_name()
self.output.file_type = normal_single_file.get_type()
self.output.file_size = normal_single_file.get_size()
self.output.file_content = normal_single_file.get_content()
 
Note: Note: Single file uploads must be accessed directly without using an index. Multiple file uploads require indexing (example: [0], [1]) to reference each file individually.

System context variables

System context variables let you store and reuse values across a workflow. They act as shared data points, and the system context variables persists for the entire duration of the workflow. If a context variable is updated at the beginning of the workflow such as in a logic block, the value remains available throughout the entire workflow run.

The following methods are available:

get_variable method

The get_variable method retrieves a system context variable by name.

  • variable_name: Specifies the variable key.

system.context.get_variable("variable_name")
 

set_variable method

The set_variable method stores a value in the system by using the variable key.

  • variable_name: Specifies the variable key.

  • variable_value: Specifies the value to store.

system.context.set_variable('variable_name', "variable_value")
 

Predefined system context variables

Predefined variables are always available in the workflow. You do not need to set them manually by using the set_variable method. Use the get_variable method to access them at any point in the workflow.

The predefined variables are:

  • wxo_email_id

  • wxo_tenant_id

  • wxo_user_name