Skip to main content
Use a user activity node to define interactive steps in your agentic workflow where users provide or receive information in one of two ways:
  • In a multi-turn conversation, where the user provides or receives one piece of data at a time
  • Through a form, which prompts the user for several pieces of data in a single conversational turn
To add a user activity node to your agentic workflow, start by creating a userflow() object.

Multi-turn conversations

To collect or deliver one piece of data at a time, call field() on the userflow() object. This method accepts the following input parameters:
ParameterTypeRequiredDescription
namestringYesUnique identifier for the node.
kindUserFieldKindYesType of user activity. Supported types: Text, Date, DateTime, Time, Number, File, Boolean, Object, Choice.
display_namestringNoDisplay name for the node.
descriptionstringNoDescription of the node.
directionstringYesIndicates whether the node is for input or output.
defaultAnyNoDefault value for the node.
optionUserFieldOptionNoList of predefined options with it labels and values.
is_listboolNoIndicates whether the node accepts multiple values.
minAnyNoMinimum value or constraint.
maxAnyNoMaximum value or constraint.
input_mapDataMapNoDefine input mappings using a structured collection of Assignment objects.
customdictionaryNoDictionary for additional metadata or configuration.
Here is an example that shows how to use field():
Python
from typing import List
from pydantic import BaseModel, Field
from ibm_watsonx_orchestrate.flow_builder.flows import (
    Flow, flow, UserNode, START, END
)
from ibm_watsonx_orchestrate.flow_builder.types import Assignment, UserFieldKind
from ibm_watsonx_orchestrate.flow_builder.data_map import DataMap

class Name(BaseModel):
    """
    This class represents a person's name.

    Attributes:
        name (str): The person's first name.
    """
    first_name: str = Field(default="John Doe", description="First name")

@flow(
    name ="user_flow_example",
    display_name="user_flow_example",
    description="Example user flow.",
    input_schema=Name,
)
def build_user_flow(aflow: Flow = None) -> Flow:
    # user_flow which is a subflow to be added to the aflow
    user_flow = aflow.userflow()

    # add file upload
    user_node1 = user_flow.field(direction="input",name="upload", display_name="File upload 1",  kind=UserFieldKind.File)

    # add file download
    data_map = DataMap()
    data_map.add(Assignment(target_variable="self.input.value",value_expression="flow[\"userflow_1\"][\"File upload 1\"].output.value"))
    user_node2 = user_flow.field(direction="output",name="download", display_name="Download file",  kind=UserFieldKind.File, input_map=data_map)

    # add a Display user text field
    user_node3 = user_flow.field(direction="output",name="display_first_name", display_name="Display first name", kind=UserFieldKind.Text, text="Display of first name is {flow.input.first_name}")

    # add a text input field
    user_node4 = user_flow.field(direction="input",name="last_name", display_name="Last name",  kind=UserFieldKind.Text)
    
    # add a Number input field
    user_node5 = user_flow.field(direction="input",name="age", display_name="Age",  kind=UserFieldKind.Number)

    # create a data map to build an array to be assigned to a user field of kind List
    data_map = DataMap()
    data_map.add(Assignment(target_variable="self.input.value",value_expression="[\"Alice\", \"Bob\", \"Charlie\", \"Diana\", \"Ethan\", \"Fiona\", \"George\"]"))
    user_node6 = user_flow.field(direction="output",name="Friends", display_name="List of friends", kind=UserFieldKind.List, input_map=data_map)

    # A user flow edges
    user_flow.edge(START, user_node1)
    user_flow.edge(user_node1, user_node2)
    user_flow.edge(user_node2, user_node3)
    user_flow.edge(user_node3, user_node4)
    user_flow.edge(user_node4, user_node5)
    user_flow.edge(user_node5, user_node6)
    user_flow.edge(user_node5, END)
    
    # add the user flow to the flow sequence to create the flow edges
    aflow.sequence(START, user_flow, END)

    return aflow

Forms

