Skip to content

Instantly share code, notes, and snippets.

@StanGirard
Forked from Shaunwei/functions.py
Created June 19, 2023 13:35
Show Gist options
  • Save StanGirard/1d5383ca8be398339d17ced09b256cfb to your computer and use it in GitHub Desktop.
Save StanGirard/1d5383ca8be398339d17ced09b256cfb to your computer and use it in GitHub Desktop.
Best OpenAI function calling template
from pydantic import BaseModel, Field
from tenacity import retry, stop_after_attempt
class FakeGoogleSearch(BaseModel):
query: str = Field(..., description='The query of Google search')
class FakeGoogleSearchResponse(BaseModel):
result: str = Field(..., description='The search result')
@openai_function
@retry(stop=stop_after_attempt(3))
def google_search(search: FakeGoogleSearch) -> FakeGoogleSearchResponse:
"""Perform a google search for the query and return result"""
# do something
if 'weather' in search.query:
result = 'Weather is great!'
else:
result = 'Finished search:' + search.query
return FakeGoogleSearchResponse(result=result)
# add more functions here
import openai
from functions import google_search
def chat(messages):
response = openai.ChatCompletion.create(
model='gpt-3.5-turbo-0613',
messages=messages,
functions=[
google_search.schema,
],
function_call='auto',
)
return response
def process_response(response, messages):
if response['choices'][0]['finish_reason'] == 'function_call':
function_name = response['choices'][0]['message']['function_call']['name']
if function_name == 'google_search':
new_response = google_search.from_response(response)
messages.append(
{
'role': 'function',
'name': function_name,
'content': new_response.result
}
)
return
# add more functions here
else:
messages.append(
{
'role': 'assistant',
'content': response['choices'][0]['message']['content']
}
)
while True:
messages = []
try:
query = input('User: ')
messages.append({'role': 'user', 'content': query})
response = chat(messages)
process_response(response, messages)
print(messages)
except KeyboardInterrupt:
break
from pydantic import validate_arguments
import json
from functools import wraps
from typing import Any, Callable
class OpenAIFunction:
def __init__(self, func: Callable) -> None:
self.func = func
self.validate_func = validate_arguments(func)
assert self.func.__doc__ is not None, "Missing docstring for {}".format(
self.func.__name__)
self.schema = {
"name": self.func.__name__,
"description": self.func.__doc__,
"parameters": self.validate_func.model.schema(),
}
self.model = self.validate_func.model
def __call__(self, *args: Any, **kwargs: Any) -> Any:
@wraps(self.func)
def wrapper(*args, **kwargs):
return self.validate_func(*args, **kwargs)
return wrapper(*args, **kwargs)
def from_response(self, completion, throw_error=True):
"""Execute the function from the response of an openai chat completion"""
message = completion.choices[0].message
if throw_error:
assert "function_call" in message, "No function call detected"
assert message["function_call"]["name"] == self.schema["name"], "Function name does not match"
function_call = message["function_call"]
arguments = json.loads(function_call["arguments"])
return self.validate_func(**arguments)
def openai_function(func: Callable) -> OpenAIFunction:
return OpenAIFunction(func)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment