How to turn any python function into an LLM tool with LLMEasyTools
Lets say we have a function to contact a user:
def contact_user(name: str, city: str) -> str:
# Note the type annotations
#
# Lets say that a user is identified by his name
# and that we can contact a user if we know in which city he is
#
# lots of code that contacts the user
#
# and then we report that the user was indeed contacted
return f"User {name} from {city} was contacted"
We would like the LLM to call this function for all users mentioned in a text - so we would like to generate a schema for this function. In LLMEasyTools we have a function for that:
from llm_easy_tools import get_tool_defs
tool_schemas = get_tool_defs([contact_user])
Now we can use this schema in a chat completion call asking the LLM to generate function calls for our function for all users mentioned in a text:
text = "John lives in Warsaw. Bob lives in London."
response = client.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=[
{"role": "system", "content": "You are a personal assistant. Your current task is to contact all users mentioned in the message."},
{"role": "user", "content": text}],
tools=tool_schemas,
# ^^^^^^^^^^^^ here are our generated schemas
tool_choice="auto",
)
Now the LLM response should contain a list of json encoded calls for our `contact_user` function and we would like to execute these function calls. Again LLMEasyTools comes to help:
from llm_easy_tools import process_response
from pprint import pprint
# There might be more than one tool calls in a single response so results are a list
results = process_response(response, [contact_user])
for result in results:
pprint(result.output)
If everything goes well we should get:
'User John from Warsaw was contacted'
'User Bob from London was contacted'
Note the quotes - this is because we used pprint. The output of the called function can be anything - not just text.
**Limitations** - actually I have to admit that the title is not entirely correct this will not work for any function - there are some limitations. First - type annotations for parameters are crucial here, because schemas require parameter types. Second the parameter types cannot be something that would not work for pydantic model field - so stuff like parameters that are functions themselves or a socket connections are out. Hopefully this is not a big limitation.
The full code for this example is at: https://github.com/zby/LLMEasyTools/blob/main/examples/basic_function_call.py
See also other examples in that directory.