To prompt for multiple pieces of data in a single turn, instantiate the form() object to create a form node. This method accepts the following input parameters:
ParameterTypeRequiredDescription
namestringYesThe internal name of the form.
display_namestringNoThe display name shown on the form.
instructionsstringNoInstructions text for the form.
submit_button_labelstringNoThe label for the submit button. Defaults to Submit if not set.
cancel_button_labelstringNoThe label for the cancel button. If set to None, hides the button.
After instantiating the form object, add fields to it. Each field type requires its own configuration. Supported field types include:
Creates a text input field. Configure it as a single-line input or a multi-line text area.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
requiredboolNoWhether the field is required. Defaults to False.
single_lineboolNoWhether the field uses a single line. If False, creates a multi-line text area. Defaults to True.
placeholder_textstringNoPlaceholder text for the field.
help_textstringNoHelp text for the field.
defaultanyNoDefault value for the field, passed as DataMap.
Creates a boolean input field. Render it as a checkbox or radio buttons.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
single_checkboxboolNoWhether to display as a single checkbox. If False, displays as radio buttons. Defaults to True.
defaultanyNoDefault value for the field, passed as input_map.
true_labelstringNoLabel for the true option. Defaults to True.
false_labelstringNoLabel for the false option. Defaults to False.
Creates a date range input field with start and end date pickers.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
requiredboolNoWhether the field is required. Defaults to False.
start_date_labelstringNoLabel for the start date field.
end_date_labelstringNoLabel for the end date field.
default_startanyNoDefault value for the start date, passed as DataMap.
default_endanyNoDefault value for the end date, passed as DataMap.
Creates a date input field.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
requiredboolNoWhether the field is required. Defaults to False.
defaultanyNoDefault value for the field, passed as DataMap.
Creates a number input field.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
requiredboolNoWhether the field is required. Defaults to False.
is_integerboolNoWhether the field accepts only integers. If False, accepts decimal numbers. Defaults to True.
help_textstringNoHelp text for the field.
defaultanyNoDefault value for the field, passed as DataMap.
minimumanyNoMinimum allowed value, passed as DataMap.
maximumanyNoMaximum allowed value, passed as DataMap.
Creates a file upload field.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
instructionsstringNoInstructions for the file upload.
requiredboolNoWhether the field is required. Defaults to False.
allow_multiple_filesboolNoWhether multiple files can be uploaded. Defaults to False.
file_max_sizeintNoMaximum file size in MB. Defaults to 10.
Creates a message output field to display static text.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
messagestringNoThe message text to display.
Creates a field output to display dynamic values.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
valueanyNoThe value to display, passed as DataMap.
Creates a list output field to display tabular data.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
choicesanyNoThe list of items to display, passed in a DataMap.
columnsdict[string, str]NoMapping of source property names to table column labels. Only those columns appear if present.
Creates a file download field.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
valueanyNoThe file to download, passed as DataMap.
Creates a single-choice input field. Display options as a dropdown or as radio buttons.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
requiredboolNoWhether the field is required. Defaults to False.
choicesanyNoThe list of available choices, passed as DataMap.
show_as_dropdownboolNoWhether to display options as a dropdown. If False, display as radio buttons. Defaults to True.
dropdown_item_columnstringNoColumn name used for display text in dropdown.
placeholder_textstringNoPlaceholder text for the dropdown.
defaultanyNoDefault selected value, passed as DataMap.
columnsdict[string, str]NoMapping of source property names to display labels for complex choice objects.
Creates a multi-choice input field. Display options as a multi-select dropdown or as checkboxes.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
requiredboolNoWhether the field is required. Defaults to False.
choicesanyNoThe list of available choices, passed as DataMap.
show_as_dropdownboolNoWhether to display options as a dropdown. If False, display as checkboxes. Defaults to True.
dropdown_item_columnstringNoColumn name used for display text in dropdown.
placeholder_textstringNoPlaceholder text for the dropdown.
Creates a multi-choice input field. Display options as a multi-select dropdown or as checkboxes.
ParameterTypeRequiredDescription
namestringYesThe internal name of the field.
labelstringNoDisplay label for the field.
choicesanyNoThe list of available choices, passed as DataMap.
columnsdict[string, str]NoWhether to display options as a dropdown. If False, display as checkboxes. Defaults to True.
Here is an example that shows how to use form():
Python
from typing import List
from pydantic import BaseModel, Field
from ibm_watsonx_orchestrate.flow_builder.flows import (
    Flow, flow, UserNode, START, END
)
from ibm_watsonx_orchestrate.flow_builder.types import Assignment, UserFieldKind
from ibm_watsonx_orchestrate.flow_builder.data_map import DataMap

