C3 AI Documentation Home

Configure Workflows through Jupyter

In this guide, you will create a simple workflow using the C3 AI Workflows Python API. You will build a straightforward two-node workflow without complex logic or branching.

Prerequisites

Before you begin, ensure the following:

  • You can access Jupyter Notebook and the Application C3 AI Console.

Open Jupyter Notebook

Open Jupyter Notebook from the C3 Generative AI Application card:

  1. Navigate to your application in C3 AI Studio.
  2. Select Jupyter Notebook.
  3. Set the py-workflows_312 runtime as the notebook kernel.

Overview of key concepts

Workflows have several components:

  • Nodes: Individual steps that do specific tasks
  • Workflow State: Shared memory that holds data between nodes
  • Edges: Connections that control which node runs next
  • Templates: Blueprints for creating nodes

Build a workflow with two nodes

This tutorial shows you how to create and execute a workflow with two nodes to get user input (a name) and return a greeting.

  1. Setup and create a workflow
  2. Register a node template
  3. Create the first node to collect the user's name
  4. Create the second node to generate a greeting
  5. Connect the nodes together
  6. Add the nodes to the workflow state and specify mappings
  7. Validate your greeting workflow for completeness
  8. Update the graph instance and re-publish the workflow
  9. Execute the workflow
  10. Handle interrupt nodes and provide user input
  11. Print the final results
  12. Visualize the workflow structure in Mermaid

1. Setup and create a workflow

Start by importing required packages and creating a basic workflow:

Python
# Basic imports
from workflows import Graph
from workflows.components import (
    FieldTypeSchema,
    Node,
    ConditionalEdges,
)

# Create a simple workflow
workflow = c3.Genai.Agent.Resource.Workflow(
    id="Simple Greeting Workflow",
    name="Simple Greeting Workflow",
    projects=[c3.Genai.Project.forId("default")]
)

# Create the native graph
graph = Graph()

# Attach the native graph to the persisted workflow entity
workflow = workflow.updateNative(graph)
graph = workflow.toNative()

2. Register a node template

There are several available node templates, including:

Text
- **get_user_input_node_template**: Displays a message and pauses the workflow until the user enters text. Produces one output 'user_input' as a string.
- **code_agent_node_template**: Executes a Code Agent call. that can run Python code and search the internet. Both inputs and outputs are configurable.
- **c3_type_input_node_template**: Accepts a C3 Type from the whitelisted data model and makes it available to the application for processing.

Register two templates to have the user input their name and to create a greeting:

Python
# Get the templates we need
string_input_template = c3.Genai.Workflow.NodeTemplate.forName("string_input_node_template").toNative()
llm_template = c3.Genai.Workflow.NodeTemplate.forName("llm_node_template").toNative()

# Add the templates to the graph
graph.update_node_templates([string_input_template, llm_template], force_update=True)

print("✅ Templates registered")

3. Create the first node to collect the user's name

Python
# Node 1: Get the user's name
name_input_node = graph.register_node_from_template(
    template_name="string_input_node_template",
    node_name="GetUserName",
    description="Ask the user for their name",
    output_spec={"user_name": str},
    llm_config_values={
        "input_description": "What's your name?",
        "instructions": "Just return the user's name as a simple string. No extra formatting."
    }
)

print("✅ Created GetUserName node")

4. Create the second node to generate a greeting

Python
# Node 2: Generate a greeting using the name
greeting_node = graph.register_node_from_template(
    template_name="llm_node_template",
    node_name="GenerateGreeting",
    description="Create a friendly greeting message",
    input_spec={"user_name": str},
    output_spec={"greeting_message": str},
    llm_config_values={
        "instructions": "Create a friendly, warm greeting message using the user's name. Keep it simple and cheerful.",
        "output_schema": {
            "type": "object",
            "properties": {
                "greeting_message": {
                    "type": "string",
                    "description": "A friendly greeting message that includes the user's name"
                }
            },
            "required": ["greeting_message"],
            "additionalProperties": False
        }
    }
)

print("✅ Created GenerateGreeting node")

5. Connect the nodes together

Python
# Create a simple edge connecting the name input to the greeting generator
graph.add_edge(
"GetUserName",
"GenerateGreeting"
)

print("✅ Connected nodes: GetUserName → GenerateGreeting")

6. Add the nodes to the workflow state and specify mappings

Python

# Add all fields to the workflow state
for node_name, node in graph.nodes.items():
    for key, val in node.output_spec.fields.items():
        graph.add_field_to_state(key, val)

# Add input and output mappings for all nodes
for node_name, node in graph.nodes.items():
    for key, val in node.input_spec.fields.items():
        graph.add_input_mapping(state_field=key, target_node=node_name, target_input=key, force_update=True)

for node_name, node in graph.nodes.items():
    for key, val in node.output_spec.fields.items():
        graph.add_output_mapping(source_node=node_name, source_output=key, state_field=key)

print("✅ Added input/output mappings")

7. Validate your greeting workflow for completeness

Python
_ = graph.diagnose_graph_completeness(display_results=True)

8. Update the graph instance and re-publish the workflow

Python
workflow = workflow.updateNative(graph)
workflow = workflow.get().publish()

9. Execute the workflow

Python

# Create execution. If not provided, execution is auto-generated.
workflow_execution = c3.Genai.WorkflowExecution(
    id="greeting_execution",
    name="Greeting Execution",
    workflow=workflow
).upsert()

# Execute the workflow
results = workflow.executeWorkflow(
    workflowExecution=workflow_execution,
    input={},  # Empty because none of the state variables are initialized to anything (all None)
    config=None,
    kwargs={}
)

print("✅ Workflow started successfully!")

10. Handle interrupt nodes and provide user input

Since the username node interrupts the user for a response, it is an interrupt node. You can check for its status with the following code. Update the payload to try different names.

Python

# Check for pending interrupts
pending_interrupts = workflow.getPendingInterrupts(workflow_execution.id)
if pending_interrupts:
    interrupt = pending_interrupts[0]

    # Provide the user's name
    resume_response = c3.Genai.Workflow.ResumeResponse(
        interruptId=interrupt.interruptId,
        payload="Bob"  # The user's name
    )

    # Resume execution
    resumed_results = list(workflow.resumeWorkflow(workflow_execution, [resume_response]))
print("✅ Workflow resumed and completed!")

11. Print the final results

Python

# Get the final results
final_state = workflow.getNativeExecutionStateValues(workflow_execution)
print(f"User Name: {final_state.get('user_name', 'Not found')}")
print(f"Greeting: {final_state.get('greeting_message', 'Not found')}")

12. Visualize the workflow structure in Mermaid

Python
# Check that the workflow is properly connected
_ = graph.diagnose_graph_completeness(display_results=True)

# Visualize the simple workflow
mermaid_spec = graph.visualize(direction="LR", sub_direction="TB", show_io=True)
print("Workflow visualization created!")

Adding More Complexity

Once you are comfortable with this simple workflow, you can:

  • Add more nodes: Create additional processing steps
  • Add conditional logic: Route based on user input
  • Add error handling: Handle unexpected inputs gracefully
  • Save/load state: Persist workflow progress

See Also

Was this page helpful?