Skip to content

Commit 9f8f94e

Browse files
Maximilian-Winterjordankanter
authored andcommitted
examples : add complete parallel function calling example (ggml-org#4974)
1 parent 4562e5f commit 9f8f94e

File tree

1 file changed

+121
-2
lines changed

1 file changed

+121
-2
lines changed

examples/pydantic-models-to-grammar-examples.py

+121-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Function calling example using pydantic models.
2-
2+
import datetime
33
import json
44
from enum import Enum
55
from typing import Union, Optional
@@ -8,7 +8,8 @@
88
from pydantic import BaseModel, Field
99

1010
import importlib
11-
from pydantic_models_to_grammar import generate_gbnf_grammar_and_documentation
11+
from pydantic_models_to_grammar import generate_gbnf_grammar_and_documentation, convert_dictionary_to_pydantic_model, add_run_method_to_dynamic_model, create_dynamic_model_from_function
12+
1213

1314
# Function to get completion on the llama.cpp server with grammar.
1415
def create_completion(prompt, grammar):
@@ -134,3 +135,121 @@ class Book(BaseModel):
134135
json_data = json.loads(text)
135136

136137
print(Book(**json_data))
138+
# An example for parallel function calling with a Python function, a pydantic function model and an OpenAI like function definition.
139+
140+
def get_current_datetime(output_format: Optional[str] = None):
141+
"""
142+
Get the current date and time in the given format.
143+
Args:
144+
output_format: formatting string for the date and time, defaults to '%Y-%m-%d %H:%M:%S'
145+
"""
146+
if output_format is None:
147+
output_format = '%Y-%m-%d %H:%M:%S'
148+
return datetime.datetime.now().strftime(output_format)
149+
150+
151+
# Enum for the calculator tool.
152+
class MathOperation(Enum):
153+
ADD = "add"
154+
SUBTRACT = "subtract"
155+
MULTIPLY = "multiply"
156+
DIVIDE = "divide"
157+
158+
159+
160+
# Simple pydantic calculator tool for the agent that can add, subtract, multiply, and divide. Docstring and description of fields will be used in system prompt.
161+
class Calculator(BaseModel):
162+
"""
163+
Perform a math operation on two numbers.
164+
"""
165+
number_one: Union[int, float] = Field(..., description="First number.")
166+
operation: MathOperation = Field(..., description="Math operation to perform.")
167+
number_two: Union[int, float] = Field(..., description="Second number.")
168+
169+
def run(self):
170+
if self.operation == MathOperation.ADD:
171+
return self.number_one + self.number_two
172+
elif self.operation == MathOperation.SUBTRACT:
173+
return self.number_one - self.number_two
174+
elif self.operation == MathOperation.MULTIPLY:
175+
return self.number_one * self.number_two
176+
elif self.operation == MathOperation.DIVIDE:
177+
return self.number_one / self.number_two
178+
else:
179+
raise ValueError("Unknown operation.")
180+
181+
182+
# Example function to get the weather
183+
def get_current_weather(location, unit):
184+
"""Get the current weather in a given location"""
185+
if "London" in location:
186+
return json.dumps({"location": "London", "temperature": "42", "unit": unit.value})
187+
elif "New York" in location:
188+
return json.dumps({"location": "New York", "temperature": "24", "unit": unit.value})
189+
elif "North Pole" in location:
190+
return json.dumps({"location": "North Pole", "temperature": "-42", "unit": unit.value})
191+
else:
192+
return json.dumps({"location": location, "temperature": "unknown"})
193+
194+
195+
# Here is a function definition in OpenAI style
196+
current_weather_tool = {
197+
"type": "function",
198+
"function": {
199+
"name": "get_current_weather",
200+
"description": "Get the current weather in a given location",
201+
"parameters": {
202+
"type": "object",
203+
"properties": {
204+
"location": {
205+
"type": "string",
206+
"description": "The city and state, e.g. San Francisco, CA",
207+
},
208+
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
209+
},
210+
"required": ["location"],
211+
},
212+
},
213+
}
214+
215+
# Convert OpenAI function definition into pydantic model
216+
current_weather_tool_model = convert_dictionary_to_pydantic_model(current_weather_tool)
217+
# Add the actual function to a pydantic model
218+
current_weather_tool_model = add_run_method_to_dynamic_model(current_weather_tool_model, get_current_weather)
219+
220+
# Convert normal Python function to a pydantic model
221+
current_datetime_model = create_dynamic_model_from_function(get_current_datetime)
222+
223+
tool_list = [SendMessageToUser, Calculator, current_datetime_model, current_weather_tool_model]
224+
225+
226+
gbnf_grammar, documentation = generate_gbnf_grammar_and_documentation(
227+
pydantic_model_list=tool_list, outer_object_name="function",
228+
outer_object_content="params", model_prefix="Function", fields_prefix="Parameters", list_of_outputs=True)
229+
230+
system_message = "You are an advanced AI assistant. You are interacting with the user and with your environment by calling functions. You call functions by writing JSON objects, which represent specific function calls.\nBelow is a list of your available function calls:\n\n" + documentation
231+
232+
233+
text = """Get the date and time, get the current weather in celsius in London and solve the following calculation: 42 * 42"""
234+
prompt = f"<|im_start|>system\n{system_message}<|im_end|>\n<|im_start|>user\n{text}<|im_end|>\n<|im_start|>assistant"
235+
236+
text = create_completion(prompt=prompt, grammar=gbnf_grammar)
237+
238+
json_data = json.loads(text)
239+
240+
print(json_data)
241+
# Should output something like this:
242+
# [{'function': 'get_current_datetime', 'params': {'output_format': '%Y-%m-%d %H:%M:%S'}}, {'function': 'get_current_weather', 'params': {'location': 'London', 'unit': 'celsius'}}, {'function': 'Calculator', 'params': {'number_one': 42, 'operation': 'multiply', 'number_two': 42}}]
243+
244+
245+
for call in json_data:
246+
if call["function"] == "Calculator":
247+
print(Calculator(**call["params"]).run())
248+
elif call["function"] == "get_current_datetime":
249+
print(current_datetime_model(**call["params"]).run())
250+
elif call["function"] == "get_current_weather":
251+
print(current_weather_tool_model(**call["params"]).run())
252+
# Should output something like this:
253+
# 2024-01-14 13:36:06
254+
# {"location": "London", "temperature": "42", "unit": "celsius"}
255+
# 1764

0 commit comments

Comments
 (0)