class Name(BaseModel):
    """
    This datatype represents a person's name.

    Attributes:
        name (str): The person's first name.
    """
    first_name: str = Field(default="John", description="First name")
    last_name: str = Field(default="Doe", description="Last name")

class Book(BaseModel):
    """
    This datatype represents a book.

    Attributes:
        title (str): The book's title.
        author (str): The book's author.
    """
    title: str = Field(default="Shaken", description="Book title")
    author: str = Field(default="John Grisham", description="Author")

class StringListNames(BaseModel):
    listOfNames: List[Name] = Field(
        default=[{"John", "Doe"}, {"Jane", "Doe"}, {"Jean", "Doe"}],
        description="A list of Name objects with first and last names."
    )

class MyDate(BaseModel):
    """
    This datatype represents date information.

    Attributes:
        dateStart (str): The start date.
        dateEnd (str): The end date.
        dateEvent (str): The event date.
    """
    dateStart: str = Field(default="2023-01-01", description="Start date")
    dateEnd: str = Field(default="2023-12-31", description="End date")
    dateEvent: str = Field(default="2023-06-15", description="Event date")

class FlowInput(BaseModel):
    salutations: List[str] = Field(
        default=["Mr", "Mrs"],
        description="A list of string salutations."
    )
    listOfLanguages: List[str] = Field(
        default=["java", "python", "typescript"],
        description="A list of languages."
    )
    salary_expectation: int = Field(
        default=200000,
        description="Expected salary as an integer number."
    )

    friends: StringListNames = Field(
        default=StringListNames(
            listOfNames=[
                Name(first_name="John", last_name="Doe"),
                Name(first_name="Jane", last_name="Doe"),
                Name(first_name="Jean", last_name="Doe")
            ]
        ),
        description="A list of friends with their names."
    )
    books: List[Book] = Field(
        default=[Book()],
        description="A list of books."
    )

    event_date: MyDate = Field(
        default=MyDate(),
        description="The event date"
    ),

    listOfFruits: List[str] = Field(
        default=["apple", "oranges", "bananas"],
        description="A list of fruits."
    )

@flow(
    name ="user_flow_application_form",
    display_name="Application form",
    description="Creates a sample application form.",
    input_schema=FlowInput,
)

