> ## Documentation Index
> Fetch the complete documentation index at: https://docs.julep.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tasks

> Understanding Julep Tasks and workflows

## Overview

Tasks are GitHub Actions-style workflows that define multi-step actions in Julep. Think of them as recipes that tell an agent exactly how to accomplish a goal. For example, a task might outline the steps to "Summarize a Research Paper" or "Debug a Code Issue".

<Frame>
  <iframe src="https://www.loom.com/embed/c5cda67936254465aaff4548245b3e13?hideEmbedTopBar=true" alt="Tasks in action" width="720" height="480" allow="autoplay; encrypted-media" allowfullscreen />
</Frame>

Here are some of the key features of tasks:

* Connect multiple AI operations seamlessly
* Make decisions based on intermediate results
* Run operations in parallel for efficiency
* Integrate with external tools and APIs
* Maintain state throughout execution

## Components

A task consists of several key components which can be broadly classified into:

### Input Schema

```yaml YAML theme={"dark"}
name: Summarize Document
description: Create a concise summary of any document
input_schema:
  type: object
  properties:
    document_text:
      type: string
      description: The text to summarize
```

<Warning>
  **⚠️ Workflow Input Size Warning**: Avoid passing extremely large objects as input to your workflows, as this can cause execution failures. Large inputs (such as massive JSON objects, extensive arrays, or huge text files) may exceed memory limits or hit serialization constraints.

  **What to do instead:**

  * **Break large data into smaller chunks** and process them iteratively
  * **Use file uploads** and pass file references instead of raw content
  * **Implement pagination** for large datasets

  **Rule of thumb:** If your input data is larger than a few megabytes, consider alternative approaches.
</Warning>

### Tools

Tools are functions that can be used by an agent to perform tasks. Julep supports:

