Skip to content
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

Add tool calls #80

Merged
merged 4 commits into from
Jun 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion chattool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__author__ = """Rex Wang"""
__email__ = '[email protected]'
__version__ = '3.1.7'
__version__ = '3.2.0'

import os, sys, requests
from .chattype import Chat, Resp
Expand Down
5 changes: 2 additions & 3 deletions chattool/asynctool.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ def async_chat_completion( msgs:Union[List[List[Dict]], str]
if clearfile and os.path.exists(chkpoint):
os.remove(chkpoint)
if api_key is None:
api_key = chattool.api_key
assert api_key is not None, "API key is not provided!"
api_key = chattool.api_key or ""
if chat_url is None:
if chattool.api_base:
chat_url = os.path.join(chattool.api_base, "chat/completions")
Expand All @@ -187,7 +186,7 @@ def async_chat_completion( msgs:Union[List[List[Dict]], str]
raise Exception("chat_url is not provided!")
chat_url = chattool.request.normalize_url(chat_url)
if 'model' not in options:
options['model'] = chattool.model if chattool.model else "gpt-3.5-turbo"
options['model'] = chattool.model or ""
# run async process
assert nproc > 0, "nproc must be greater than 0!"
max_tries = max(max_tries, max_requests)
Expand Down
179 changes: 138 additions & 41 deletions chattool/chattype.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions chattool/functioncall.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ def delete_dialogue_assist(chat_log:List[Dict]):
ind = 0
while ind < len(chat_log) - 1:
log = chat_log[ind]
if log['role'] == 'assistant' and 'function_call' in log:
if log['role'] == 'assistant' and ('tool_calls' in log or 'function_call' in log):
nextind = ind + 1
nextlog = chat_log[nextind]
if nextlog['role'] == 'function':
if nextlog['role'] == 'tool' or nextlog['role'] == 'function':
chat_log.pop(nextind)
chat_log.pop(ind)
else:
Expand Down
5 changes: 5 additions & 0 deletions chattool/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ def function_call(self):
"""Function call"""
return self.message.get('function_call')

@property
def tool_calls(self):
"""Tool calls"""
return self.message.get('tool_calls')

@property
def delta(self):
"""Delta"""
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
with open('README.md') as readme_file:
readme = readme_file.read()

VERSION = '3.1.7'
VERSION = '3.2.0'

requirements = [
'Click>=7.0', 'requests>=2.20', "responses>=0.23", 'aiohttp>=3.8',
Expand Down
36 changes: 19 additions & 17 deletions tests/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ def test_auto_response():
chat = Chat("What's the weather like in Boston?")
chat.functions, chat.function_call = functions, 'auto'
chat.name2func = name2func
chat.autoresponse(max_tries=2)
chat.autoresponse(max_tries=2, use_tool=False)
chat.print_log()
chat.clear()
# response with nonempty content
chat.user("what is the result of 1+1, and What's the weather like in Boston?")
chat.autoresponse(max_tries=2)
chat.autoresponse(max_tries=2, use_tool=False)

# generate docstring from functions
def add(a: int, b: int) -> int:
Expand Down Expand Up @@ -89,6 +89,17 @@ def mult(a:int, b:int) -> int:
"""
return a * b

def test_mix_function_tool():
chat = Chat("find the sum of 784359345 and 345345345")
chat.setfuncs([add])
chat.autoresponse(max_tries=3, display=True, timeinterval=2)
chat.clear()
chat.user("find the sum of 784359345 and 345345345")
chat.autoresponse(use_tool=False)
newchat = Chat("find the product of 123124 and 399090")
newchat.settools([mult])
newchat.autoresponse()

def test_add_and_mult():
functions = [generate_json_schema(add)]
chat = Chat("find the sum of 784359345 and 345345345")
Expand All @@ -100,35 +111,26 @@ def test_add_and_mult():
chat.name2func = {'add': add} # dictionary of functions
chat.function_call = 'auto' # auto decision
# run until success: maxturns=-1
chat.autoresponse(max_tries=3, display=True, timeinterval=2)
chat.autoresponse(max_tries=3, display=True, timeinterval=2, use_tool=False)
# response should be finished
chat.simplify()
chat.print_log()
# use the setfuncs method
chat = Chat("find the value of 124842 * 3423424")
chat.setfuncs([add, mult]) # multi choice
chat.autoresponse(max_tries=3, timeinterval=2)
chat.autoresponse(max_tries=3, timeinterval=2, use_tool=False)
chat.simplify() # simplify the chat log
chat.print_log()
# test multichoice
chat.clear()
chat.user("find the value of 23723 + 12312, and 23723 * 12312")
# chat.autoresponse(max_tries=3, timeinterval=2)

def test_mock_resp():
chat = Chat("find the sum of 1235 and 3423")
chat.setfuncs([add, mult])
# mock result of the resp
para = {'name': 'add', 'arguments': '{\n "a": 1235,\n "b": 3423\n}'}
chat.assistant(content=None, function_call=para)
chat.callfunction()
chat.getresponse(max_tries=2)
chat.user("find the value of (23723 * 1322312 ) + 12312")
chat.autoresponse(max_tries=3, timeinterval=2, use_tool=False)

def test_use_exec_function():
chat = Chat("find the result of sqrt(121314)")
chat.setfuncs([exec_python_code])
chat.autoresponse(max_tries=2, display=True)
# chat.autoresponse(max_tries=2, display=True, use_tool=False)

def test_find_permutation_group():
pass

Expand Down
145 changes: 145 additions & 0 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# tests for function call

from chattool import Chat, generate_json_schema, exec_python_code
import json

# schema of functions
tools = [
{
"type": "function",
"function":{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location", "unit"],
},
}
}
]
weatherinfo = {
"location": "Boston, MA",
"temperature": "72",
"forecast": ["sunny", "windy"],
"unit":"celsius"
}
name2func = {
'get_current_weather': lambda *kargs, **kwargs: weatherinfo
}

def test_call_weather():
chat = Chat("What's the weather like in Boston?")
resp = chat.getresponse(tools=tools, tool_choice='auto', max_tries=3)
if resp.finish_reason == 'tool_calls':
last_tool = chat[-1]['tool_calls'][0] # get the last tool call
parainfo, tool_call_id = last_tool['function'], last_tool['id']
tool_name, tool_args = parainfo['name'], json.loads(parainfo['arguments'])
assert tool_name == 'get_current_weather'
assert 'location' in tool_args and 'unit' in tool_args
# continue the chat
# tool call result: weatherinfo
chat.tool(weatherinfo, tool_name, tool_call_id)
chat.getresponse()
chat.print_log()
else:
print("No function call found.")
assert True

def test_auto_response():
chat = Chat("What's the weather like in Boston?")
chat.tools, chat.tool_choice = tools, 'auto'
chat.name2func = name2func
chat.autoresponse(max_tries=2, display=True)
chat.print_log()
newchat = chat.deepcopy()
newchat.clear()
# response with nonempty content
newchat.user("what is the result of 1+1, and What's the weather like in Boston?")
newchat.autoresponse(max_tries=2, display=True)

# generate docstring from functions
def add(a: int, b: int) -> int:
"""
This function adds two numbers.

Parameters:
a (int): The first number.
b (int): The second number.

Returns:
int: The sum of the two numbers.
"""
return a + b

# with optional parameters
def mult(a:int, b:int) -> int:
"""This function multiplies two numbers.
It is a useful calculator!

Args:
a (int): The first number.
b (int): The second number.

Returns:
int: The product of the two numbers.
"""
return a * b

def test_add_and_mult():
tools = [{
'type':'function',
'function': generate_json_schema(tool)} for tool in [add, mult]]
chat = Chat("find the sum of 784359345 and 345345345")
chat.tools = tools
chat.tool_choice = None # unset keyword equivalent to "auto"
chat.tool_choice = 'none'
chat.tool_choice = {'name':'add'}
chat.tool_choice = 'add' # specify the function(convert to dict)
chat.tools = tools
chat.name2func = {'add': add} # dictionary of functions
chat.tool_choice = 'auto' # auto decision
# run until success: maxturns=-1
chat.autoresponse(max_tries=3, display=True, timeinterval=2)
# response should be finished
chat.simplify()
chat.print_log()
# use the setfuncs method
chat2 = Chat("find the value of 124842 * 3423424")
chat2.settools([add, mult]) # multi choice
chat2.autoresponse(max_tries=3, display=True, timeinterval=2)
chat2.simplify() # simplify the chat log
chat2.print_log()
# test multichoice
chat3 = chat2.deepcopy()
chat3.clear()
chat3.user("find the value of 23723 + 12312, and 23723 * 12312")
chat3.autoresponse(max_tries=3, display=True, timeinterval=2)
# test multichoice
chat4 = chat2.deepcopy()
chat4.clear()
chat4.user("find the value of (23723 * 1322312 ) + 12312")
chat4.autoresponse(max_tries=3, display=True, timeinterval=2)


def test_use_exec_function():
chat = Chat("find the result of sqrt(121314)")
chat.settools([exec_python_code])
chat.autoresponse(max_tries=2, display=True)

def test_find_permutation_group():
pass

def test_interact_with_leandojo():
pass

# debug area
# test_generate_docstring()
# test_function_call()
# test_function_call2()
Loading