def build_user_form(aflow: Flow = None) -> Flow:

    user_flow = aflow.userflow()
    user_flow.spec.display_name= "Application"

    user_node_with_form = user_flow.form(name="ApplicationForm", display_name="Application")
    
    data_map = DataMap()
    data_map.add(Assignment(target_variable="self.input.choices", value_expression="flow.input.salutations"))
 
    #Single-choice: Salutation
    user_node_with_form.single_choice_input_field(name="salutation", label="Salutation", required=True, choices=data_map, 
                                                  show_as_dropdown=True, placeholder_text="Please enter your title")
   
    #Boolean: Married
    user_node_with_form.boolean_input_field(name="married", label="Married", single_checkbox = True, true_label="Married", false_label="Not married")

    #Text: Last Name
    user_node_with_form.text_input_field(name="lastName", label="Last name", required=True, placeholder_text="Enter your name here", help_text="Enter name")

    #Number: Age
    user_node_with_form.number_input_field(name="age", label="Age", required=True, help_text="Enter your age")

    data_map_salary = DataMap()
    data_map_salary.add(Assignment(target_variable="self.input.default", value_expression="flow.input.salary_expectation"))
    
    #Number: Desired Salary
    user_node_with_form.number_input_field(name="salary", label="Desired salary", is_integer=False, help_text="Your dream salary is here", default=data_map_salary)
  
    data_map_desired_salary = DataMap()
    data_map_desired_salary.add(Assignment(target_variable="self.input.value", value_expression="flow.input.salary_expectation"))

     #Field: Projected salary
    user_node_with_form.field_output_field(name="acknowledge", label="Projected salary", value = data_map_desired_salary)

    #Date: End Date
    data_map_end_date = DataMap()
    data_map_end_date.add(Assignment(target_variable="self.input.default",value_expression="flow.input.event_date.dateEnd"))
    user_node_with_form.date_input_field(name="endDate", label="End Date", default=data_map_end_date,required=True)
  
    data_map_list_source = DataMap()
    data_map_list_source.add(Assignment(target_variable="self.input.choices", value_expression="flow.input.listOfLanguages"))
    
    #Output List: Qualification
    user_node_with_form.list_output_field(name="strength", label="Qualification", choices=data_map_list_source)

    #Output list: Friends table
    data_map_list_friends = DataMap()
    data_map_list_friends.add(Assignment(target_variable="self.input.choices", value_expression="flow.input.friends.listOfNames"))
    user_node_with_form.list_output_field(name="friends", label="Friends", choices=data_map_list_friends, columns={"first_name": "First", "last_name": "Last"})

    #Mult-chocice: List of fruits dowpdown primitives
    data_map_multi_choice = DataMap()
    data_map_multi_choice.add(Assignment(target_variable="self.input.choices", value_expression="flow.input.listOfFruits"))
    user_node_with_form.multi_choice_input_field(name="multi-choice", label="List of Fruits", required=False, choices=data_map_multi_choice, 
                                                  show_as_dropdown=True, placeholder_text="Please enter your choice")
    #Mult-chocice: Books dowpdown complex
    data_map_list_books = DataMap()
    data_map_list_books.add(Assignment(target_variable="self.input.choices", value_expression="flow.input.books"))
    user_node_with_form.multi_choice_input_field(name="multi-choice_as_dropdown_1", label="List of Books", required=False, choices=data_map_list_books, 
                                                  show_as_dropdown=True, dropdown_item_column="title", placeholder_text="Please enter your choice")
 
    #Mult-chocice: List of names table
    data_map_list_friends_choice2 = DataMap()
    data_map_list_friends_choice2.add(Assignment(target_variable="self.input.choices", value_expression="flow.input.friends.listOfNames"))

    user_node_with_form.multi_choice_input_field(name="multi-choice_as_table", label="List of Names", required=False, choices=data_map_list_friends_choice2, 
                                                  show_as_dropdown=False, placeholder_text="Please enter your choice", columns={"first_name": "First", "last_name": "Last"} )

    #Input list: Books editable table
    data_map_list_books = DataMap()
    data_map_list_books.add(Assignment(target_variable="self.input.default", value_expression="flow.input.books"))

    user_node_with_form.list_input_field(name="books", label="Books", default=data_map_list_books, columns={"title": "Book Title", "author": "Book Author"})
    #Input list: Fuits editable table
    data_map_list_fruits = DataMap()
    data_map_list_fruits.add(Assignment(target_variable="self.input.default", value_expression="flow.input.listOfFruits"))

    user_node_with_form.list_input_field(name="Fruits", label="Preferred fruits", default=data_map_list_fruits)

    #Output Message: Successful submission
    user_node_with_form.message_output_field(name="success", label="Successful submission", message="Application successfully completed.")
 
    #Add user flow edges
    user_flow.edge(START, user_node_with_form)
    user_flow.edge(user_node_with_form, END)

    # Script to initialize friends list
    init_script = """
flow.input.friends.listOfNames = [
    {"first_name": "John", "last_name": "Doe"},
    {"first_name": "Alice", "last_name": "Smith"},
    {"first_name": "Bob", "last_name": "Johnson"}
]
flow.input.books=[
    { "title": "Shaken", "author":"John Grisham"},
    {"title": "The Client", "author":"John Grisham"},
]
flow.input.listOfFruits = ["apple", "oranges", "bananas"]
"""
    init_data = aflow.script(name="init_data", script=init_script)
    
    # add the user flow to the flow sequence to create the flow edges
    aflow.sequence(START, init_data, user_flow, END)
  
    return aflow