Overview

In Julep broadly speaking there are two types of steps:
The steps defined out here are in the YAML format. You can learn more about the YAML format here.

Control Flow Steps

Prompt Step

Send messages to the AI model:
YAML
# Simple prompt
- prompt: What is your name?

# Multi-message prompt
- prompt:
    - role: system
      content: You are a helpful assistant
    - role: user
      content: "Hello!"

# Prompt with settings
- prompt:
    - role: user
      content: Generate a creative story
  settings:
    model: "claude-3.5-sonnet"
    temperature: 0.8

# Prompt with automatic tool execution
- prompt: What's the weather in San Francisco?
  auto_run_tools: true  # Tools execute automatically if needed

# Prompt with JSON response format
- prompt:
    - role: system
      content: You are a helpful assistant that always responds in JSON format.
    - role: user
      content: List 3 interesting facts about space. Format as JSON with 'facts' array.
  settings:
    model: "gpt-4o-mini"
    temperature: 0.7
    response_format:
      type: json_object

# Prompt with JSON schema for structured output
- prompt:
    - role: system
      content: You are a data extraction specialist.
    - role: user
      content: Extract product information from the description.
  settings:
    response_format:
      type: json_schema
      json_schema:
        name: product_info
        schema:
          type: object
          properties:
            product_name:
              type: string
            features:
              type: array
              items:
                type: string
            price:
              type: number
          required: ["product_name", "features", "price"]
When auto_run_tools is set to true in a prompt step, any tools available to the agent will be automatically executed if the model decides to use them. The results are then fed back to the model to continue processing. This is particularly useful for creating autonomous workflows where the agent can gather information and make decisions without manual intervention.
The response_format setting allows you to request structured output from the model. There are two main options:
  • type: json_object - Ensures the model responds with valid JSON
  • type: json_schema - Enforces a specific JSON structure defined by a schema
When using response_format, make sure to instruct the model to produce JSON in your prompt (via system or user message) for best results. Response format support varies by model provider - check the supported models documentation for compatibility.
In the prompt step we offer a bunch of Python functions to help you manipulate data. Here is a list of the functions you can use:
  • Standard library modules:
    • re: Regular expressions (safe against ReDoS)
    • json: JSON encoding/decoding
    • yaml: YAML parsing/dumping
    • string: String constants and operations
    • datetime: Date and time operations
    • math: Mathematical functions
    • statistics: Statistical operations
    • base64: Base64 encoding/decoding
    • urllib: URL parsing operations
    • random: Random number generation
    • time: Time operations
  • Constants:
    • NEWLINE: Newline character
    • true: Boolean true
    • false: Boolean false
    • null: None value

Tool Call Step

Execute tools defined in the task:
YAML
# Simple tool call
- tool: web_search
  arguments:
    query: Latest AI news

# Tool call with complex arguments
- tool: process_data
  arguments:
    input_data: $ _.previous_result
    options:
      format: "json"
      validate: true

Evaluate Step

Perform calculations or data manipulation:
YAML
# Simple evaluation
- evaluate:
    count: $ len(_.results)

# Multiple evaluations
- evaluate:
    total: $ sum(_.numbers)
    average: $ _.total / len(_.numbers)
    formatted: $ f'Average: {_.average:.2f}'
In the evaluate step we offer a bunch of Python functions to help you manipulate data. Check out the Python Expressions for more information.

Wait for Input Step

Pause workflow for user input:
YAML
# Simple input request
- wait_for_input:
    info:
      message: "Please provide your name"

# Input with validation
- wait_for_input:
    info:
      message: "Enter your age"
      validation:
        type: "number"
        minimum: 0
        maximum: 150

Subworkflow Step

Executing a subworkflow from a main workflow:
YAML
# Subworkflow
subworkflow:
- evaluate:
    main_workflow_input: $ _.content # you can use steps[0].input.content to access the input of the subworkflow
- return:
    result: "This is the subworkflow"

# Main workflow
main:
# Step 0: Evaluate step
- evaluate:
    result: "This is the main workflow"

# Step 1: Call the subworkflow
- workflow: subworkflow # name of the subworkflow
  arguments: # input to the subworkflow
    content: $ _.result # you can use steps[0].output.result to access the result of the previous step

# Step 2: Evaluate step
- evaluate:
    subworkflow_result: $ steps[1].output.result # this will be the result of the subworkflow
  • The arguments passed from the main workflow to the subworkflow are available in the steps[0].input of the subworkflow.
  • The result of the subworkflow is available in the steps[1].output.result of the main workflow.
  • The Input/Output Data References between steps is exclusive to the workflow they are defined in. A workflow cannot reference the input or output of another workflow between the steps. To learn more about Input/Output Data References click here.
Self recursion is allowed in a subworkflow but not in a main workflow.

Key-Value Steps

Get Step

Retrieve values from storage:
YAML
# Get a single value
- get: user_preference

# Get multiple values
- get:
    - preference1
    - preference2

Set Step

Store values for later use:
YAML
# Set a single value
- set:
    user_name: John

# Set multiple values
- set:
    count: $ len(_.results)
    has_data: $ _.count > 0
Values stored using the set step are added to the workflow’s global state object, which can be accessed anywhere in the workflow using state.variable_name. For example:
YAML
# Access previously set values
- evaluate:
    greeting: $ f"Hello, {state.user_name}!"
    data_status: $ f"Has data: {state.has_data}, Count: {state.count}"
