> This page is for Voice APIs.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://dev.hume.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://dev.hume.ai/_mcp/server.

# Tool Use

EVI simplifies the integration of external APIs through function calling. Developers can integrate custom functions
that are invoked dynamically based on the user’s input, enabling more useful conversations. There are two key concepts
for using function calling with EVI: **Tools** and **Configurations** (Configs):

* **Tools** are resources that EVI uses to do things, like search the web or call external APIs. For example, tools
  can check the weather, update databases, schedule appointments, or take actions based on what occurs in the
  conversation. While the tools can be user-defined, Hume also offers natively implemented tools, like web search,
  which are labeled as “built-in” tools.

* **Configurations** enable developers to customize an EVI’s behavior and incorporate these custom tools. Setting up
  an EVI configuration allows developers to seamlessly integrate their tools into the voice interface. A configuration
  includes prompts, user-defined tools, and other settings.

![Tool use flow diagram](/_fern-img/a9cdfea8777ea2cdc98b08d72c04191919e3aa6abeb1350da2b0be43f75834d8.webp)

> **Info**
>
> Tool use is only supported when specifying certain [supplemental LLMs](/docs/speech-to-speech-evi/configuration/language-model) within your configuration. Currently, tool use
> is supported by [Claude](https://docs.anthropic.com/en/docs/tool-use),
> [GPT](https://platform.openai.com/docs/guides/function-calling),
> [Gemini](https://ai.google.dev/gemini-api/docs/function-calling),
> and [Moonshot AI](https://platform.moonshot.ai/docs/guide/use-kimi-api-to-complete-tool-calls) models. Function calling is also available if you are
> using your own custom language model using the [OpenAI function calling specification](https://platform.openai.com/docs/guides/function-calling). For best results, we suggest choosing a fast and intelligent LLM that performs well on function calling benchmarks.

The focus of this guide is on creating a Tool and a Configuration that allows EVI to use the Tool. Additionally, this
guide details the message flow of function calls within a session, and outlines the expected responses when function
calls fail. Refer to our [Configuration Guide](/docs/speech-to-speech-evi/configuration) for detailed,
step-by-step instructions on how to create and use an EVI Configuration.

> **Info**
>
> Explore these sample projects to see how Tool use can be implemented in
> [TypeScript](https://github.com/HumeAI/hume-api-examples/tree/main/evi/evi-typescript-function-calling),
> [Next.js](https://github.com/HumeAI/hume-api-examples/tree/main/evi/evi-next-js-function-calling), and
> [Python](https://github.com/HumeAI/hume-api-examples/tree/main/evi/evi-python-function-calling).

## Setup

For EVI to leverage tools or call functions, a configuration must be created with the tool’s definition. Our
step-by-step guide below walks you through creating a tool and adding it to a configuration, using either a no-code
approach through our [Portal](https://app.hume.ai) or a full-code approach through our API.

#### No code

### Create a Tool

We will first create a Tool with a specified function. In this example, we will create a tool for getting the
weather. In the [Portal](https://app.hume.ai), navigate to the
[EVI Tools page](https://app.hume.ai/evi/tools). Click the **Create tool** button to begin.

![EVI Tools page](/_fern-img/7aba7d31e27f4787d9ce3f18d73dc83fa2098b3d769ddf36637a314110a0cd93.webp)

### Fill in Tool details

Next, we will fill in the details for a weather tool named `get_current_weather`. This tool fetches the current
weather conditions in a specified location and reports the temperature in either Celsius or Fahrenheit. We can
establish the tool's behavior by completing the following fields:

* **Name**: Specify the name of the function that the language model will invoke. Ensure it begins with a
  lowercase letter and only contains letters, numbers, or underscores.
* **Description**: Provide a brief description of what the function does.
* **Parameters**: Define the function's input parameters using a JSON schema.

![EVI Create function interface](/_fern-img/4236235722d701705e3fdeffd571d724b1de7cfb0c16ac9383624c9e07e4360f.webp)

The JSON schema defines the expected structure of a function's input parameters. Here's an example JSON schema we
can use for the [parameters](/reference/speech-to-speech-evi/tools/create-tool#request.body.parameters)
field of a weather function:

#### parameters

```json
{
  "type": "object",
  "required": ["location", "format"],
  "properties": {
    "location": {
      "type": "string",
      "description": "The city and state, e.g. San Francisco, CA"
    },
    "format": {
      "type": "string",
      "enum": ["celsius", "fahrenheit"],
      "description": "The temperature unit to use. Infer this from the user's location."
    }
  }
}
```

### Create a Configuration

Next, we will create an EVI Configuration called Weather Assistant Config. This configuration will utilize the
`get_current_weather` Tool created in the previous step. See our
[Configuration guide](https://dev.hume.ai/docs/speech-to-speech-evi/configuration) for step-by-step
instructions on how to create a configuration. During the **Set up LLM** step, remember to select a model that supports tool use.

![Create a configuration called Weather Assistant Config in the Hume portal](/_fern-img/7295b4f3f286ce4cc0d60da6e360a03ce19db5dd94a8799a7c140071e026178d.webp)

### Add Tool to Configuration

Finally, we will specify the `get_current_weather` Tool in the Weather Assistant Config. Navigate to the **Tools**
section of the EVI Config details page. Click the **Add** button to add a function to your configuration.
Since we have already created a `get_current_weather` Tool in previous steps, we can simply select **Add existing
tool...** from the dropdown to specify it.

![Add tool to configuration within the Hume portal](/_fern-img/31bd791aad2f81dad193e561d3a2316e2b52fbfb8bc04ee02f891b7231417732.webp)

Select the tool to add `get_current_weather` to your configuration, then complete the remaining steps to create the
configuration.

![Add get\_current\_weather to configuration within the Hume portal](/_fern-img/7d26d9a7bafb10fd54686aeb636d51989bc61c5960af3345e615985f5843b6bc.webp)

#### Full code

### Create a Tool

We will first create a Tool with a specified function. In this example, we will create a tool for getting the
weather. Create this tool by making a POST request to
[/tools](/reference/speech-to-speech-evi/tools/create-tool) using the following request body:

#### Request body

```json
{
  "name": "get_current_weather",
  "version_description": "Fetches current weather and uses celsius or fahrenheit based on user's location.",
  "description": "This tool is for getting the current weather.",
  "parameters": "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }"
}
```

> **Warning**
>
> The `parameters` field must contain a valid JSON schema.

#### Sample response body

```json
{
  "tool_type": "FUNCTION",
  "id": "15c38b04-ec9c-4ae2-b6bc-5603512b5d00",
  "version": 0,
  "version_description": "Fetches current weather and uses celsius or fahrenheit based on user's location.",
  "name": "get_current_weather",
  "created_on": 1714421925626,
  "modified_on": 1714421925626,
  "fallback_content": null,
  "description": "This tool is for getting the current weather.",
  "parameters": "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }"
}
```

Record the value in the `id` field, as we will use it to specify the newly created Tool in the next step.

### Create a Configuration

Next, we will create an EVI Configuration called Weather Assistant Config, and include the created Tool by making
a POST request to [/configs](/reference/speech-to-speech-evi/configs/create-config) with the following
request body:

#### Request body

```json
{
  "name": "Weather Assistant Config",
  "language_model": {
    "model_provider": "OPEN_AI",
    "model_resource": "gpt-3.5-turbo",
    "temperature": null
  },
  "tools": [
    {
      "id": "15c38b04-ec9c-4ae2-b6bc-5603512b5d00",
      "version": 0
    }
  ]
}
```

#### Sample response body

```json
{
  "id": "87e88a1a-3768-4a01-ba54-2e6d247a00a7",
  "version": 0,
  "version_description": null,
  "name": "Weather Assistant Config",
  "created_on": 1714421581844,
  "modified_on": 1714421581844,
  "prompt": null,
  "voice": null,
  "language_model": {
    "model_provider": "OPEN_AI",
    "model_resource": "gpt-3.5-turbo",
    "temperature": null
  },
  "tools": [
    {
      "tool_type": "FUNCTION",
      "id": "15c38b04-ec9c-4ae2-b6bc-5603512b5d00",
      "version": 0,
      "version_description": "Fetches current weather and uses celsius or fahrenheit based on user's location.",
      "name": "get_current_weather",
      "created_on": 1714421925626,
      "modified_on": 1714421925626,
      "fallback_content": null,
      "description": "This tool is for getting the current weather.",
      "parameters": "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }"
    }
  ],
  "builtin_tools": []
}
```

> **Info**
>
> Ensure your tool definitions conform to the language model's schema. The specified language model will be the
> one to execute the function calls.

## Function calling

In this section, we will go over the end-to-end flow of a function call within a chat session. This flow will be
predicated on having specified the **Weather Assistant Config** when establishing a connection with EVI. See our
[Configuration Guide](/docs/speech-to-speech-evi/configuration/build-a-configuration#apply-the-configuration) for details on how to
apply your configuration when connecting.

> **Info**
>
> Check out the
> [TypeScript](https://github.com/HumeAI/hume-api-examples/blob/main/evi/evi-typescript-function-calling/src/handleToolCall.ts)
> and [Python](https://github.com/HumeAI/hume-api-examples/blob/main/evi/evi-python-function-calling/main.py)
> example projects for complete implementations of the weather Tool you'll build in this tutorial.

### Define a function

We must first define a function for your Tool. This function will take the same
[parameters](/reference/speech-to-speech-evi/tools/create-tool#request.body.parameters) as those specified
during your Tool's creation.

For this tutorial, we will define a function that calls a weather API (e.g., the
[Geocoding API](https://geocode.maps.co/)) to retrieve the weather for a designated city in a specified format. This
weather function will accept `location` and `format` as its parameters.

See the code below for a sample implementation:

#### TypeScript

```ts
async function fetchWeather(location: string, format: string): Promise<string> {
  // Fetch the location's geographic coordinates using Geocoding API
  const locationApiURL = `https://geocode.maps.co/search?q=${location}&api_key=${YOUR_WEATHER_API_KEY}`;
  const locationResponse = await fetch(locationApiURL);
  const locationData = await locationResponse.json();

  // Extract latitude and longitude from fetched location data
  const { lat, lon } = locationData[0];

  // Fetch point metadata using the extracted location coordinates
  const pointMetadataEndpoint = `https://api.weather.gov/points/${parseFloat(
    lat
  ).toFixed(3)},${parseFloat(lon).toFixed(3)}`;
  const pointMetadataResponse = await fetch(pointMetadataEndpoint);
  const pointMetadata = await pointMetadataResponse.json();

  // Extract weather forecast URL from point metadata
  const forecastUrl = pointMetadata.properties.forecast;

  // Fetch the weather forecast using the forecast URL
  const forecastResponse = await fetch(forecastUrl);
  const forecastData = await forecastResponse.json();
  const forecast = JSON.stringify(forecastData.properties.periods);

  // Return the temperature in the specified format
  return `${forecast} in ${format}`;
}
```

#### Python

```python
async def fetch_weather(location: str, format: str) -> str:
    # Construct the URL for the Weather API request
    location_api_url = f"https://geocode.maps.co/search?q={location}&api_key={YOUR_WEATHER_API_KEY}"

    # Create an HTTP client that automatically follows redirects
    async with httpx.AsyncClient(follow_redirects=True) as client:
        try:
            # Step 1: Fetch location data
            location_response = await client.get(location_api_url)
            location_response.raise_for_status()
            location_data = location_response.json()
        except httpx.HTTPError as e:
            return f"ERROR: Failed to fetch location data. {str(e)}"

        if not location_data:
            return "ERROR: No location data found."

        try:
            # Extract latitude and longitude from the location data
            lat = location_data[0]['lat']
            lon = location_data[0]['lon']
        except (IndexError, KeyError):
            return "ERROR: Unable to extract latitude and longitude."

        # Construct the URL for the Weather.gov API points endpoint
        point_metadata_endpoint = f"https://api.weather.gov/points/{float(lat):.4f},{float(lon):.4f}"

        try:
            # Step 2: Fetch point metadata
            point_metadata_response = await client.get(point_metadata_endpoint)
            point_metadata_response.raise_for_status()
            point_metadata = point_metadata_response.json()
        except httpx.HTTPError as e:
            return f"ERROR: Failed to fetch point metadata. {str(e)}"

        try:
            # Extract the forecast URL from the point metadata
            forecast_url = point_metadata['properties']['forecast']
        except KeyError:
            return "ERROR: Unable to extract forecast URL from point metadata."

        try:
            # Step 3: Fetch the weather forecast
            forecast_response = await client.get(forecast_url)
            forecast_response.raise_for_status()
            forecast_data = forecast_response.json()
        except httpx.HTTPError as e:
            return f"ERROR: Failed to fetch weather forecast. {str(e)}"

        try:
            # Extract the forecast periods from the response
            periods = forecast_data['properties']['periods']
        except KeyError:
            return "ERROR: Unable to extract forecast periods."

        # Validate the desired temperature format
        desired_unit = format.lower()
        if desired_unit not in ['fahrenheit', 'celsius']:
            return "ERROR: Invalid format specified. Please use 'fahrenheit' or 'celsius'."

        # Convert temperatures for all periods to the desired unit
        for period in periods:
            temperature = period.get('temperature')
            temperature_unit = period.get('temperatureUnit')

            if temperature is not None and temperature_unit is not None:
                if desired_unit == 'celsius' and temperature_unit == 'F':
                    # Convert Fahrenheit to Celsius
                    converted_temp = round((temperature - 32) * 5 / 9)
                    period['temperature'] = converted_temp
                    period['temperatureUnit'] = 'C'
                elif desired_unit == 'fahrenheit' and temperature_unit == 'C':
                    # Convert Celsius to Fahrenheit
                    converted_temp = round((temperature * 9 / 5) + 32)
                    period['temperature'] = converted_temp
                    period['temperatureUnit'] = 'F'

        # Return the forecast data as a JSON-formatted string
        forecast = json.dumps(periods, indent=2)
        return forecast
```

> **Info**
>
> Instead of calling a weather API, you can hardcode a return value like `75F` as a means to quickly test for the sake
> of this tutorial.

### EVI signals function call

Once EVI is configured with your Tool, it will automatically infer when to signal a function call within a chat
session. With EVI configured to use the `get_current_weather` Tool, we can now ask it: "what is the weather in New
York?"

Let's try it out in the [EVI Playground](https://app.hume.ai/evi/playground).

![Ask EVI what is the weather in New York](/_fern-img/35a4c589ea9112e9ec5f198e5f88b7d246e48c3a14674ebdede4d0cb945cffe4.webp)

We can expect EVI to respond with a
[User Message](/reference/speech-to-speech-evi/chat#receive.UserMessage) and a
[Tool Call](/reference/speech-to-speech-evi/chat#receive.ToolCallMessage.name) message:

#### Sample User Message

```json
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "What's the weather in New York?"
  },
  // ...etc
}
```

#### Sample Tool Call message

```json
{
  "type": "tool_call",
  "tool_type": "function",
  "response_required": true,
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "name": "get_current_weather",
  "parameters": "{\"location\":\"New York\",\"format\":\"fahrenheit\"}"
}
```

> **Info**
>
> Currently, EVI does not support parallel function calling. Only one function call can be processed at a time.

### Extract arguments from Tool Call message

Upon receiving a [Tool Call](/reference/speech-to-speech-evi/chat#receive.ToolCallMessage.name) message from EVI, we will parse the [parameters](/reference/speech-to-speech-evi/chat#receive.ToolCallMessage.parameters) and extract the arguments.

The code below demonstrates how to extract the `location` and `format` arguments, which the user-defined fetch weather function is expecting, from a received **Tool Call** message.

#### TypeScript

```ts
import { Hume } from 'hume';

async function handleToolCallMessage(
  toolCallMessage: Hume.empathicVoice.ToolCallMessage,
  socket: Hume.empathicVoice.chat.ChatSocket): Promise<void> {
  if (toolCallMessage.name === "get_current_weather") {
    // 1. Parse the parameters from the Tool Call message
    const args = JSON.parse(toolCallMessage.parameters) as {
      location: string;
      format: string;
    };
    // 2. Extract the individual arguments
    const { location, format } = args;
    // ...etc.
  }
}
```

#### Python

```python
import asyncio
from hume.client import AsyncHumeClient
from hume.empathic_voice import ToolCallMessage, ToolResponseMessage
from typing import Optional

async def handle_tool_call(self, message: ToolCallMessage) -> Optional[ToolResponseMessage]:
    # Extract the tool name and ID from the message
    tool_name = message.name
    tool_call_id = message.tool_call_id
    
    # 1. Parse the parameters from the Tool Call message
    tool_parameters = json.loads(message.parameters)

    if tool_name == "get_current_weather":
        # 2. Extract the individual arguments
        obtained_location = tool_parameters.get('location')
        obtained_format = tool_parameters.get('format', 'text')

        # ...etc.
```

### Invoke function call

Next, we will pass the extracted arguments into the previously defined fetch weather function. We will capture the return value to send back to EVI:

#### TypeScript

```ts
import { Hume } from 'hume';

async function handleToolCallMessage(
  toolCallMessage: Hume.empathicVoice.ToolCallMessage,
  socket: Hume.empathicVoice.chat.ChatSocket): Promise<void> {
  if (toolCallMessage.name === "get_current_weather") {
    // 1. Parse the parameters from the Tool Call message
    const args = JSON.parse(toolCallMessage.parameters) as {
      location: string;
      format: string;
    };
    // 2. Extract the individual arguments
    const { location, format } = args;
    // 3. Call fetch weather function with extracted arguments
    const weather = await fetchWeather(location, format);
    // ...etc.
  }
}
```

#### Python

```python
import asyncio
from hume.client import AsyncHumeClient
from hume.empathic_voice import ToolCallMessage, ToolResponseMessage
from typing import Optional

async def handle_tool_call(self, message: ToolCallMessage) -> Optional[ToolResponseMessage]:
    # Extract the tool name and ID from the message
    tool_name = message.name
    tool_call_id = message.tool_call_id
    
    # 1. Parse the parameters from the Tool Call message
    tool_parameters = json.loads(message.parameters)

    if tool_name == "get_current_weather":
        # 2. Extract the individual arguments
        obtained_location = tool_parameters.get('location')
        obtained_format = tool_parameters.get('format', 'text')

        if obtained_location:
            # 3. Call fetch weather function with extracted arguments
            weather = await fetch_weather(location=obtained_location, format=obtained_format)
            
            # ...etc.
```

### Send function call result

Upon receiving the return value of your function, we will send a [Tool Response](/reference/speech-to-speech-evi/chat#send.ToolResponseMessage.content) message containing the result. The specified `tool_call_id` must match the one received in
the [Tool Call](/reference/speech-to-speech-evi/chat#receive.ToolCallMessage.tool_call_id) message from EVI:

#### TypeScript

```ts
import { Hume } from 'hume';

async function handleToolCallMessage(
  toolCallMessage: Hume.empathicVoice.ToolCallMessage,
  socket: Hume.empathicVoice.chat.ChatSocket): Promise<void> {
  if (toolCallMessage.name === "get_current_weather") {
    // 1. Parse the parameters from the Tool Call message
    const args = JSON.parse(toolCallMessage.parameters) as {
      location: string;
      format: string;
    };
    // 2. Extract the individual arguments
    const { location, format } = args;
    // 3. Call fetch weather function with extracted arguments
    const weather = await fetchWeather(location, format);
    // 4. Construct a Tool Response message containing the result
    const toolResponseMessage = {
      type: "tool_response",
      toolCallId: toolCallMessage.toolCallId,
      content: weather,
    };
    // 5. Send Tool Response message to the WebSocket
    socket.sendToolResponseMessage(toolResponseMessage);
  }
}
```

#### Python

```python
import asyncio
from hume.client import AsyncHumeClient
from hume.empathic_voice import ToolCallMessage, ToolResponseMessage
from typing import Optional

async def handle_tool_call(self, message: ToolCallMessage) -> Optional[ToolResponseMessage]:
    # Extract the tool name and ID from the message
    tool_name = message.name
    tool_call_id = message.tool_call_id
    
    # 1. Parse the parameters from the Tool Call message
    tool_parameters = json.loads(message.parameters)

    if tool_name == "get_current_weather":
        # 2. Extract the individual arguments
        obtained_location = tool_parameters.get('location')
        obtained_format = tool_parameters.get('format', 'text')

        if obtained_location:
            # 3. Call fetch weather function with extracted arguments
            weather = await fetch_weather(location=obtained_location, format=obtained_format)
            
            if not weather.startswith("ERROR"):
                # 4. Construct a Tool Response message containing the result
                resp = ToolResponseMessage(
                    tool_call_id=tool_call_id,
                    content=weather
                )
                # 5. Send Tool Response message to the WebSocket
                await self.socket.send_tool_response(resp)
                print(f"(Sent ToolResponseMessage for tool_call_id {tool_call_id}: {weather})\n")
                return resp

    # Return None if the tool is not recognized or if there's an error
    return None
```

Let's try it in the [EVI Playground](https://app.hume.ai/evi/playground). Enter the return value of your function in the input field below the **Tool Call** message, and click **Send Response**. In practice, you will use the actual return value from your function call. However, for demonstration purposes, we will assume a return value of "75F".

![Send EVI function result](/_fern-img/70002b408e9aa128f892240dc3b6ce926a7bc829c4ee103fa27ab918abd2c1ae.webp)

### EVI responds

After the interface receives the [Tool Response](/reference/speech-to-speech-evi/chat#send.ToolResponseMessage.content) message, it will then send an [Assistant Message](/reference/speech-to-speech-evi/chat#receive.AssistantMessage.message) containing the response generated from the reported result of the function call.

#### Sample assistant\_message

```json
{
  "type": "assistant_message",
  "message": {
    "role": "assistant",
    "content": "The current temperature in New York, NY is 75F."
  }
}
```

See how it works in the [EVI Playground](https://app.hume.ai/evi/playground).

![EVI responds with function call result](/_fern-img/483cfe2ff0da039aaf2a504e8838102abd737cc38c9cd33ce69ad9670c96ab11.webp)

To summarize, **Tool Call** serves as a programmatic tool for intelligently signaling when you should invoke your
function. EVI does not invoke the function for you. You will need to define a function, invoke the function, and pass
the return value of your function to EVI via a
[Tool Response](/reference/speech-to-speech-evi/chat#send.ToolResponseMessage.content) message.
EVI will generate a response based on the content of your message.

## Using built-in tools

User-defined tools allow EVI to identify when a function should be invoked, but you will need to invoke the function
itself. On the other hand, Hume also provides built-in tools that are natively integrated. This means that you don't
need to define the function; EVI handles both determining when the function needs to be called and invoking it.

Hume supports the following built-in tools:

* **web\_search:** Enables EVI to search the web for real-time information when needed.
* **hang\_up:** Closes the WebSocket connection with status code `1000` when appropriate (e.g., after detecting a
  farewell, signaling the end of the conversation).

This section explains how to specify built-in tools in your configurations and details the message flow you can expect
when EVI uses a built-in tool during a chat session.

### Specify built-in tool in EVI configuration

Let's begin by creating a configuration which includes the built-in web search tool. To specify the web search tool in
your EVI configuration, during the **Add tools** step,
ensure **Web search** is enabled. Refer to our
[Configuration Guide](/docs/speech-to-speech-evi/configuration/build-a-configuration#create-a-configuration) for more details on
creating a configuration.

![Create a configuration with a built-in web search tool](/_fern-img/b1889354cf78c9f78cbae1a7721296f3eca74eb184124648db20b74233a1846c.webp)

Alternatively, you can specify the built-in tool by making a POST request to
[/configs](/reference/speech-to-speech-evi/configs/create-config) with the following request body:

#### Request body

```json
{
  "name": "Web Search Config",
  "language_model": {
    "model_provider": "OPEN_AI",
    "model_resource": "gpt-5-mini"
  },
  "builtin_tools": [
    { 
      "name": "web_search",
      "fallback_content": "Optional fallback content to inform EVI’s spoken response if web search is not successful."
    }
  ]
}
```

Upon success, expect EVI to return a response similar to this example:

#### Sample response body

```json
{
  "id": "3a60e85c-d04f-4eb5-8076-fb4bd344d5d0",
  "version": 0,
  "version_description": null,
  "name": "Web Search Config",
  "created_on": 1714421925626,
  "modified_on": 1714421925626,
  "prompt": null,
  "voice": null,
  "language_model": {
    "model_provider": "OPEN_AI",
    "model_resource": "gpt-5-mini",
    "temperature": null
  },
  "tools": [],
  "builtin_tools": [
    {
      "tool_type": "BUILTIN",
      "name": "web_search",
      "fallback_content": "Optional fallback content to inform EVI’s spoken response if web search is not successful."
    }
  ]
}
```

### EVI uses built-in tool

Now that we've created an EVI configuration which includes the built-in web search tool, let's test it out in the [EVI Playground](https://app.hume.ai/evi/playground).
Try asking EVI a question that requires web search, like "what is the latest news with AI research?"

![Ask EVI what is the latest news with AI research](/_fern-img/203d641abe42f4948e3a45a6e91a371e72f3966528c6179738b864398e970e74.webp)

EVI will send a response generated from the web search results:

![EVI sends a response generated from web search results](/_fern-img/7ccdfd5c27cd4ffaa669f3f3469f24ee1daf3e7fbc01fc693ec5d0f733b6bbe4.webp)

Let's review the message flow for when web search is invoked.

#### Web search message flow

```json
// 1. User asks EVI for the latest news in AI research
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "What is the latest news with AI research?"
  },
  // ...etc
}
// 2. EVI infers it needs to use web search, generates a search query, and invokes Hume's native web search function
{
  "name": "web_search", 
  "parameters": "{\"query\":\"latest news AI research\"}", 
  "tool_call_id": "call_zt1NYGpPkhR7v4kb4RPxTkLn", 
  "type": "tool_call", 
  "tool_type": "builtin", 
  "response_required": false
}
// 3. EVI sends back the web search results 
{
  "type": "tool_response", 
  "tool_call_id": "call_zt1NYGpPkhR7v4kb4RPxTkLn", 
  "content": "{ \”summary\”:null, “references”: [{\”content\”:\”Researchers have demonstrated a new method...etc.\”, \”url\”:\”https://www.sciencedaily.com/news/computers_math/artificial_intelligence/\”, \”name\”:\”Artificial Intelligence News -- ScienceDaily\”}] }", 
  "tool_name": "web_search", 
  "tool_type": "builtin"
}
// 4. EVI sends a response generated from the web search results
{
  "type": "assistant_message", 
  "message": {
    "role": "assistant", 
    "content": "Oh, there's some interesting stuff happening in AI research right now."
  },
  // ...etc
}
{
  "type": "assistant_message", 
  "message": {
    "role": "assistant", 
    "content": "Just a few hours ago, researchers demonstrated a new method using AI and computer simulations to train robotic exoskeletons."
  },
  // ...etc
}
```

## Interruptibility

Function calls can be interrupted to cancel them or to resend them with updated parameters.

### Canceling a function call

Just as EVI is able to infer when to make a function call, it can also infer from the user's input when to cancel one.
Here is an overview of what the message flow would look like:

![User signals they want to cancel a function call](/_fern-img/8de8e5b9e163cbbba5005c64b702b0e6c1260c0f913e75fc8c5c3ddd74f5e7c5.webp)![EVI infers from user input to cancel function call](/_fern-img/2d4eb18862f63eab990b52fd1c6be36e92ca5760f40fdf7c56a3006646f98fbf.webp)

#### Cancel function call message flow

```json
// 1. User asks what the weather is in New York
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "What's the weather in New York?"
  },
  // ...etc
}
// 2. EVI infers it is time to make a function call
{
  "type": "tool_call",
  "tool_type": "function",
  "response_required": true,
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "name": "get_current_weather",
  "parameters": "{\"location\":\"New York\",\"format\":\"fahrenheit\"}"
}
// 3. User communicates sudden disinterested in the weather
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "Actually, never mind."
  }
}
// 4. EVI infers the function call should be canceled
{
    "type": "assistant_message",
    "message": {
      "role": "assistant",
      "content": "If you change your mind or need any weather information in the future, feel free to let me know."
    },
    // ...etc
  }
```

### Updating a function call

Sometimes we don't necessarily want to cancel the function call, and instead want to update the parameters. EVI can
infer the difference. Below is a sample flow of interrupting the interface to update the parameters of the function call:

![User asks EVI the weather in New York](/_fern-img/1626c872bf8b235bdc02531b8a05c5f70948c00586d0d692d9791f9464015fd3.webp)![EVI updates function call to get weather in Los Angeles](/_fern-img/a544d2ff09a77252a09b651cc2ef23c15af2f94fec78d410922cf7029c2ce96d.webp)

#### Update function call message flow

```json
// 1. User asks EVI what the weather is in New York
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "What's the weather in New York?"
  },
  // ...etc
}
// 2. EVI infers it is time to make a function call
{
  "type": "tool_call",
  "tool_type": "function",
  "response_required": true,
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "name": "get_current_weather",
  "parameters": "{\"location\":\"New York\",\"format\":\"fahrenheit\"}"
}
// 3. User communicates to EVI they want the weather in Los Angeles instead
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "Actually, Los Angeles."
  }
}
// 4. EVI infers the parameters to function call should be updated
{
  "type": "tool_call",
  "response_required": true,
  "tool_call_id": "call_5RWLt3IMQyayzGdvMQVn5AOQ",
  "name": "get_current_weather",
  "parameters": "{\"location\":\"Los Angeles\",\"format\":\"celsius\"}"
}
// 5. User sends results of function call to EVI
{
  "type": "tool_response",
  "tool_call_id":"call_5RWLt3IMQyayzGdvMQVn5AOQ",
  "content":"72F"
}
// 6. EVI sends response container function call result
{
  "type": "assistant_message",
  "message": {
    "role": "assistant",
    "content": "The current weather in Los Angeles is 72F."
  },
  // ...etc
}
```

## Handling errors

It's possible for tool use to fail. For example, it can fail if the
[Tool Response](/reference/speech-to-speech-evi/chat#send.ToolResponseMessage.content) message
content was not in UTF-8 format or if the function call response timed out. This section outlines how to specify
fallback content to be used by EVI to communicate a failure, as well as the message flow for when a function call failure
occurs.

### Specifying fallback content

When defining your Tool, you can specify fallback content within the Tool's `fallback_content` field. When the Tool
fails to generate content, the text in this field will be sent to the LLM in place of a result. To accomplish this,
let's update the Tool we created during setup to include fallback content. We can accomplish this by publishing a new
version of the Tool via a POST request to [/tools/\{id}](/reference/speech-to-speech-evi/tools/create-tool-version):

#### Request body

```json
{
  "version_description": "Adds fallback content",
  "description": "This tool is for getting the current weather.",
  "parameters": "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }",
  "fallback_content": "Something went wrong. Failed to get the weather."
}
```

#### Sample response body

```json
{
  "tool_type": "FUNCTION",
  "id": "36f09fdc-4630-40c0-8afa-6a3bdc4eb4b1",
  "version": 1,
  "version_type": "FIXED",
  "version_description": "Adds fallback content",
  "name": "get_current_weather",
  "created_on": 1714421925626,
  "modified_on": 1714425632084,
  "fallback_content": "Something went wrong. Failed to get the weather.",
  "description": null,
  "parameters": "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the user's location.\" } }, \"required\": [\"location\", \"format\"] }"
}
```

### Failure message flow

This section outlines the sort of messages that can be expected when Tool use fails. After sending a **Tool Response**
message, we will know an error, or failure, occurred when we receive the
[Tool Error](/reference/speech-to-speech-evi/chat#receive.ToolErrorMessage) message:

#### Bad function call response error flow

```json
// 1. User asks EVI what the weather is in New York
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "What's the weather in New York?"
  },
  // ...etc
}
// 2. EVI infers it is time to make a function call
{
  "type": "tool_call",
  "tool_type": "function",
  "response_required": true,
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "name": "get_current_weather",
  "parameters": "{\"location\":\"New York\",\"format\":\"fahrenheit\"}"
}
// 3. User sends results of function call to EVI (result not formatted correctly)
{
  "type": "tool_response",
  "tool_call_id":"call_5RWLt3IMQyayzGdvMQVn5AOQ",
  "content":"MALFORMED RESPONSE"
}
// 4. EVI sends response communicating it failed to process the tool_response
{
  "type": "tool_error",
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "error": "Malformed tool response: <error message here>",
  "fallback_content": "Something went wrong. Failed to get the weather.",
  "level": "warn"
}
// 5. EVI generates a response based on the failure
{
  "type": "assistant_message",
  "message": {
    "role": "assistant",
    "content": "It looks like there was an issue retrieving the weather information for New York."
  },
  // ...etc
}
```

Let's cover another type of failure scenario: what if the weather API the function was using was down? In this case,
we would send EVI a [Tool Error](/reference/speech-to-speech-evi/chat#send.ToolErrorMessage)
message. When sending the **Tool Error** message, we can specify `fallback_content` to be more specific to the error
our function throws. This is what the message flow would be for this type of failure:

#### Failed function call flow

```json
// 1. User asks EVI what the weather is in New York
{
  "type": "user_message",
  "message": {
    "role": "user",
    "content": "What's the weather in New York?"
  },
  // ...etc
}
// 2. EVI infers it is time to make a function call
{
  "type": "tool_call",
  "tool_type": "function",
  "response_required": true,
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "name": "get_current_weather",
  "parameters": "{\"location\":\"New York\",\"format\":\"fahrenheit\"}"
}
// 3. Function failed, so we send EVI a message communicating the failure on our end
{
  "type": "tool_error",
  "tool_call_id": "call_m7PTzGxrD0i9oCHiquKIaibo",
  "error": "Malformed tool response: <error message here>",
  "fallback_content": "Function execution failure - weather API down.",
  "level": "warn"
}
// 4. EVI generates a response based on the failure
{
  "type": "assistant_message",
  "message": {
    "role": "assistant",
    "content": "Sorry, our weather resource is unavailable. Can I help with anything else?"
  },
  // ...etc
}
```

Let's revisit our function for handling **Tool Call** messages from the
[Function Calling](/docs/speech-to-speech-evi/features/tool-use#function-calling) section. We can now add support for
error handling by sending **Tool Error** messages to EVI. This will enable our function to handle cases where
fetching the weather fails or the requested tool is not found:

#### TypeScript

```ts
import { Hume } from 'hume';

async function handleToolCallMessage(
  toolCallMessage: Hume.empathicVoice.ToolCallMessage,
  socket: Hume.empathicVoice.chat.ChatSocket): Promise<void> {
  if (toolCallMessage.name === "get_current_weather") {
    try{
      // parse the parameters from the Tool Call message
      const args = JSON.parse(toolCallMessage.parameters) as {
        location: string;
        format: string;
      };
      // extract the individual arguments
      const { location, format } = args;
      // call fetch weather function with extracted arguments
      const weather = await fetchWeather(location, format);
      // send Tool Response message to the WebSocket
      const toolResponseMessage = {
        type: "tool_response",
        toolCallId: toolCallMessage.toolCallId,
        content: weather,
      };
      socket.sendToolResponseMessage(toolResponseMessage);
    } catch (error) {
      // send Tool Error message if weather fetching fails
      const weatherToolErrorMessage = {
        type: "tool_error",
        toolCallId: toolCallMessage.toolCallId,
        error: "Weather tool error",
        content: "There was an error with the weather tool",
      };
      socket.sendToolErrorMessage(weatherToolErrorMessage);
    }
  } else {
    // send Tool Error message if the requested tool was not found
    const toolNotFoundErrorMessage = {
      type: "tool_error",
      toolCallId: toolCallMessage.toolCallId,
      error: "Tool not found",
      content: "The tool you requested was not found",
    };
    socket.sendToolErrorMessage(toolNotFoundErrorMessage);
  }
}
```

#### Python

```python
import asyncio
from hume.client import AsyncHumeClient
from hume.empathic_voice import ToolCallMessage, ToolErrorMessage, ToolResponseMessage
from typing import Union

async def handle_tool_call(self, message: ToolCallMessage) -> Union[ToolCallMessage, ToolErrorMessage]:
    # Obtain the name, ID, and parameters of the tool call
    tool_name = message.name
    tool_call_id = message.tool_call_id

    # Parse the stringified JSON parameters into a dictionary
    try:
        tool_parameters = json.loads(message.parameters)
    except json.JSONDecodeError:
        resp = ToolErrorMessage(
            tool_call_id=tool_call_id,
            content="Invalid parameters format.",
            error="JSONDecodeError"
        )
        await self.socket.send_tool_error(resp)
        print(f"(Sent ToolErrorMessage for tool_call_id {tool_call_id} due to JSON decode error.)\n")
        return

    if tool_name == "get_current_weather":
        obtained_location = tool_parameters.get('location')
        obtained_format = tool_parameters.get('format', 'text')

        if not obtained_location:
            resp = ToolErrorMessage(
                tool_call_id=tool_call_id,
                content="Missing 'location' parameter.",
                error="MissingParameter"
            )
            await self.socket.send_tool_error(resp)
            print(f"(Sent ToolErrorMessage for tool_call_id {tool_call_id} due to missing location parameter.)\n")
            return

        weather = await fetch_weather(location=obtained_location, format=obtained_format)

        if weather.startswith("ERROR"):
            resp = ToolErrorMessage(
                tool_call_id=tool_call_id,
                content=weather,
                error="WeatherFetchError"
            )
            await self.socket.send_tool_error(resp)
            print(f"(Sent ToolErrorMessage for tool_call_id {tool_call_id}: {weather})\n")
        else:
            resp = ToolResponseMessage(
                tool_call_id=tool_call_id,
                content=weather
            )
            await self.socket.send_tool_response(resp)
            print(f"(Sent ToolResponseMessage for tool_call_id {tool_call_id}: {weather})\n")
```

---