* [User-defined functions](/concepts/tools#user-defined-functions)
* [System tools](/concepts/tools#system-tools)
* [Integrations](/concepts/tools#integration-tools)
* [API calls](/concepts/tools#api-call-tools)

Learn more about tools [here](/concepts/tools).

```yaml YAML theme={"dark"}
tools:
- name: internet_search
  description: Performs an internet search using Brave
  type: integration
  integration:
    provider: brave
    method: search
    setup:
      brave_api_key: <BRAVE_API_KEY>
```

### Sub-Workflows

A task can be made up of multiple sub-workflows. These sub-workflows can be named and can be used to break down complex tasks into smaller, more manageable pieces.

```yaml YAML theme={"dark"}
name: Summarize Document
description: Create a concise summary of any document

sample_sub_workflow:
- prompt: |-
    $ f'Tell me a joke about {steps[0].input.topic}:'

main:
- workflow: sample_sub_workflow
  arguments:
    topic: AI
```

<Info>
  You can learn more about sub-workflows [here](/advanced/types-of-task-steps#subworkflow-step).
</Info>

### Steps

<Note>
  We use tasks and workflows interchangeably. They are the same except Julep's branding reflects tasks.
</Note>

Below is a table of all the steps that can be used in a task.

| Name                                                                | Description                                  |
| ------------------------------------------------------------------- | -------------------------------------------- |
| [Tool Call](/advanced/types-of-task-steps#tool-call-step)           | Execute tools defined in the task            |
| [Prompt](/advanced/types-of-task-steps#prompt-step)                 | Send messages to the AI model                |
| [Evaluate](/advanced/types-of-task-steps#evaluate-step)             | Perform calculations or data manipulation    |
| [Wait for Input](/advanced/types-of-task-steps#wait-for-input-step) | Pause workflow for user input                |
| [Subworkflow](/advanced/types-of-task-steps#subworkflow-step)       | Execute a subworkflow                        |
| [Set](/advanced/types-of-task-steps#set-step)                       | Store values for later use                   |
| [Get](/advanced/types-of-task-steps#get-step)                       | Retrieve values from storage                 |
| [Foreach](/advanced/types-of-task-steps#foreach-step)               | Iterate over a collection                    |
| [Map-reduce](/advanced/types-of-task-steps#map-reduce-step)         | Process collections in parallel              |
| [Switch](/advanced/types-of-task-steps#switch-step)                 | Multiple condition handling                  |
| [If-else](/advanced/types-of-task-steps#if-else-step)               | Conditional execution                        |
| [Sleep](/advanced/types-of-task-steps#sleep-step)                   | Pause execution                              |
| [Return](/advanced/types-of-task-steps#return-step)                 | Return values from workflow                  |
| [Yield](/advanced/types-of-task-steps#yield-step)                   | Execute subworkflows                         |
| [Log](/advanced/types-of-task-steps#log-step)                       | Log messages or specific values              |
| [Error](/advanced/types-of-task-steps#error-step)                   | Handle errors by specifying an error message |

> You can learn more about workflow steps as to how they work in the [Workflow Steps](/advanced/types-of-task-steps) section.

### Context Variables

Tasks have access to three types of context:

#### Input Variables

Access input parameters:

```yaml YAML theme={"dark"}
- prompt: $ f'Hello {steps[0].input.user_name}'
```

#### Step Results

Use outputs from previous steps:

```yaml YAML theme={"dark"}
- evaluate: $ len(_.search_results)
- if: $ _.count > 0
```

<Accordion title="How to access the input and output of a step">
  In any step, you can access the input and output of a step using the `steps[index].output` or `steps[index].input` variable. For example:

  ```yaml theme={"dark"}
  - evaluate:
      topic: $ steps[0].input.topic
  ```

  ```yaml theme={"dark"}
  - evaluate:
      topic: $ steps[0].output.topic
  ```

  In Julep, the steps are indexed from 0. So the first step is `steps[0]` and the second step is `steps[1]` and so on.
  Furthermore the first step input is nothing but the task input and the last step output is nothing but the output of the task.
</Accordion>

<Note>
  To learn more about how to use the `$` variable and new syntax, please refer to the [New Syntax](/advanced/new-syntax) section.
</Note>

#### Environment Context

Access agent and session data:

```yaml YAML theme={"dark"}
- prompt: $ f'Agent {agent.name} is helping you'
```

<Info>
  Input schemas help catch errors early by validating all inputs before execution starts.
</Info>

Here's how these components work together:

```yaml YAML [expandable] theme={"dark"}
name: Process Customer Feedback
description: Analyze and categorize customer feedback
input_schema:
  type: object
  required: ["feedback_text"]
  properties:
    feedback_text:
      type: string
    sentiment_analysis:
      type: boolean
      default: true

tools:
- name: get_weather_info
  type: integration  
  integration:
    provider: weather
    setup:
      openweathermap_api_key: OPENWEATHERMAP_API_KEY

main:
- tool: get_weather_info
  arguments:
    location: $ steps[0].input.location

- prompt: |-
    $ f"""The weather in {steps[0].output.location} is the following:
    {steps[0].output.weather}
    
    <task>
    Analyze the weather and provide a summary of the weather in the location. Include some recommendations on what to wear based on the weather.
    </task>
    """
```

> Learn more about tools [here](/concepts/tools).

### Metadata

Metadata is a key-value pair that can be used to categorize and filter tasks.

## How to Use Tasks ?

### Creating a Task

Here's a simple task that summarizes a document and checks if the summary is too long. We first define the task in a YAML file and then create it using the Julep SDK.

<CodeGroup>
  ```yaml task.yaml [expandable] theme={"dark"}
  name: Summarize Document
  description: Create a concise summary of any document
  input_schema:
    type: object
    properties:
      document_text:
        type: string
        description: The text to summarize

  main:
    - prompt: |-
        $ f'''Analyze the following text and create a summary:
        {steps[0].input.document_text}'''
      unwrap: true
    - evaluate:
        too_long: $ len(_) > 500
    - if: $ _.too_long
      then:
        prompt: |-
          $ f'''Make the summary more concise:
          {steps[0].output}'''
        unwrap: true
      else: 
        evaluate:
          content: $ steps[0].output
  ```

  ```python main.py theme={"dark"}
  import yaml
  import os

  task_def = yaml.safe_load(open("task.yaml"))

  task = client.tasks.create(
      agent_id="agent_id",
      **task_def
  )
  ```

  ```javascript index.js theme={"dark"}


  // Create a task
  import yaml from "yaml";
  import fs from "fs";

  const task_definition = yaml.parse(fs.readFileSync("task.yaml", "utf8"));

  async function createTask(agentId) {
    const task = await client.tasks.create(agentId, task_definition);
    return task;
  }
  ```
</CodeGroup>

<Tip>
  Check out the API reference [here](/api-reference/tasks) or SDK reference (Python [here](/sdks/python/reference#tasks) or JavaScript [here](/sdks/nodejs/reference#tasks) for more details on different operations you can perform on tasks.
</Tip>

### Executing a Task

Here's how to execute a task:

<CodeGroup>
  ```python Python theme={"dark"}
  # Execute a task
  execution = client.executions.create(
    task_id=task.id,
    input={
        "document_text": "This is a sample document"
    }
  )

  # Monitor progress
  while True:
    result = client.executions.get(execution.id)
    if result.status in ["succeeded", "failed"]:
        break
    time.sleep(1)
  ```

  ```javascript Node.js [expandable] theme={"dark"}
  // Execute a task
  async function executeTask(taskId) {
    const execution = await client.executions.create(taskId, {
      input: { idea: "A cat who learns to fly" },
    });

    while (true) {
      const result = await client.executions.get(execution.id);
      console.log(result.status, result.output);

      if (result.status === "succeeded" || result.status === "failed") {
        // 📦 Once the execution is finished, retrieve the results
        if (result.status === "succeeded") {
          console.log(result.output);
        } else {
          throw new Error(result.error);
        }
        break;
      }

      await new Promise((resolve) => setTimeout(resolve, 1000));
    }
  }
  ```
</CodeGroup>

<Tip>
  Check out the API reference [here](/api-reference/executions) or SDK reference (Python [here](/sdks/python/reference#executions) or JavaScript [here](/sdks/nodejs/reference#executions) for more details on different operations you can perform on tasks.
</Tip>

## Relationship to Other Concepts

This section will help you understand how tasks relate to other concepts in Julep.

### Agents

Julep agents can power tasks by providing memory, context, or tools. Tasks are multi-step workflows designed for complex, automated execution. Whenever you create a task, you can associate it with an agent if you want to leverage that agent's capabilities. Unlike sessions, tasks are not meant for real-time interaction; they run through a defined workflow to achieve a goal.

For example:

<CodeGroup>
  ```python Python [expandable] theme={"dark"}
  import yaml

  # Create an agent
  agent = client.agents.create(
    name="Customer Support Agent",
    about="An agent that handles customer support requests",
    model="gpt-4o",
  )

  # Add a tool to the agent
  client.agents.tools.create(
    agent_id=agent.id,
    **yaml.safe_load("""
    name: send_email
    type: integration
    integration:
      provider: email
      method: send
      setup:
        host: "smtp.example.com"
        port: 587
        user: "your_username"
        password: "your_password"
    """),
  )

  # Create a task that inherits this tool
  task = client.tasks.create(
    agent_id=agent.id,
    **yaml.safe_load("""
      name: Handle Support Request

      # Make sure to set this to true if you want to inherit tools from the agent
      inherit_tools: true

      input_schema:
        type: object
        properties:
          customer_email:
            type: string
            description: The email of the customer
          subject:
            type: string
          body:
            type: string
            description: The body of the email

      main:
      - prompt:
        - role: system
          content: You are a customer support agent who works for Julep AI. You will be given a support request from a customer. You will need to handle the request by sending a reply email to the customer.
        - role: user
          content: |-
            $ f"""Handle the support request from this email: {steps[0].input.customer_email}
            The subject of the email is this:
            {steps[0].input.subject}

            ---

            The body of the email is this:
            {steps[0].input.body}
            """
        unwrap: true

      - tool: send_email
        arguments:
          to: $ steps[0].input.customer_email
          from: "support@julep.ai"
          subject: "$ f'Re: {steps[0].input.subject}'"
          body: $ steps[0].output
    """)
  )
  ```

  ```javascript Node.js [expandable] theme={"dark"}
  // Create an agent
  const agent = await client.agents.create({ 
    name: "Customer Support Agent",
    about: "An agent that handles customer support requests",
    model: "gpt-4o",
    });

  // Add a tool to the agent
  await client.agents.tools.create(
    agent.id,
    yaml.parse(`
      name: send_email
      type: integration
      integration:
        provider: email
        method: send
        setup:
          host: "smtp.example.com"
          port: 587
          user: "your_username"
          password: "your_password"
    `)
  );

  // Create a task that inherits this tool
  const task = await client.tasks.create(
    agent.id, 
    yaml.parse(`
      name: Handle Support Request

      # Make sure to set this to true if you want to inherit tools from the agent
      inherit_tools: true

      input_schema:
        type: object
        properties:
          customer_email:
            type: string
            description: The email of the customer
          subject:
            type: string
          body:
            type: string
            description: The body of the email

      main:
      - prompt:
        - role: system
          content: You are a customer support agent who works for Julep AI. You will be given a support request from a customer. You will need to handle the request by sending a reply email to the customer.
        - role: user
          content: |-
            $ f"""Handle the support request from this email: {steps[0].input.customer_email}
            The subject of the email is this:
            {steps[0].input.subject}

            ---

            The body of the email is this:
            {steps[0].input.body}
            """
        unwrap: true

      - tool: send_email
        arguments:
          to: $ steps[0].input.customer_email
          from: "support@julep.ai"
          subject: "$ f'Re: {steps[0].input.subject}'"
          body: $ steps[0].output
    `)
  );
  ```
</CodeGroup>

### Tools

Task can leverage tools to perform complex operations. There are 2 ways of defining tools for tasks:

1. Associate a tool with an agent, and inherit it in the task definition by setting `inherit_tools` to `true` while creating the task. Example:

<CodeGroup>
  ```python Python [expandable] theme={"dark"}
  client.agents.tools.create(
    agent_id="agent_id",
    **yaml.safe_load("""
      name: get_weather_info
      type: integration
      integration:
        provider: weather
        setup:
          openweathermap_api_key: "your_openweathermap_api_key"
    """)

  task = client.tasks.create(
    agent_id="agent_id",
    **yaml.safe_load("""
      name: Get Weather Info
      inherit_tools: true
      main:
        - tool: get_weather_info
          arguments:
            location: New York
    """)
  )
  ```

  ```javascript Node.js [expandable] theme={"dark"}
  await client.agents.tools.create(
    agent.id,
    yaml.parse(`
      name: get_weather_info
      type: integration
      integration:
        provider: weather
        setup:
          openweathermap_api_key: "your_openweathermap_api_key"
    `)
  );

  task = await client.tasks.create(
    agent.id,
    yaml.parse(`
      name: Get Weather Info
      inherit_tools: true
      main:
        - tool: get_weather_info
          arguments:
            location: New York
    `)
  );
  ```
</CodeGroup>

2. Define a tool in the task definition. Example:

<CodeGroup>
  ```python Python theme={"dark"}
  task = client.tasks.create(
    agent_id="agent_id",
    **yaml.safe_load("""
      name: Get Weather Info
      tools:
      - name: get_weather_info
        type: integration
        integration:
          provider: weather
          setup:
            openweathermap_api_key: "your_openweathermap_api_key"
      main:
        - tool: get_weather_info
          arguments:
            location: New York
    """)
  )
  ```

  ```javascript Node.js theme={"dark"}
  task = await client.tasks.create(
    agent.id,
    yaml.parse(`
      name: Get Weather Info
      tools:
      - name: get_weather_info
        type: integration
        integration:
          provider: weather
          setup:
            openweathermap_api_key: "your_openweathermap_api_key"
      main:
        - tool: get_weather_info
          arguments:
            location: New York
    `)
  );
  ```
</CodeGroup>

When you define a tool in the task definition, it is available to all steps in that task only. On the other hand, when you associate a tool with an agent, it is available to all the Tasks associated with that agent.

## Best Practices

<CardGroup cols={3}>
  <Card title="Keep Tasks Focused" icon="bullseye">
    <ul>
      <li>1. **Purpose**: Each task should have a single, clear purpose</li>
      <li>2. **Subtasks**: Break complex workflows into smaller subtasks</li>
    </ul>
  </Card>

  <Card title="Handle Errors Gracefully" icon="shield-check">
    <ul>
      <li>1. **Error Handling**: Use try/catch blocks for error-prone operations</li>
      <li>2. **Error Messages**: Provide helpful error messages</li>
      <li>3. **Fallback Options**: Include fallback options where appropriate</li>
    </ul>
  </Card>

  <Card title="Optimize Performance" icon="gauge">
    <ul>
      <li>1. **Parallel Execution**: Use parallel execution when steps are independent</li>
      <li>2. **Map-Reduce**: Use map-reduce to run steps in parallel</li>
    </ul>
  </Card>
</CardGroup>

## Next Steps

* [Workflow Steps](/advanced/types-of-task-steps) - Learn about all available step types
* [Tools](/concepts/tools) - Learn about tools and how to use them in tasks
* [Sessions](/concepts/sessions) - Learn about sessions and how to use them in tasks

## See Examples

* [Simple Task notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/basics/02-Simple-Task.ipynb)
* [Trip Planning Assistant notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/03-trip-planning-assistant.ipynb)
