-
Notifications
You must be signed in to change notification settings - Fork 118
add template for multi_tool_use #299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
khai-meetkai
wants to merge
5
commits into
main
Choose a base branch
from
add_multi_tool_use_template
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
functionary/prompt_template/llama31_multi_tool_use_template.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| import datetime | ||
| import json | ||
| import re | ||
| from typing import Any, Dict, List, Literal, Optional, Tuple, Union | ||
|
|
||
| from functionary.openai_types import Function, Tool | ||
| from functionary.prompt_template import prompt_utils | ||
| from functionary.prompt_template.llama31_prompt_template import Llama31Template | ||
|
|
||
|
|
||
| def return_multi_tool_use(): | ||
| return { | ||
| "type": "function", | ||
| "function": { | ||
| "name": "multi_tool_use", | ||
| "description": "This tool serves as a wrapper for utilizing multiple tools. Each tool that can be used must be specified in the tool sections.\nEnsure that the parameters provided to each tool are valid according to that tool's specification.\nUse this function to run multiple tools simultaneously, but only if they can operate in parallel.", | ||
| "parameters": { | ||
| "type": "object", | ||
| "properties": { | ||
| "tool_uses": { | ||
| "type": "array", | ||
| "description": "The tools to be executed in parallel. NOTE: only functions tools are permitted", | ||
| "items": { | ||
| "type": "object", | ||
| "properties": { | ||
| "recipient_name": { | ||
| "type": "string", | ||
| "description": "The name of the tool to use. The format should either be just the name of the tool, or in the format namespace.function_name for plugin and function tools.", | ||
| }, | ||
| "parameters": { | ||
| "type": "object", | ||
| "additionalProperties": True, | ||
| "description": "The parameters to pass to the tool. Ensure these are valid according to the tool's own specifications.", | ||
| }, | ||
| }, | ||
| "required": ["recipient_name", "parameters"], | ||
| }, | ||
| }, | ||
| }, | ||
| "required": ["tool_uses"], | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| def merge_tool_calls(tool_calls: list[dict]) -> dict: | ||
| tool_uses = [] | ||
| for tool_call in tool_calls: | ||
| tool_uses.append( | ||
| { | ||
| "recipient_name": tool_call["function"]["name"], | ||
| "parameters": json.loads(tool_call["function"]["arguments"]), | ||
| } | ||
| ) | ||
| return { | ||
| "type": "function", | ||
| "function": { | ||
| "name": "multi_tool_use", | ||
| "arguments": json.dumps({"tool_uses": tool_uses}, ensure_ascii=False), | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| def convert_parallel_to_multi_tool_use_example( | ||
| messages: List[Dict], tools: List[Dict] | ||
| ) -> Tuple[List[Dict], List[Dict]]: | ||
| # add multi_tool_use tool | ||
| all_tools = tools + [return_multi_tool_use()] | ||
| merged_messages = [] | ||
| for message in messages: | ||
| tool_calls = message.get("tool_calls", []) or [] | ||
| if len(tool_calls) > 0: | ||
| if len(tool_calls) > 1: | ||
| # print("mesage 0: ", messages[0]["content"]) | ||
| merged_tool_call = merge_tool_calls(tool_calls) | ||
| merged_messages.append( | ||
| { | ||
| "role": "assistant", | ||
| "content": message.get("content", None), | ||
| "tool_calls": [merged_tool_call], | ||
| } | ||
| ) | ||
| else: | ||
| merged_messages.append(message) | ||
| else: | ||
| merged_messages.append(message) | ||
|
|
||
| return all_tools, merged_messages | ||
|
|
||
|
|
||
| class MultiToolUseLlama31Template(Llama31Template): | ||
| version = "v3-llama3.1-multi-tool-use" | ||
|
|
||
| def get_prompt_from_messages( | ||
| self, | ||
| messages: List[Dict], | ||
| tools_or_functions: Optional[List[Dict]] = None, | ||
| bos_token: Optional[str] = "", | ||
| add_generation_prompt: bool = False, | ||
| ) -> str: | ||
| """This function is used to get the complete prompt for list of messages | ||
|
|
||
| Args: | ||
| messages (List[Dict]): List of messages | ||
| tools_or_functions (Optional[List[Dict]], optional): List of tools or functions. Defaults to None. | ||
|
|
||
| Returns: | ||
| str: the prompt for inference/training | ||
| """ | ||
| if not tools_or_functions: | ||
| all_tools, merged_messages = [], messages | ||
| else: | ||
| all_tools, merged_messages = convert_parallel_to_multi_tool_use_example( | ||
| messages, tools_or_functions | ||
| ) | ||
| return super().get_prompt_from_messages( | ||
| merged_messages, all_tools, bos_token, add_generation_prompt | ||
| ) | ||
|
|
||
| def parse_assistant_response( | ||
| self, llm_output: str, tool_choice: Any = None | ||
| ) -> Dict: | ||
| assistant_response = super().parse_assistant_response(llm_output, tool_choice) | ||
| tool_calls = assistant_response.get("tool_calls", []) | ||
| n_tool_calls = [] | ||
| if tool_calls: | ||
| for tool_call in tool_calls: | ||
| if tool_call["function"]["name"] == "multi_tool_use": | ||
| sub_tool_calls = [] | ||
| tool_use_list = json.loads(tool_call["function"]["arguments"]) | ||
| for tool_use in tool_use_list: | ||
| sub_tool_calls.append( | ||
| { | ||
| "id": prompt_utils.get_random_tool_call_id(), | ||
| "type": "function", | ||
| "function": { | ||
| "name": tool_use["recipient_name"], | ||
| "arguments": json.dumps( | ||
| tool_use["parameters"], ensure_ascii=False | ||
| ), | ||
| }, | ||
| } | ||
| ) | ||
| n_tool_calls.extend(sub_tool_calls) | ||
| else: | ||
| n_tool_calls.append(tool_call) | ||
| return { | ||
| "role": "assistant", | ||
| "content": assistant_response.get("content", None), | ||
| "tool_calls": n_tool_calls, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| <|start_header_id|>system<|end_header_id|> | ||
|
|
||
| Environment: ipython | ||
|
|
||
| Cutting Knowledge Date: December 2023 | ||
|
|
||
|
|
||
| You have access to the following functions: | ||
|
|
||
| Use the function 'get_car_price' to 'Get the price of a particular car model' | ||
| {"name": "get_car_price", "description": "Get the price of a particular car model", "parameters": {"type": "object", "properties": {"car_name": {"type": "string", "description": "The name of the car model"}}, "required": ["car_name"]}} | ||
|
|
||
| Use the function 'get_weather' to 'This function's purpose is to get the weather of a location' | ||
| {"name": "get_weather", "description": "This function's purpose is to get the weather of a location", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "where to get weather"}}, "required": ["location"]}} | ||
|
|
||
| Use the function 'multi_tool_use' to 'This tool serves as a wrapper for utilizing multiple tools. Each tool that can be used must be specified in the tool sections. | ||
| Ensure that the parameters provided to each tool are valid according to that tool's specification. | ||
| Use this function to run multiple tools simultaneously, but only if they can operate in parallel.' | ||
| {"name": "multi_tool_use", "description": "This tool serves as a wrapper for utilizing multiple tools. Each tool that can be used must be specified in the tool sections.\nEnsure that the parameters provided to each tool are valid according to that tool's specification.\nUse this function to run multiple tools simultaneously, but only if they can operate in parallel.", "parameters": {"type": "object", "properties": {"tool_uses": {"type": "array", "description": "The tools to be executed in parallel. NOTE: only functions tools are permitted", "items": {"type": "object", "properties": {"recipient_name": {"type": "string", "description": "The name of the tool to use. The format should either be just the name of the tool, or in the format namespace.function_name for plugin and function tools."}, "parameters": {"type": "object", "additionalProperties": true, "description": "The parameters to pass to the tool. Ensure these are valid according to the tool's own specifications."}}, "required": ["recipient_name", "parameters"]}}}, "required": ["tool_uses"]}} | ||
|
|
||
|
|
||
| Think very carefully before calling functions. | ||
| If a you choose to call a function ONLY reply in the following format: | ||
| <{start_tag}={function_name}>{parameters}{end_tag} | ||
| where | ||
|
|
||
| start_tag => `<function` | ||
| parameters => a JSON dict with the function argument name as key and function argument value as value. | ||
| end_tag => `</function>` | ||
|
|
||
| Here is an example, | ||
| <function=example_function_name>{"example_name": "example_value"}</function> | ||
|
|
||
| Reminder: | ||
| - If looking for real time information use relevant functions before falling back to brave_search | ||
khai-meetkai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Function calls MUST follow the specified format, start with <function= and end with </function> | ||
| - Required parameters MUST be specified | ||
| - Only call one function at a time | ||
| - Put the entire function call reply on one line | ||
|
|
||
| <|eot_id|><|start_header_id|>user<|end_header_id|> | ||
|
|
||
| who is the CEO of Meetkai<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| James Kaplan is the Co-Founder and CEO of MeetKai Inc.<|eot_id|><|start_header_id|>user<|end_header_id|> | ||
|
|
||
| is the car Song more expensive than car Tang?<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| I will get the price of 2 cars and compare<function=multi_tool_use>{"tool_uses": [{"recipient_name": "get_car_price", "parameters": {"car_name": "Song"}}, {"recipient_name": "get_car_price", "parameters": {"car_name": "Tang"}}]}</function><|eom_id|><|start_header_id|>ipython<|end_header_id|> | ||
|
|
||
| {"price": {"price": "$25000"}}<|eot_id|><|start_header_id|>ipython<|end_header_id|> | ||
|
|
||
| {"price": {"price": "$20000"}}<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| No, the car Tang is less expensive than the car Song. The car Song is priced at $25,000, while the car Tang is priced at $20,000.<|eot_id|><|start_header_id|>user<|end_header_id|> | ||
|
|
||
| what's the weather like in Hanoi?<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| <function=get_weather>{"location": "Hanoi"}</function><|eom_id|><|start_header_id|>ipython<|end_header_id|> | ||
|
|
||
| {"result": {"temperature": 10}}<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| The temperature in Hanoi is: 10 degree Celcious<|eot_id|><|start_header_id|>user<|end_header_id|> | ||
|
|
||
| Given the list of strings: '0','1','2','3','4','5' remove the number in the list that is close to 3.6 the most<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| I'll use code interpreter to handle this<|python_tag|>l=[int('0'),int('1'),int('2'),int('3'),int('4'),int('5')] | ||
| l.remove(3.6)<|eom_id|><|start_header_id|>ipython<|end_header_id|> | ||
|
|
||
| ValueError: list.remove(x): x not in list<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| I will fix the code<|python_tag|>l.remove(round(3.6)) | ||
| l<|eom_id|><|start_header_id|>ipython<|end_header_id|> | ||
|
|
||
| [0,1,2,3,5,]<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| The final list is: 0,1,2,3,5<|eot_id|><|start_header_id|>user<|end_header_id|> | ||
|
|
||
| Thanks! What's the weather in San Francisco?<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| <function=get_weather>{"location": "San Francisco, CA"}</function><|eom_id|><|start_header_id|>ipython<|end_header_id|> | ||
|
|
||
| {"result": {"temperature": 20}}<|eot_id|><|start_header_id|>assistant<|end_header_id|> | ||
|
|
||
| The temperature in San Francisco is: 20 degree Celcious<|eot_id|> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.