AI Function Calling
When using AI assistants in Turn, you can define a list of custom functions that the model is able to call. Those functions need be exposed by you on an HTTP endpoint and Turn will call your endpoint whenever the AI assistant decides to call one of the functions you have defined.
The text output from the function call will be forwarded to the AI assistant and added to the context that it uses to generate its response.
For example, you can define a function called get_weather
and the AI assistant will call this function whenever the user wants to know the weather.
Function definition
You can define the list of available functions in the AI Settings page in Turn. Functions are defined as a JSON list of objects, where each object defines the properties of one function.
A function includes three primary parameters: name
, description
, and parameters
. The description parameter guides the model on when and how to invoke the function, so it is crucial to provide a clear and concise explanation of the function’s purpose.
The parameters parameter is a JSON schema object that defines the parameters the function can accept.
For example, the following JSON defines a function called get_weather
with a required argument location
and an optional argument unit
. As per the description, the function will return the current weather at the provided location.
[
{
"description": "Returns the current weather for the specified location",
"name": "get_weather",
"parameters": {
"properties": {
"location": {
"description": "The city and state e.g. San Francisco, CA",
"type": "string"
},
"unit": {
"enum": ["c", "f"],
"type": "string"
}
},
"required": ["location"],
"type": "object"
}
}
]
Function Calling HTTP endpoint
Turn will send a POST HTTP request to the API endpoint you specified in the AI Settings page whenever the AI assistant decides to call one of the functions that you defined. It is vital that your endpoint is quick in responding, otherwise the conversation will feel sluggish on the user's end. If your endpoint does not respond in less than 10 seconds, the request will time out and an empty output will be passed to the AI assistant.
The POST request from Turn will be contain an HMAC signature in its X-Turn-Hook-Signature
header, it is important that you verify this signature to ensure that the request was indeed send by Turn and its payload has not been tempered. Please refer to the example below for more instructions on how to verify the signature. The HMAC secret needed to compute the signature can be found in the AI settings page.
The body of the request will contain the following keys:
function_name
: The name of the function that the AI assistant wants to call.function_args
: The arguments the the AI assistant decided to pass to the function.context
: A context provided by Turn (and not by the AI assistant) which contains useful information about the Contact that is messaging the AI assistant. This must be used as the only source of truth when you need to identify the Contact and access its profile fields. The arguments provided by the AI assistant (function_args
) cannot be trusted for authentication purposes because AI assistants are prone to hallucinations and prompt injection attacks.tool_call_id
: The ID of the function call, which can be useful for debugging purposes.
Here is an example of the JSON body when the AI assistant decides to call a function as part of a Journey execution:
{
"function_name": "get_weather",
"function_args": {
"location": "San Francisco, CA"
},
"wa_id": "+16506001008",
"tool_call_id": "call_iIlPzTvI1DN00000yo0z5Rx",
"context": {
"context_type": "journey",
"journey_uuid": "b5ddb14c-1ecb-49dd-a54d-1c343364e9e7",
"contact": {
"birthday": null,
"first_message_received_at": "2024-04-30T09:18:12Z",
"is_blocked": false,
"language": null,
"last_message_received_at": "2024-05-29T08:14:41Z",
"last_message_sent_at": "2024-05-28T17:38:21.284541Z",
"last_seen_at": "2024-05-29T08:14:41Z",
"location": null,
"name": null,
"opted_in": false,
"opted_in_at": null,
"surname": null,
"whatsapp_id": "16506001008",
"whatsapp_profile_name": "Contact Name",
"your_custom_profile_field_1": "value 1",
"your_custom_profile_field_2": "value 2"
}
}
}
Python example
The following Python code shows how you can build an HTTP endpoint that handles the Function Calling requests from the AI assistant.
import base64
import hmac
from hashlib import sha256
from flask import Flask, request
app = Flask(__name__)
# Get this from Turn's AI settings page
TURN_HMAC_SECRET = b'PASTE-THE-SECRET-HERE'
@app.route('/')
def index():
return 'Hello from Flask!'
@app.route('/function-calling', methods=['POST'])
def function_calling():
# Extract signature from the request headers
request_signature = request.headers.get('X-Turn-Hook-Signature')
# Compute the signature of the request body
raw_request_body = request.get_data()
hmac_object = hmac.new(TURN_HMAC_SECRET, raw_request_body, sha256)
computed_signature = base64.b64encode(hmac_object.digest())
# Verify the signature
if computed_signature.decode('ascii') != request_signature:
print(f'Invalid signature: {request_signature}')
print(f'Computed signature is: {computed_signature}')
return 'Invalid signature', 403
# Fetch the request JSON body
request_body = request.get_json()
print(request_body)
# Extract function name, arguments and context
function_name = request_body.get('function_name')
args = request_body.get('function_args')
context = request_body.get('context')
print(
f'Function {function_name} was called with args {args} and context {context}'
)
# Handle the function call
match function_name:
case 'get_weather':
return {'output': get_weather(args, context)}
case 'some_other_function':
return {'output': some_other_function(args, context)}
case _:
return {'output': 'This function is not supported.'}
def get_weather(args, context):
location = args.get('location')
# Always fetch the WhatsApp ID of the Contact from the context,
# do not trust the args provided by the AI assistant to authenticate
# the user.
whatsapp_id = context.get('wa_id')
print('WhatsApp ID', whatsapp_id)
contact = context.get('contact', {})
name = contact.get('name')
print('Contact name', name)
return f'The weather for {location} is sunny and it\'s 22 degrees'
def some_other_function(args, context):
return 'Result from some other function'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)