Each subworkflow has its own isolated state object. Values set in one subworkflow are not accessible from other subworkflows or the parent workflow.

Label Step

Label a step to make it easier to identify and access those values later in any step:
YAML
# Step 0: Set a single value
- set:
    user_name: John
  label: get_user_name

# Step 1: Set multiple values
- set:
    count: $ len(_.results)
    has_data: $ _.count > 0
  label: get_count_and_has_data
In any steps following the label step, you can access the values set in the label step using the $ steps['label_name'].input.attribute_name or $ steps['label_name'].output.attribute_name syntax. For example:
YAML
- evaluate:
    user_name: $ steps['get_user_name'].output.user_name
- evaluate:
    count: $ steps['get_count_and_has_data'].output.count
    has_data: $ steps['get_count_and_has_data'].output.has_data

Iteration Steps

Foreach Step

Iterate over a collection:
YAML
# Simple foreach
- foreach:
    in: $ _.items
    do:
      log: $ f'Processing {_}'

# Foreach with complex processing
- foreach:
    in: $ _.documents
    do:
      tool: analyze
      arguments:
        text: $ _.content
      evaluate:
        results: $ _ + [_.analysis]

Map-Reduce Step

Process collections in parallel:
YAML
# Simple map-reduce
- over: $ _.urls
  map:
    tool: fetch_content
    arguments:
      url: $ _
  reduce: $ results + [_]

# Map-reduce with parallelism
- over: $ _.queries
  map:
    tool: web_search
    arguments:
      query: $ _
  parallelism: 5 # Number of parallel steps to execute
  • By default the parallelism if not mentioned is 100. If mentioned, it is the maximum number of steps that can run in parallel concurrently.
  • When using over step, the map step is executed for each value in the collection.
  • The reduce step is executed after the map step.

Conditional Steps

If-Else Step

Conditional execution:
YAML
# Simple if
- if: $ _.count > 0
  then:
    log: Found results

# If-else
- if: $ _.score > 0.8
  then:
    log: High score
  else:
    log: Low score

# If-else with multiple conditions
- if: $ 1 > 0
  then:
    if: $ 2 > 1
    then:
      evaluate:
        x: y
  else:
    evaluate:
      x: z

Switch Step

Multiple condition handling:
YAML
# Switch statement
- switch:
    - case: $ _.category == "A"
      then:
        - log: Category A
    - case: $ _.category == "B"
      then:
        - log: Category B
    - case: $ _  # Default case
      then:
        - log: Unknown category

Other Control Flow

Sleep Step

Pause execution:
YAML
# Sleep for duration
- sleep:
    seconds: 30

# Sleep with different units
- sleep:
    minutes: 5
    # hours: 1
    # days: 1

Return Step

Return values from workflow:
YAML
# Simple return
- return: $ _.result

# Structured return
- return:
    data: $ _.processed_data
    metadata:
      count: $ _.count
      timestamp: $ datetime.now().isoformat()

Log Step

Log messages or specific values:
YAML
- log: $ f'Processing completed for item {item_id}'

Error Step

Handle errors by specifying an error message:
YAML
- error: Invalid input provided

Example: Complex Workflow

Here’s an example combining various step types:
YAML
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/schemas/create_task_request.json
name: Multi-Step Task Demonstration
description: A demonstration of multi-step task processing with research and summarization capabilities.

################################################################################
############################# INPUT SCHEMA #####################################
################################################################################
input_schema:
  type: object
  properties:
    topic:
      type: string
      description: The topic to research and summarize.

################################################################################
############################# TOOLS ############################################
################################################################################

# Describing the tools that will be used in the workflow
tools:
- name: web_search
  type: integration
  integration:
    provider: brave
    setup:
      brave_api_key: "YOUR_BRAVE_API_KEY"

################################################################################
############################# MAIN WORKFLOW ####################################
################################################################################
main:
# Step 0: Generate initial research questions
- prompt:
  - role: system
    content: >-
      $ f'''
      You are a research assistant. Your task is to formulate three specific research questions about the given topic: {steps[0].input.topic}'''
  unwrap: true

# Step 1: Web search for each question
- foreach:
    in: $ _.split('\\n')
    do:
      tool: web_search
      arguments:
        query: $ _

# Step 2: Extract relevant information
- evaluate:
    relevant_info: $ [output for output in _]

# Step 3: Process and summarize information
- if: $ len(_.relevant_info) >= 3
  then:
      prompt:
      - role: system
        content: >-
          $ f'''
          Summarize the following information about {steps[0].input.topic}:
          {_.relevant_info}'''
      unwrap: true
  else:
      prompt:
      - role: system
        content: >-
          $ f'''
          Not enough information gathered. Please provide a brief overview of {steps[0].input.topic} based on your knowledge.'''
      unwrap: true

# Step 4: Record the summary
- log: >-
    $ f'''
    Summary for {steps[0].input.topic}: {_}'''

# Step 5: Prepare final output
- return: 
    summary: $ _
    topic: $ steps[0].input.topic

Best Practices

Step Organization

  • Group related steps logically
  • Use comments to explain complex steps
  • Keep step chains focused and manageable

Error Handling

  • Use if-else for error conditions
  • Provide fallback options
  • Log important state changes

Performance

  • Use parallel execution when possible
  • Optimize data passing between steps
  • Cache frequently used values

Support

If you need help with further questions in Julep: