diff --git a/docs/en/llm/api_server_tools.md b/docs/en/llm/api_server_tools.md
index 56fb1b598a..39e91dbf07 100644
--- a/docs/en/llm/api_server_tools.md
+++ b/docs/en/llm/api_server_tools.md
@@ -1,6 +1,6 @@
# Tools Calling
-LMDeploy supports tools for InternLM2, InternLM2.5 and llama3.1 models.
+LMDeploy supports tools for InternLM2, InternLM2.5, llama3.1 and Qwen2.5 models.
## Single Round Invocation
@@ -241,3 +241,156 @@ messages += [
assistant_response = request_llama3_1_service(messages)
print(assistant_response)
```
+
+### Qwen2.5
+
+Qwen2.5 supports multi tool calling, which means that multiple tool requests can be initiated in one request
+
+```python
+from openai import OpenAI
+import json
+
+def get_current_temperature(location: str, unit: str = "celsius"):
+ """Get current temperature at a location.
+
+ Args:
+ location: The location to get the temperature for, in the format "City, State, Country".
+ unit: The unit to return the temperature in. Defaults to "celsius". (choices: ["celsius", "fahrenheit"])
+
+ Returns:
+ the temperature, the location, and the unit in a dict
+ """
+ return {
+ "temperature": 26.1,
+ "location": location,
+ "unit": unit,
+ }
+
+
+def get_temperature_date(location: str, date: str, unit: str = "celsius"):
+ """Get temperature at a location and date.
+
+ Args:
+ location: The location to get the temperature for, in the format "City, State, Country".
+ date: The date to get the temperature for, in the format "Year-Month-Day".
+ unit: The unit to return the temperature in. Defaults to "celsius". (choices: ["celsius", "fahrenheit"])
+
+ Returns:
+ the temperature, the location, the date and the unit in a dict
+ """
+ return {
+ "temperature": 25.9,
+ "location": location,
+ "date": date,
+ "unit": unit,
+ }
+
+def get_function_by_name(name):
+ if name == "get_current_temperature":
+ return get_current_temperature
+ if name == "get_temperature_date":
+ return get_temperature_date
+
+tools = [{
+ 'type': 'function',
+ 'function': {
+ 'name': 'get_current_temperature',
+ 'description': 'Get current temperature at a location.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type': 'string',
+ 'description': 'The location to get the temperature for, in the format \'City, State, Country\'.'
+ },
+ 'unit': {
+ 'type': 'string',
+ 'enum': [
+ 'celsius',
+ 'fahrenheit'
+ ],
+ 'description': 'The unit to return the temperature in. Defaults to \'celsius\'.'
+ }
+ },
+ 'required': [
+ 'location'
+ ]
+ }
+ }
+}, {
+ 'type': 'function',
+ 'function': {
+ 'name': 'get_temperature_date',
+ 'description': 'Get temperature at a location and date.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type': 'string',
+ 'description': 'The location to get the temperature for, in the format \'City, State, Country\'.'
+ },
+ 'date': {
+ 'type': 'string',
+ 'description': 'The date to get the temperature for, in the format \'Year-Month-Day\'.'
+ },
+ 'unit': {
+ 'type': 'string',
+ 'enum': [
+ 'celsius',
+ 'fahrenheit'
+ ],
+ 'description': 'The unit to return the temperature in. Defaults to \'celsius\'.'
+ }
+ },
+ 'required': [
+ 'location',
+ 'date'
+ ]
+ }
+ }
+}]
+messages = [{'role': 'user', 'content': 'Today is 2024-11-14, What\'s the temperature in San Francisco now? How about tomorrow?'}]
+
+client = OpenAI(api_key='YOUR_API_KEY', base_url='http://0.0.0.0:23333/v1')
+model_name = client.models.list().data[0].id
+response = client.chat.completions.create(
+ model=model_name,
+ messages=messages,
+ temperature=0.8,
+ top_p=0.8,
+ stream=False,
+ tools=tools)
+print(response.choices[0].message.tool_calls)
+messages.append(response.choices[0].message)
+
+for tool_call in response.choices[0].message.tool_calls:
+ tool_call_args = json.loads(tool_call.function.arguments)
+ tool_call_result = get_function_by_name(tool_call.function.name)(**tool_call_args)
+ messages.append({
+ 'role': 'tool',
+ 'name': tool_call.function.name,
+ 'content': tool_call_result,
+ 'tool_call_id': tool_call.id
+ })
+
+response = client.chat.completions.create(
+ model=model_name,
+ messages=messages,
+ temperature=0.8,
+ top_p=0.8,
+ stream=False,
+ tools=tools)
+print(response.choices[0].message.content)
+
+```
+
+Using the Qwen2.5-14B-Instruct, similar results can be obtained as follows
+
+```
+[ChatCompletionMessageToolCall(id='0', function=Function(arguments='{"location": "San Francisco, California, USA"}', name='get_current_temperature'), type='function'),
+ ChatCompletionMessageToolCall(id='1', function=Function(arguments='{"location": "San Francisco, California, USA", "date": "2024-11-15"}', name='get_temperature_date'), type='function')]
+
+The current temperature in San Francisco, California, USA is 26.1°C. For tomorrow, 2024-11-15, the temperature is expected to be 25.9°C.
+```
+
+It is important to note that in scenarios involving multiple tool calls, the order of the tool call results can affect the response quality. The tool_call_id has not been correctly provided to the LLM.
diff --git a/docs/zh_cn/llm/api_server_tools.md b/docs/zh_cn/llm/api_server_tools.md
index 643a39d5d2..8688ea35cd 100644
--- a/docs/zh_cn/llm/api_server_tools.md
+++ b/docs/zh_cn/llm/api_server_tools.md
@@ -1,6 +1,6 @@
# Tools
-LMDeploy 支持 InternLM2, InternLM2.5 和 Llama3.1 模型的工具调用。
+LMDeploy 支持 InternLM2, InternLM2.5, Llama3.1 和 Qwen2.5模型的工具调用。
## 单轮调用
@@ -241,3 +241,156 @@ messages += [
assistant_response = request_llama3_1_service(messages)
print(assistant_response)
```
+
+### Qwen2.5
+
+Qwen2.5 支持了多工具调用,这意味着可以在一次请求中可能发起多个工具请求
+
+```python
+from openai import OpenAI
+import json
+
+def get_current_temperature(location: str, unit: str = "celsius"):
+ """Get current temperature at a location.
+
+ Args:
+ location: The location to get the temperature for, in the format "City, State, Country".
+ unit: The unit to return the temperature in. Defaults to "celsius". (choices: ["celsius", "fahrenheit"])
+
+ Returns:
+ the temperature, the location, and the unit in a dict
+ """
+ return {
+ "temperature": 26.1,
+ "location": location,
+ "unit": unit,
+ }
+
+
+def get_temperature_date(location: str, date: str, unit: str = "celsius"):
+ """Get temperature at a location and date.
+
+ Args:
+ location: The location to get the temperature for, in the format "City, State, Country".
+ date: The date to get the temperature for, in the format "Year-Month-Day".
+ unit: The unit to return the temperature in. Defaults to "celsius". (choices: ["celsius", "fahrenheit"])
+
+ Returns:
+ the temperature, the location, the date and the unit in a dict
+ """
+ return {
+ "temperature": 25.9,
+ "location": location,
+ "date": date,
+ "unit": unit,
+ }
+
+def get_function_by_name(name):
+ if name == "get_current_temperature":
+ return get_current_temperature
+ if name == "get_temperature_date":
+ return get_temperature_date
+
+tools = [{
+ 'type': 'function',
+ 'function': {
+ 'name': 'get_current_temperature',
+ 'description': 'Get current temperature at a location.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type': 'string',
+ 'description': 'The location to get the temperature for, in the format \'City, State, Country\'.'
+ },
+ 'unit': {
+ 'type': 'string',
+ 'enum': [
+ 'celsius',
+ 'fahrenheit'
+ ],
+ 'description': 'The unit to return the temperature in. Defaults to \'celsius\'.'
+ }
+ },
+ 'required': [
+ 'location'
+ ]
+ }
+ }
+}, {
+ 'type': 'function',
+ 'function': {
+ 'name': 'get_temperature_date',
+ 'description': 'Get temperature at a location and date.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type': 'string',
+ 'description': 'The location to get the temperature for, in the format \'City, State, Country\'.'
+ },
+ 'date': {
+ 'type': 'string',
+ 'description': 'The date to get the temperature for, in the format \'Year-Month-Day\'.'
+ },
+ 'unit': {
+ 'type': 'string',
+ 'enum': [
+ 'celsius',
+ 'fahrenheit'
+ ],
+ 'description': 'The unit to return the temperature in. Defaults to \'celsius\'.'
+ }
+ },
+ 'required': [
+ 'location',
+ 'date'
+ ]
+ }
+ }
+}]
+messages = [{'role': 'user', 'content': 'Today is 2024-11-14, What\'s the temperature in San Francisco now? How about tomorrow?'}]
+
+client = OpenAI(api_key='YOUR_API_KEY', base_url='http://0.0.0.0:23333/v1')
+model_name = client.models.list().data[0].id
+response = client.chat.completions.create(
+ model=model_name,
+ messages=messages,
+ temperature=0.8,
+ top_p=0.8,
+ stream=False,
+ tools=tools)
+print(response.choices[0].message.tool_calls)
+messages.append(response.choices[0].message)
+
+for tool_call in response.choices[0].message.tool_calls:
+ tool_call_args = json.loads(tool_call.function.arguments)
+ tool_call_result = get_function_by_name(tool_call.function.name)(**tool_call_args)
+ messages.append({
+ 'role': 'tool',
+ 'name': tool_call.function.name,
+ 'content': tool_call_result,
+ 'tool_call_id': tool_call.id
+ })
+
+response = client.chat.completions.create(
+ model=model_name,
+ messages=messages,
+ temperature=0.8,
+ top_p=0.8,
+ stream=False,
+ tools=tools)
+print(response.choices[0].message.content)
+
+```
+
+使用Qwen2.5-14B-Instruct,可以得到以下类似结果
+
+```
+[ChatCompletionMessageToolCall(id='0', function=Function(arguments='{"location": "San Francisco, California, USA"}', name='get_current_temperature'), type='function'),
+ ChatCompletionMessageToolCall(id='1', function=Function(arguments='{"location": "San Francisco, California, USA", "date": "2024-11-15"}', name='get_temperature_date'), type='function')]
+
+The current temperature in San Francisco, California, USA is 26.1°C. For tomorrow, 2024-11-15, the temperature is expected to be 25.9°C.
+```
+
+需要注意的是,多工具调用的情况下,工具调用的结果顺序会影响回答的效果,tool_call_id并没有正确给到LLM.
diff --git a/lmdeploy/model.py b/lmdeploy/model.py
index 2b3a0a4e1d..b699fe4242 100644
--- a/lmdeploy/model.py
+++ b/lmdeploy/model.py
@@ -927,7 +927,8 @@ def match(cls, model_path: str) -> Optional[str]:
Args:
model_path (str): the model path used for matching.
"""
- if 'qwen' in model_path.lower():
+ if 'qwen' in model_path.lower() and 'qwen2.5' not in model_path.lower(
+ ):
return 'qwen'
if 'minicpm-v-2_6' in model_path.lower():
return 'minicpmv-2d6'
@@ -935,6 +936,113 @@ def match(cls, model_path: str) -> Optional[str]:
return 'minicpm3'
+@MODELS.register_module(name='qwen2d5')
+class Qwen2d5Chat(Qwen7BChat):
+ """Chat template for Qwen2.5-Instruct series."""
+
+ def __init__(
+ self,
+ system='<|im_start|>system\n',
+ meta_instruction='You are Qwen, created by Alibaba Cloud. You are a helpful assistant.',
+ eosys='<|im_end|>\n',
+ user='<|im_start|>user\n',
+ eoh='<|im_end|>\n',
+ assistant='<|im_start|>assistant\n',
+ eoa='<|im_end|>',
+ separator='\n',
+ tools="""\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within XML tags:\n""",
+ eotools="""\n\n\nFor each function call, return a json object with function name and arguments within XML tags:\n\n{"name": , "arguments": }\n""",
+ stop_words=['<|im_end|>'],
+ **kwargs):
+
+ self.tools = tools
+ self.eotools = eotools
+ super().__init__(system=system,
+ meta_instruction=meta_instruction,
+ eosys=eosys,
+ user=user,
+ eoh=eoh,
+ assistant=assistant,
+ eoa=eoa,
+ separator=separator,
+ stop_words=stop_words,
+ **kwargs)
+
+ def messages2prompt(self,
+ messages,
+ sequence_start=True,
+ tools=None,
+ **kwargs):
+ """Return the prompt that is concatenated with other elements in the
+ chat template.
+
+ Args:
+ messages (str | List): user's input prompt
+ Returns:
+ str: the concatenated prompt
+ """
+ if isinstance(messages, str):
+ return self.get_prompt(messages, sequence_start)
+ box_map = dict(user=self.user,
+ assistant=self.assistant,
+ system=self.system)
+ ret = ''
+ tool_prompt = ''
+ if tools is not None and len(tools) > 0:
+ for tool in tools:
+ tool_prompt += self.separator
+ tool_prompt += f'{{"type": "function", "function": {json.dumps(tool, ensure_ascii=False)}}}'
+ if len(messages) and messages[0]['role'] == 'system':
+ ret += f"{self.system}{messages[0]['content']}{self.tools}{tool_prompt}{self.eotools}{self.eosys}"
+ else:
+ ret += f'{self.system}{self.meta_instruction}{self.tools}{tool_prompt}{self.eotools}{self.eosys}'
+ else:
+ if self.meta_instruction is not None and sequence_start:
+ if len(messages) and messages[0]['role'] == 'system':
+ ret += f"{self.system}{messages[0]['content']}{self.eosys}"
+ else:
+ ret += f'{self.system}{self.meta_instruction}{self.eosys}'
+
+ for index, message in enumerate(messages):
+ if (message['role'] == 'user'
+ or (message['role'] == 'system' and index != 0)
+ or (message['role'] == 'assistant'
+ and message.get('tool_calls') is None)):
+ ret += f"{box_map[message['role']]}{message['content']}{self.eosys}"
+ elif message['role'] == 'assistant':
+ ret += f'<|im_start|>assistant'
+ if message.get('content') is not None:
+ ret += f"{self.separator}{message['content']}"
+
+ if message.get('tool_calls') is not None:
+ tool_calls = message['tool_calls']
+ for tool_call in tool_calls:
+ if tool_call.get('function') is not None:
+ tool_call = tool_call['function']
+ ret += f'{self.separator}{self.separator}{{"name": "{tool_call["name"]}", "arguments": {json.dumps(tool_call["arguments"], ensure_ascii=False)}}}{self.separator}'
+ ret += self.eosys
+ if message['role'] == 'tool':
+ if index == 0 or messages[index - 1]['role'] != 'tool':
+ ret += f'<|im_start|>user'
+ ret += f"{self.separator}{self.separator}{message['content']}{self.separator}"
+ if index == len(messages) - 1 or messages[index +
+ 1]['role'] != 'tool':
+ ret += f'{self.eoh}'
+ ret += f'{self.assistant}'
+ return ret
+
+ @classmethod
+ def match(cls, model_path: str) -> Optional[str]:
+ """Return the model_name that was registered to MODELS.
+
+ Args:
+ model_path (str): the model path used for matching.
+ """
+ lower_path = model_path.lower()
+ if 'qwen2.5' in lower_path or 'qwen2_5' in lower_path:
+ return 'qwen2d5'
+
+
@MODELS.register_module(name='codellama')
class CodeLlama(Llama2):
diff --git a/lmdeploy/serve/async_engine.py b/lmdeploy/serve/async_engine.py
index 3c8f193cd5..f3c3432328 100644
--- a/lmdeploy/serve/async_engine.py
+++ b/lmdeploy/serve/async_engine.py
@@ -4,6 +4,7 @@
import json
import os
import random
+import re
from contextlib import asynccontextmanager
from copy import deepcopy
from itertools import count
@@ -648,14 +649,37 @@ def parse_tool_response(self, text, tools, **kwargs):
name, parameters = action['name'], json.dumps(action.get(
'parameters', action.get('arguments', {})),
ensure_ascii=False)
+ call_info_list = [(name, parameters)]
elif '')
parameters = action[action.find('{'):]
name = action.split('{')[0]
+ call_info_list = [(name, parameters)]
+ elif '' in text and '' in text: # qwen2.5
+ # get tool_call in text
+ pattern = r'(.*?)'
+ match_result_list = re.findall(pattern, text, re.DOTALL)
+ call_info_list = []
+ for match_result in match_result_list:
+ action = json.loads(match_result)
+ call_info_list.append((action['name'],
+ json.dumps(action['arguments'],
+ ensure_ascii=False)))
+ # get text outside of tags
+ if not text.startswith(''):
+ text = text[:text.find('')]
+ elif not text.endswith(''):
+ text = text[text.rfind('') + len(''):]
+ else:
+ text = ''
+
else:
raise RuntimeError(f'Unexpected model response: {text}')
- action_id = [tool.function.name for tool in tools].index(name)
- return text, action_id, name, parameters
+
+ call_info_list = [([tool.function.name for tool in tools
+ ].index(call_info[0]), call_info[0], call_info[1])
+ for call_info in call_info_list]
+ return text, call_info_list
def chat(self,
prompt: str,
diff --git a/lmdeploy/serve/openai/api_server.py b/lmdeploy/serve/openai/api_server.py
index a12cadaa7d..2d0560720d 100644
--- a/lmdeploy/serve/openai/api_server.py
+++ b/lmdeploy/serve/openai/api_server.py
@@ -495,17 +495,18 @@ async def completion_stream_generator() -> AsyncGenerator[str, None]:
final_logprobs.extend(res.logprobs)
tool_calls = None
- if request.tool_choice != 'none' and ('<|plugin|>' in text
- or '' in text or '' in text):
if final_res.finish_reason == 'stop':
final_res.finish_reason = 'tool_calls'
try: # TODO add json_schema guidance to turbomind
- text, action_id, name, parameters = VariableInterface.async_engine.parse_tool_response( # noqa
+ text, call_info_list = VariableInterface.async_engine.parse_tool_response( # noqa
text, request.tools)
tool_calls = [
- ToolCall(id=str(action_id),
- function=FunctionResponse(name=name,
- arguments=parameters))
+ ToolCall(id=str(call_info[0]),
+ function=FunctionResponse(name=call_info[1],
+ arguments=call_info[2]))
+ for call_info in call_info_list
]
except Exception as e:
logger.error(f'Exception: {e}')
diff --git a/tests/test_lmdeploy/test_model.py b/tests/test_lmdeploy/test_model.py
index a38971e4d0..ae21053308 100644
--- a/tests/test_lmdeploy/test_model.py
+++ b/tests/test_lmdeploy/test_model.py
@@ -9,6 +9,7 @@
('internlm/internlm2-1_8b', ['base']),
('models--internlm--internlm-chat-7b/snapshots/1234567', ['internlm']),
('Qwen/Qwen-7B-Chat', ['qwen']),
+ ('Qwen/Qwen2.5-7B-Instruct', ['qwen2d5']),
('codellama/CodeLlama-7b-hf', ['codellama']),
('upstage/SOLAR-0-70b', ['solar', 'solar-70b']),
('meta-llama/Llama-2-7b-chat-hf', ['llama2']),
@@ -283,6 +284,291 @@ def test_qwen():
assert _prompt is None
+def test_qwen2d5():
+ prompt = 'hello, can u introduce yourself'
+ model = MODELS.get('qwen2d5')(capability='completion')
+ assert model.get_prompt(prompt, sequence_start=True) == prompt
+ assert model.get_prompt(prompt, sequence_start=False) == prompt
+
+ model = MODELS.get('qwen2d5')(capability='chat')
+
+ # No tool call
+ messages = [
+ dict(role='user',
+ content='What\'s the temperature in San Francisco now?')
+ ]
+ no_tool_prompt = ('<|im_start|>system\nYou are Qwen, created by Alibaba '
+ 'Cloud. You are a helpful '
+ "assistant.<|im_end|>\n<|im_start|>user\nWhat's the "
+ 'temperature in San Francisco '
+ 'now?<|im_end|>\n<|im_start|>assistant\n')
+ assert model.messages2prompt(messages) == no_tool_prompt
+ assert model.messages2prompt(messages, tools=[]) == no_tool_prompt
+
+ messages.append({'role': 'assistant', 'content': 'I don\'t know.'})
+ no_tool_prompt = ('<|im_start|>system\nYou are Qwen, created by Alibaba '
+ 'Cloud. You are a helpful '
+ "assistant.<|im_end|>\n<|im_start|>user\nWhat's the "
+ 'temperature in San Francisco '
+ "now?<|im_end|>\n<|im_start|>assistant\nI don't "
+ 'know.<|im_end|>\n<|im_start|>assistant\n')
+ assert model.messages2prompt(messages) == no_tool_prompt
+ # Single tool call
+ tools = [{
+ 'name': 'get_current_temperature',
+ 'description': 'Get current temperature at a location.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type':
+ 'string',
+ 'description':
+ 'The location to get the temperature for,'
+ ' in the format \'City, State, Country\'.'
+ },
+ 'unit': {
+ 'type':
+ 'string',
+ 'enum': ['celsius', 'fahrenheit'],
+ 'description':
+ 'The unit to return the temperature in. Defaults to '
+ '\'celsius\'.'
+ }
+ },
+ 'required': ['location']
+ }
+ }]
+
+ messages = [
+ dict(role='user',
+ content='What\'s the temperature in San Francisco now?')
+ ]
+ tool_prompt = ('<|im_start|>system\nYou are Qwen, created by Alibaba '
+ 'Cloud. You are a helpful assistant.\n\n# Tools\n\nYou '
+ 'may call one or more functions to assist with the user '
+ 'query.\n\nYou are provided with function signatures '
+ "within XML tags:\n\n{\"type\": "
+ "\"function\", \"function\": {\"name\": "
+ "\"get_current_temperature\", \"description\": \"Get "
+ "current temperature at a location.\", \"parameters\": {"
+ "\"type\": \"object\", \"properties\": {\"location\": {"
+ "\"type\": \"string\", \"description\": \"The location to "
+ "get the temperature for, in the format 'City, State, "
+ "Country'.\"}, \"unit\": {\"type\": \"string\", \"enum\": "
+ "[\"celsius\", \"fahrenheit\"], \"description\": \"The "
+ 'unit to return the temperature in. Defaults to '
+ "'celsius'.\"}}, \"required\": ["
+ "\"location\"]}}}\n\n\nFor each function call, "
+ 'return a json object with function name and arguments '
+ 'within XML tags:\n\n{'
+ "\"name\": , \"arguments\": "
+ '}\n<|im_end|>\n<|im_start'
+ "|>user\nWhat's the temperature in San Francisco "
+ 'now?<|im_end|>\n<|im_start|>assistant\n')
+ assert model.messages2prompt(messages, tools=tools) == tool_prompt
+
+ messages.append(
+ dict(role='tool',
+ name='get_current_temperature',
+ content={
+ 'temperature': 26.1,
+ 'location': 'San Francisco, California, USA',
+ 'unit': 'celsius'
+ },
+ tool_call_id='0'))
+ tool_prompt = ('<|im_start|>system\nYou are Qwen, created by Alibaba '
+ 'Cloud. You are a helpful assistant.\n\n# Tools\n\nYou '
+ 'may call one or more functions to assist with the user '
+ 'query.\n\nYou are provided with function signatures '
+ "within XML tags:\n\n{\"type\": "
+ "\"function\", \"function\": {\"name\": "
+ "\"get_current_temperature\", \"description\": \"Get "
+ "current temperature at a location.\", \"parameters\": {"
+ "\"type\": \"object\", \"properties\": {\"location\": {"
+ "\"type\": \"string\", \"description\": \"The location to "
+ "get the temperature for, in the format 'City, State, "
+ "Country'.\"}, \"unit\": {\"type\": \"string\", \"enum\": "
+ "[\"celsius\", \"fahrenheit\"], \"description\": \"The "
+ 'unit to return the temperature in. Defaults to '
+ "'celsius'.\"}}, \"required\": ["
+ "\"location\"]}}}\n\n\nFor each function call, "
+ 'return a json object with function name and arguments '
+ 'within XML tags:\n\n{'
+ "\"name\": , \"arguments\": "
+ '}\n<|im_end|>\n<|im_start'
+ "|>user\nWhat's the temperature in San Francisco "
+ 'now?<|im_end|>\n<|im_start|>user\n\n{'
+ "'temperature': 26.1, 'location': 'San Francisco, "
+ "California, USA', 'unit': "
+ "'celsius'}\n<|im_end|>\n<|im_start"
+ '|>assistant\n')
+ assert model.messages2prompt(messages, tools=tools) == tool_prompt
+ # Multi tool calling
+ tools = [{
+ 'name': 'get_current_temperature',
+ 'description': 'Get current temperature at a location.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type':
+ 'string',
+ 'description':
+ 'The location to get the temperature for, in the format '
+ '\'City, State, Country\'.'
+ },
+ 'unit': {
+ 'type':
+ 'string',
+ 'enum': ['celsius', 'fahrenheit'],
+ 'description':
+ 'The unit to return the temperature in.'
+ ' Defaults to \'celsius\'.'
+ }
+ },
+ 'required': ['location']
+ }
+ }, {
+ 'name': 'get_temperature_date',
+ 'description': 'Get temperature at a location and date.',
+ 'parameters': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type':
+ 'string',
+ 'description':
+ 'The location to get the temperature for,'
+ ' in the format \'City, State, Country\'.'
+ },
+ 'date': {
+ 'type':
+ 'string',
+ 'description':
+ 'The date to get the temperature for,'
+ ' in the format \'Year-Month-Day\'.'
+ },
+ 'unit': {
+ 'type':
+ 'string',
+ 'enum': ['celsius', 'fahrenheit'],
+ 'description':
+ 'The unit to return the temperature in.'
+ ' Defaults to \'celsius\'.'
+ }
+ },
+ 'required': ['location', 'date']
+ }
+ }]
+ messages = [
+ dict(role='user',
+ content='Today is 2024-11-14, What\'s the temperature in'
+ ' San Francisco now? How about tomorrow?')
+ ]
+ tool_prompt = ('<|im_start|>system\nYou are Qwen, created by Alibaba '
+ 'Cloud. You are a helpful assistant.\n\n# Tools\n\nYou '
+ 'may call one or more functions to assist with the user '
+ 'query.\n\nYou are provided with function signatures '
+ "within XML tags:\n\n{\"type\": "
+ "\"function\", \"function\": {\"name\": "
+ "\"get_current_temperature\", \"description\": \"Get "
+ "current temperature at a location.\", \"parameters\": {"
+ "\"type\": \"object\", \"properties\": {\"location\": {"
+ "\"type\": \"string\", \"description\": \"The location to "
+ "get the temperature for, in the format 'City, State, "
+ "Country'.\"}, \"unit\": {\"type\": \"string\", \"enum\": "
+ "[\"celsius\", \"fahrenheit\"], \"description\": \"The "
+ 'unit to return the temperature in. Defaults to '
+ "'celsius'.\"}}, \"required\": [\"location\"]}}}\n{"
+ "\"type\": \"function\", \"function\": {\"name\": "
+ "\"get_temperature_date\", \"description\": \"Get "
+ "temperature at a location and date.\", \"parameters\": {"
+ "\"type\": \"object\", \"properties\": {\"location\": {"
+ "\"type\": \"string\", \"description\": \"The location to "
+ "get the temperature for, in the format 'City, State, "
+ "Country'.\"}, \"date\": {\"type\": \"string\", "
+ "\"description\": \"The date to get the temperature for, "
+ "in the format 'Year-Month-Day'.\"}, \"unit\": {\"type\": "
+ "\"string\", \"enum\": [\"celsius\", \"fahrenheit\"], "
+ "\"description\": \"The unit to return the temperature "
+ "in. Defaults to 'celsius'.\"}}, \"required\": ["
+ "\"location\", \"date\"]}}}\n\n\nFor each "
+ 'function call, return a json object with function name '
+ 'and arguments within XML '
+ "tags:\n\n{\"name\": , "
+ "\"arguments\": "
+ '}\n<|im_end|>\n<|im_start'
+ "|>user\nToday is 2024-11-14, What's the temperature in "
+ 'San Francisco now? How about '
+ 'tomorrow?<|im_end|>\n<|im_start|>assistant\n')
+ assert model.messages2prompt(messages, tools=tools) == tool_prompt
+
+ messages.append(
+ dict(role='tool',
+ name='get_current_temperature',
+ content={
+ 'temperature': 26.1,
+ 'location': 'San Francisco, California, USA',
+ 'unit': 'celsius'
+ },
+ tool_call_id='0'))
+ messages.append(
+ dict(role='tool',
+ name='get_temperature_date',
+ content={
+ 'temperature': 25.9,
+ 'location': 'San Francisco, California, USA',
+ 'date': '2024-11-15',
+ 'unit': 'celsius'
+ },
+ tool_call_id='1'))
+ tool_prompt = ('<|im_start|>system\nYou are Qwen, created by Alibaba '
+ 'Cloud. You are a helpful assistant.\n\n# Tools\n\nYou '
+ 'may call one or more functions to assist with the user '
+ 'query.\n\nYou are provided with function signatures '
+ "within XML tags:\n\n{\"type\": "
+ "\"function\", \"function\": {\"name\": "
+ "\"get_current_temperature\", \"description\": \"Get "
+ "current temperature at a location.\", \"parameters\": {"
+ "\"type\": \"object\", \"properties\": {\"location\": {"
+ "\"type\": \"string\", \"description\": \"The location to "
+ "get the temperature for, in the format 'City, State, "
+ "Country'.\"}, \"unit\": {\"type\": \"string\", \"enum\": "
+ "[\"celsius\", \"fahrenheit\"], \"description\": \"The "
+ 'unit to return the temperature in. Defaults to '
+ "'celsius'.\"}}, \"required\": [\"location\"]}}}\n{"
+ "\"type\": \"function\", \"function\": {\"name\": "
+ "\"get_temperature_date\", \"description\": \"Get "
+ "temperature at a location and date.\", \"parameters\": {"
+ "\"type\": \"object\", \"properties\": {\"location\": {"
+ "\"type\": \"string\", \"description\": \"The location to "
+ "get the temperature for, in the format 'City, State, "
+ "Country'.\"}, \"date\": {\"type\": \"string\", "
+ "\"description\": \"The date to get the temperature for, "
+ "in the format 'Year-Month-Day'.\"}, \"unit\": {\"type\": "
+ "\"string\", \"enum\": [\"celsius\", \"fahrenheit\"], "
+ "\"description\": \"The unit to return the temperature "
+ "in. Defaults to 'celsius'.\"}}, \"required\": ["
+ "\"location\", \"date\"]}}}\n\n\nFor each "
+ 'function call, return a json object with function name '
+ 'and arguments within XML '
+ "tags:\n\n{\"name\": , "
+ "\"arguments\": "
+ '}\n<|im_end|>\n<|im_start'
+ "|>user\nToday is 2024-11-14, What's the temperature in "
+ 'San Francisco now? How about '
+ 'tomorrow?<|im_end|>\n<|im_start|>user\n\n{'temperature': 26.1, 'location': 'San Francisco, "
+ "California, USA', 'unit': "
+ "'celsius'}\n\n\n{"
+ "'temperature': 25.9, 'location': 'San Francisco, "
+ "California, USA', 'date': '2024-11-15', 'unit': "
+ "'celsius'}\n<|im_end|>\n<|im_start"
+ '|>assistant\n')
+ assert model.messages2prompt(messages, tools=tools) == tool_prompt
+
+
def test_codellama_completion():
model = MODELS.get('codellama')(capability='completion')
prompt = """\