import json
import os
import re
import requests
import sqlite3
from pathlib import Path
from transformers import AutoTokenizer
from llama_index.llms.openai_like import OpenAILike
from dotenv import load_dotenv
; load_dotenv()
This post illustrates how to use function calling of Mixtral-8x22B-Instruct-v0.1 to retrieve data from database in order to answer user’s questions.
Download sqlite3 sample database chinook
!wget https://www.sqlitetutorial.net/wp-content/uploads/2018/03/chinook.zip
--2024-05-29 18:03:16-- https://www.sqlitetutorial.net/wp-content/uploads/2018/03/chinook.zip
Resolving www.sqlitetutorial.net (www.sqlitetutorial.net)... 172.64.80.1, 2606:4700:130:436c:6f75:6466:6c61:7265
Connecting to www.sqlitetutorial.net (www.sqlitetutorial.net)|172.64.80.1|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 305596 (298K) [application/zip]
Saving to: ‘chinook.zip’
chinook.zip 100%[===================>] 298.43K --.-KB/s in 0.007s
2024-05-29 18:03:16 (44.4 MB/s) - ‘chinook.zip’ saved [305596/305596]
!unzip chinook.zip
Archive: chinook.zip
inflating: chinook.db
Setup model and tokenizer
The model is hosted using tabbyAPI locally. We’ll use “OpenAILike” from LlamaIndex to send requests to it.
def get_model_name():
= os.environ['BASE_URL']
BASE_URL = {
headers 'accept': 'application/json',
'x-api-key': os.environ['API_KEY']
}= requests.get(os.path.join(BASE_URL, 'model'), headers=headers).json()
res if 'id' not in res:
raise Exception('Model not loaded.')
return res['id']
= get_model_name()
model_name print(model_name)
= OpenAILike(
llm =model_name,
model=os.environ['BASE_URL'],
api_base=os.environ['API_KEY']
api_key
)= AutoTokenizer.from_pretrained('mistralai/Mixtral-8x22B-Instruct-v0.1') tokenizer
Mixtral-8x22B-Instruct-v0.1-exl2-4.0bpw
Setup function tools
#
# functions to be used as tools
#
def search_customer_support(customer_firstname: str, customer_lastname) -> list[tuple[str, str, int]]|None:
"""search customers table and return support representative id."""
= (
stmt "SELECT SupportRepid FROM customers "
f"WHERE FirstName LIKE '%{customer_firstname}%' and LastName LIKE '%{customer_lastname}%'"
)= conn.execute(stmt).fetchall()
res if len(res) == 0:
= None
res return res[0][0]
def search_employee(employee_id: int) -> list[tuple[str, str, str]]|None:
"""search employees table and return firstname, lastname and title."""
= (
stmt "SELECT FirstName, LastName, Title FROM employees "
f"WHERE Employeeid={employee_id}"
)= conn.execute(stmt).fetchall()
res if len(res) == 0:
= None
res return res[0]
Create tools for the model.
= {
name_fn_mappings 'search_customer_support': search_customer_support,
'search_employee': search_employee
}
= [
tools "type": "function",
{"function": {
"name":"search_customer_support",
"description": "Useful when you want to find out who provided support to a customer.",
"parameters": {
"type": "object",
"properties": {
"customer_firstname": {
"type": "string",
"description": "A customer's first name."},
"customer_lastname": {
"type": "string",
"description": "A customer's last name."}
},"required":["customer_firstname", "customer_lastname"]
}
}
},"type": "function",
{"function": {
"name":"search_employee",
"description": "Useful when you want to retrieve more information about an employee.",
"parameters": {
"type": "object",
"properties": {
"employee_id": {
"type": "integer",
"description": "employee's id"}
},"required":["employee_id"]
}
}
} ]
Run query with function calling
Define some required functions in order to run queries first.
def format_prompt(messages, tokenizer, use_tool=False, tools=None):
if use_tool:
if tools is None or len(tools)==0:
raise Exception('A list of tools is required for function calling.')
= tokenizer.apply_chat_template(
prompt
messages,='tool_use',
chat_template=json.dumps(tools),
tools=False,
tokenize=True)
add_generation_promptelse:
= tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
prompt return prompt
def call_tool(toolcall):
= name_fn_mappings[toolcall['name']]
fn = fn(**toolcall['arguments'])
fn_results return fn_results
def get_first_toolcall(response):
= re.findall('(\[\{\s*"name":.*\}\])+', response)
m if len(m) > 0:
= json.loads(m[0])
toolcalls return toolcalls[0]
else:
return None
def run_step(messages):
= format_prompt(messages, tokenizer, True, tools)
p = llm.complete(p, formatted=True).text.strip()
response return response, p
def ask_llm(messages):
while True:
= run_step(messages)
response, p = get_first_toolcall(response)
toolcall if toolcall:
= call_tool(toolcall)
fn_results
messages.append('role': 'tool_calls', 'content': json.dumps([toolcall], ensure_ascii=False)}
{
)
messages.append('role': 'tool_results', 'content': json.dumps({"content": fn_results}, ensure_ascii=False)}
{
)else:
return response, p
Ask the model questions
= sqlite3.connect('chinook.db') conn
=[
messages'role': 'user',
{'content': 'Get the firstname and lastname of the employee who provided customer support to Stanisław Wójcik.'}
]= ask_llm(messages)
response, prompt print(prompt)
response
<s>[AVAILABLE_TOOLS][{"type": "function", "function": {"name": "search_customer_support", "description": "Useful when you want to find out who provided support to a customer.", "parameters": {"type": "object", "properties": {"customer_firstname": {"type": "string", "description": "A customer's first name."}, "customer_lastname": {"type": "string", "description": "A customer's last name."}}, "required": ["customer_firstname", "customer_lastname"]}}}, {"type": "function", "function": {"name": "search_employee", "description": "Useful when you want to retrieve more information about an employee.", "parameters": {"type": "object", "properties": {"employee_id": {"type": "integer", "description": "employee's id"}}, "required": ["employee_id"]}}}][/AVAILABLE_TOOLS][INST]Get the firstname and lastname of the employee who provided customer support to Stanisław Wójcik.[/INST][TOOL_CALLS][{"name": "search_customer_support", "arguments": {"customer_firstname": "Stanisław", "customer_lastname": "Wójcik"}}]</s>[TOOL_RESULTS]{"content": 4}[/TOOL_RESULTS][TOOL_CALLS][{"name": "search_employee", "arguments": {"employee_id": 4}}]</s>[TOOL_RESULTS]{"content": ["Margaret", "Park", "Sales Support Agent"]}[/TOOL_RESULTS]
'The employee who provided customer support to Stanisław Wójcik is Margaret Park, a Sales Support Agent.'
conn.close()