Views
No views yet
Llama-3.2-1B-Instruct model specific for Function Calling, to showcase how to fine tune a model on top of a dataset
like argilla/Synth-APIGen-v0.1.1from typing import Optional
2import re
3import json
4
5from jinja2 import Template
6
7SYSTEM_PROMPT = """
8You are an expert in composing functions. You are given a question and a set of possible functions.
9Based on the question, you will need to make one or more function/tool calls to achieve the purpose.
10If none of the functions can be used, point it out and refuse to answer.
11If the given question lacks the parameters required by the function, also point it out.
12
13The output MUST strictly adhere to the following format, and NO other text MUST be included.
14The example format is as follows. Please make sure the parameter type is correct. If no function call is needed, please make the tool calls an empty list '[]'.
15```
16<tool_call>[
17{"name": "func_name1", "arguments": {"argument1": "value1", "argument2": "value2"}},
18... (more tool calls as required)
19]</tool_call>
20```
21""".strip()
22
23prompt = Template("""
24You have access to the following tools:
25<tools>{{ tools }}</tools>
26
27Please answer the following query:
28{{ query }}
29""".lstrip())
30
31def prepare_messages(
32 query: str,
33 tools: Optional[dict[str, any]] = None,
34 conversation_history: Optional[list[dict[str, str]]] = None
35) -> list[dict[str, str]]:
36 """Prepare the system and user messages for the given query and tools.
37
38 Args:
39 query: The query to be answered.
40 tools: The tools available to the user. Defaults to None, in which case if a
41 list without content will be passed to the model.
42 conversation_history: Exchange of messages, including the system_prompt from
43 the first query. Defaults to None, the first message in a conversation.
44 """
45 if tools is None:
46 tools = []
47
48 if conversation_history:
49 messages = conversation_history.copy()
50 messages.append({"role": "user", "content": query})
51 else:
52 messages = [
53 {"role": "system", "content": system_prompt},
54 {"role": "user", "content": prompt.render(tools=json.dumps(tools), query=query)}
55 ]
56
57 return messages
58
59
60def parse_response(text: str) -> str | dict[str, any]:
61 """Parses a response from the model, returning either the
62 parsed list with the tool calls parsed, or the
63 model thought or response if couldn't generate one.
64
65 Args:
66 text: Response from the model.
67 """
68 pattern = r"<tool_call>(.*?)</tool_call>"
69 matches = re.findall(pattern, text, re.DOTALL)
70 if matches:
71 return json.loads(matches[0])
72 return text
73 1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3
4model_name = "argilla-warehouse/Llama-3.2-1B-Instruct-APIGen-FC-v0.1"
5model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", torch_dtype="auto", trust_remote_code=True)
6tokenizer = AutoTokenizer.from_pretrained(model_name)
7
8
9get_weather_api = {
10 "name": "get_weather",
11 "description": "Get the current weather for a location",
12 "parameters": {
13 "type": "object",
14 "properties": {
15 "location": {
16 "type": "string",
17 "description": "The city and state, e.g. San Francisco, New York"
18 },
19 "unit": {
20 "type": "string",
21 "enum": ["celsius", "fahrenheit"],
22 "description": "The unit of temperature to return"
23 }
24 },
25 "required": ["location"]
26 }
27}
28
29search_api = {
30 "name": "search",
31 "description": "Search for information on the internet",
32 "parameters": {
33 "type": "object",
34 "properties": {
35 "query": {
36 "type": "string",
37 "description": "The search query, e.g. 'latest news on AI'"
38 }
39 },
40 "required": ["query"]
41 }
42}
43
44available_tools = [get_weather_api, search_api]
45
46query = "What's the weather like in New York in fahrenheit?"
47
48messages = prepare_messages(query, tools=available_tools)
49
50inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
51
52outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
53result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=False)
54
55response = parse_response(result)
56# [{'name': 'get_weather', 'arguments': {'location': 'New York', 'unit': 'fahrenheit'}}]Parallel function call1available_tools = [{"name": "spotify.play", "description": "Play specific tracks from a given artist for a specific time duration.", "parameters": {"type": "dict", "properties": {"artist": {"type": "string", "description": "The artist whose songs you want to play."}, "duration": {"type": "integer", "description": "The duration for which the songs should be played, in minutes."}}, "required": ["artist", "duration"]}}]
2query = "Play songs from the artists Taylor Swift and Maroon 5, with a play time of 20 minutes and 15 minutes respectively, on Spotify."
3
4messages = prepare_messages(query, tools=available_tools)
5
6inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
7outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
8result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=False)
9
10response = parse_response(result)
11# [{'name': 'spotify.play', 'arguments': {'artist': 'Taylor Swift', 'duration': 20}}, {'name': 'spotify.play', 'arguments': {'artist': 'Maroon 5', 'duration': 15}}]Multiple function call1available_tools = [{"name": "country_info.largest_city", "description": "Fetch the largest city of a specified country.", "parameters": {"type": "dict", "properties": {"country": {"type": "string", "description": "Name of the country."}}, "required": ["country"]}}, {"name": "country_info.capital", "description": "Fetch the capital city of a specified country.", "parameters": {"type": "dict", "properties": {"country": {"type": "string", "description": "Name of the country."}}, "required": ["country"]}}, {"name": "country_info.population", "description": "Fetch the current population of a specified country.", "parameters": {"type": "dict", "properties": {"country": {"type": "string", "description": "Name of the country."}}, "required": ["country"]}}]
2query = "What is the capital of Brazil?"
3
4messages = prepare_messages(query, tools=available_tools)
5
6inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
7outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
8result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=False)
9
10response = parse_response(result)
11# [{'name': 'country_info.capital', 'arguments': {'country': 'Brazil'}}]Parallel multiple function call1available_tools = [{"name": "math_toolkit.sum_of_multiples", "description": "Find the sum of all multiples of specified numbers within a specified range.", "parameters": {"type": "dict", "properties": {"lower_limit": {"type": "integer", "description": "The start of the range (inclusive)."}, "upper_limit": {"type": "integer", "description": "The end of the range (inclusive)."}, "multiples": {"type": "array", "items": {"type": "integer"}, "description": "The numbers to find multiples of."}}, "required": ["lower_limit", "upper_limit", "multiples"]}}, {"name": "math_toolkit.product_of_primes", "description": "Find the product of the first n prime numbers.", "parameters": {"type": "dict", "properties": {"count": {"type": "integer", "description": "The number of prime numbers to multiply together."}}, "required": ["count"]}}]
2query = "Find the sum of all the multiples of 3 and 5 between 1 and 1000. Also find the product of the first five prime numbers."
3
4messages = prepare_messages(query, tools=available_tools)
5
6inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
7outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
8result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=False)
9
10response = parse_response(result)
11# [{'name': 'math_toolkit.sum_of_multiples', 'arguments': {'lower_limit': 1, 'upper_limit': 1000, 'multiples': [3, 5]}}, {'name': 'math_toolkit.product_of_primes', 'arguments': {'count': 5}}]Multi-turn function call1
2get_weather_api = {
3 "name": "get_weather",
4 "description": "Get the current weather for a location",
5 "parameters": {
6 "type": "object",
7 "properties": {
8 "location": {
9 "type": "string",
10 "description": "The city and state, e.g. San Francisco, New York"
11 },
12 "unit": {
13 "type": "string",
14 "enum": ["celsius", "fahrenheit"],
15 "description": "The unit of temperature to return"
16 }
17 },
18 "required": ["location"]
19 }
20}
21
22available_tools = [get_weather_api]
23
24query = "What's the weather like in Madrid in celsius?"
25
26messages = prepare_messages(query, tools=available_tools)
27
28inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
29
30outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
31result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=False)
32
33response = parse_response(result)
34
35# 2nd turn
36conversation_history = messages.copy()
37conversation_history.append({"role": "assistant", "content": json.dumps(response)})
38
39new_query = "And in Edinburgh in celsius?"
40
41new_messages = prepare_messages(new_query, tools=available_tools, conversation_history=conversation_history)
42
43inputs = tokenizer.apply_chat_template(new_messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
44
45outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
46result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=False)
47
48response = parse_response(result)
49# [{'name': 'get_weather', 'arguments': {'location': 'Edinburgh', 'unit': 'celsius'}}]Irrelevance function call (examples when some data is missing)1available_tools = []
2
3query = "What's the weather like in New York in fahrenheit?"
4
5messages = prepare_messages(query, tools=available_tools)
6
7inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
8
9outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
10result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True)
11response = parse_response(result)
12# 'The query cannot be answered, no tools were provided.'1cut_number = {
2 'type': 'function',
3 'function': {
4 'name': 'cut_number',
5 'description': 'Returns the value `number` if it is greater than or equal to `threshold`, otherwise returns the value `threshold`.',
6 'parameters': {
7 'type': 'object',
8 'properties': {'number': {'type': 'number', 'description': 'The number to compare.'}},
9 'required': ['number']
10 }
11 }
12}
13
14available_tools = [cut_number]
15
16query = "What's the weather like in New York in fahrenheit?"
17
18messages = prepare_messages(query, tools=available_tools)
19inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
20
21outputs = model.generate(inputs, max_new_tokens=512, do_sample=False, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
22result = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True)
23response = parse_response(result)
24# "The query cannot be answered with the provided tools. The query lacks the parameters required by the function. Please provide the parameters, and I'll be happy to assist."accelerate command. It took 13 minutes in a node with 8xH100.1uv venv .venv --python 3.11
2source .venv/bin/activate
3git clone https://github.com/huggingface/trl.git
4uv pip install .
5uv pip install wandb
6uv pip install deepspeed1@misc{vonwerra2022trl,
2 title = {{TRL: Transformer Reinforcement Learning}},
3 author = {Leandro von Werra and Younes Belkada and Lewis Tunstall and Edward Beeching and Tristan Thrush and Nathan Lambert and Shengyi Huang and Kashif Rasul and Quentin Gallouédec},
4 year = 2020,
5 journal = {GitHub repository},
6 publisher = {GitHub},
7 howpublished = {\url{https://github.com/huggingface/trl}}
8}