💡 Full Course with Movies and Course Certificates (PDF): https://academy.finxter.com/college/openai-api-function-calls-and-embeddings/
Course Overview
👋 Hello, and welcome. I’m Dirk van Meerveld, and I’ll be your host and information for this tutorial collection, the place we’ll be focussing on OpenAi’s ChatGPT operate calls and the embeddings API.
Operate calls will permit us to make ChatGPT even smarter by giving it entry to further data or our personal customized performance via calling capabilities and feeding the return again once more to ChatGPT.
Embeddings will permit us to match sure items of textual content by which means as an alternative of by precise character and phrase matches, which may be very highly effective.
Each of those instruments are sport changers and have mind-blowing potential within the ever-expanding discipline of AI. So let’s soar proper in! Half 1 will probably be a bit longer as we do some setup work that we’ll use all through the approaching tutorials.
A Easy ChatGPT Name
I assume when you’re watching this you’re no less than considerably accustomed to ChatGPT calls utilizing Python. We’ll rapidly cowl the fundamentals so we’re all on the identical web page setup-wise, however gained’t go too far into the main points and fundamental settings. In case you’re utilizing ChatGPT for the primary time, I like to recommend you begin with my ‘Giggle Search‘ tutorial collection as an alternative, additionally out there on the Finxter Academy.
For the needs of this tutorial, I will probably be storing my API keys in a .env
file and studying them utilizing the config operate within the decouple bundle. You possibly can comply with together with my technique or use atmosphere variables in your native machine or another technique you like. Simply be sure to by no means ever hardcode API keys in your supply code!!
Run the under in your terminal to put in the decouple bundle when you don’t have it put in already:
pip set up python-decouple
Now create a .env
, which is just a file by the identify of '.env'
with no identify however solely the extension of .env
, within the base listing and add the next line to it:
CHATGPT_API_KEY=superdupersecretapikeygoeshere
Be sure that to insert your individual API key from Openai.com, or join an account when you wouldn’t have one but. Additionally, don’t use any areas because it won’t work for .env
recordsdata.
Now create a brand new Python file within the base listing known as Aa_get_joke.py
. You may give it a distinct identify, however I’ll be utilizing the letters A to G as there are 7 elements to this tutorial collection and it will make it simple for studying and later reference functions. After all, you wouldn’t wish to construction an actual software program venture on this approach, however for progressive classes, it is going to make issues good and alphabetically ordered for us.
A fast overview of the fundamentals earlier than we dive in
Now add the next code to your Aa_get_joke.py
file:
from decouple import config import openai openai.api_key = config("CHATGPT_API_KEY") JOKE_SETUP = "I gives you a topic every time for all the next prompts. You'll return to me a joke, but it surely shouldn't be too lengthy (4 traces at most). Please don't present an introduction like 'This is a joke for you' however get straight into the joke."
First, we import config from the decouple bundle, which is able to permit us to learn our API key from the .env
file.
Then we import the openai
bundle, which we’ll use to make our ChatGPT calls. We then set our API key utilizing the config operate from the decouple bundle, which is able to learn our API key from the .env
file.
Lastly, we set a relentless variable known as JOKE_SETUP
, which comprises a setup with directions for ChatGPT to comply with. We will probably be asking ChatGPT to generate a joke for us, an thought taken from the ‘Giggle search’ tutorial collection additionally out there on the Finxter academy.
Now proceed with the next code:
def get_joke(question): end result = openai.ChatCompletion.create( mannequin="gpt-3.5-turbo", temperature=0.4, max_tokens=200, messages=[ {"role": "system", "content": JOKE_SETUP}, {"role": "user", "content": query}, ], ) return end result["choices"][0]["message"]["content"]
We outline a operate known as get_joke
, which is only a easy ChatGPT API name.
We take a question as an argument, which is the topic of the joke we wish ChatGPT to generate for us.
We then name the ChatCompletion.create
operate from the openai
bundle, and save the lead to a variable named end result
.
We move within the mannequin we wish to use and set the temperature to 0.4, which is a measure of how inventive we wish ChatGPT to be. The upper the temperature, the extra inventive ChatGPT will probably be, however the extra nonsensical the response will probably get.
🧑💻 Really helpful: ChatGPT API Temperature
max_tokens
is self-explanatory. Observe which you can miss the temperature parameter for a default worth.
messages
is a listing of dictionaries, which is able to include the messages we wish to ship to ChatGPT. Every dictionary has a task and a content material key, and these messages will operate as a type of historical past of the dialog up to now for ChatGPT to make use of.
We set the primary message to be a system message, which is the setup we outlined earlier, and the second message to be the question that was handed into the operate.
ChatGPT will ship an object in response to us which seems to be one thing just like the under:
{ "selections": [ { "finish_reason": "stop", "index": 0, "message": { "content": "Why don't penguins like talking to strangers at parties?nBecause they find it hard to break the ice!", "role": "assistant" } } ], "created": 1690110711, "id": "chatcmpl-7fRI7RKqBl9y1oGv86ShTenOd9km5", "mannequin": "gpt-3.5-turbo-0613", "object": "chat.completion", "utilization": { "completion_tokens": 22, "prompt_tokens": 70, "total_tokens": 92 } }
Because the OpenAI module has already parsed the JSON into an object for us we don’t have to fret about this and we merely index into the thing like so ‘return end result["choices"][0]["message"]["content"]
‘ to get the response message from ChatGPT.
Now attempt operating your operate by including a print assertion to the underside of your file like so:
print(get_joke("Penguins"))
And also you get a response within the terminal that appears one thing like this:
Why do not penguins like speaking to strangers at events? As a result of they discover it onerous to interrupt the ice!
Okay, now that we’ve reviewed the naked fundamentals let’s get into the enjoyable stuff!
Making a Operate for ChatGPT to Name
Earlier than we go ham having ChatGPT calling capabilities for us we should always first create a operate for ChatGPT to name.
Let’s begin with a easy operate that can permit ChatGPT entry to an API that can lengthen its performance so we will make our joke generator extra highly effective. In your base listing, create a folder named ‘apis
‘ after which create a random_word.py
file within it like so:
> apis > random_word.py
Now add the next code to your random_word.py
file:
import requests def get_random_word() -> str: response = requests.get("https://random-word-api.vercel.app/api?phrases=1") return response.json()[0]
We first import the request
library so we will make an API name. Then we outline a operate known as get_random_word
, which is able to return a random phrase to us. We make a get request to the random phrase API, stating we solely need 1 phrase again, after which return index 0 of the JSON response, which would be the random phrase.
In case you’re not accustomed to the ‘-> str
‘ syntax, it’s only a kind trace we will add that states that this operate is meant to return a string. It’s not vital and doesn’t have an effect on the performance of the code, however with out going too deep into typing right here which we gained’t, typically it’s simply good to state that this operate we simply created will return a string kind, for readability.
Now let’s take a look at our operate by including the next code to the underside of your random_word.py
file:
print(get_random_word())
And it is best to get a random phrase printed to the terminal, like so:
demanding
Be sure that to remark out or take away the print(get_random_word())
line once more earlier than persevering with, as we don’t wish to print a random phrase each time we import this file.
Message Historical past
Nice! Now we’ve a operate that can return a random phrase to us, we will shut this file for now and use it in our ChatGPT operate calling later. Earlier than we get into operate calling although I wish to make yet one more helper operate for us to make use of all through this complete tutorial collection.
As ChatGPT will probably be telling us to name capabilities and we name them after which feed the return again into ChatGPT which in flip returns a message for the tip consumer once more the message historical past goes to get a bit sophisticated.
We’ll find yourself with a message historical past checklist of dictionaries that can look roughly like this:
[ {"role": "system", "content": "Setup here"}, {"role": "user", "content": "Query here"}, {"role": "assistant", //call a function //}, {"role": "function", "content": //function response//}, {"role": "assistant", "content": //ChatGPT response to enduser//}, ]
As the aim of this tutorial collection is to get understanding of how ChatGPT offers with operate calls, and the message historical past will usually be an enormous garbled mess with lengthy operate responses in there, we’ll create a easy helper to assist us fairly print the above model message historical past to the console. This may tremendously improve our studying expertise over the approaching tutorials as we will see precisely what we’re doing each step alongside the best way.
First, create a brand new folder known as 'utils'
in your base listing, after which create a brand new file known as 'printer.py'
within it like so:
> utils > printer.py
Now add the next code to your printer.py
file:
class ColorPrinter: _color_mapping = { "system": " 33[33m", # Yellow "user": " 33[32m", # Green "function": " 33[34m", # Blue "assistant": " 33[35m", # Purple "header": " 33[36m", # Cyan "undefined": " 33[37m", # White "closing_tag": " 33[00m", }
First, we define a class called ColorPrinter
, and then we define a dictionary called color_mapping
, which will map the different roles to different colors. We will use this to print the different roles in different colors to the console.
Note the in front of the dictionary name. This is a convention in Python that states that this variable is private, and should not be accessed outside of the class. It’s not enforced by the language, but it’s a convention that is good to follow. The weird-looking codes are ANSI escape codes, which are used to color text in the terminal.
Now add the following method to your ColorPrinter
class below the _color_mapping
property:
def _color_text_line(message) -> str: color_closing_tag = ColorPrinter._color_mapping["closing_tag"] attempt: function = message["role"] color_open_tag = ColorPrinter._color_mapping[role] besides KeyError: function = "undefined" color_open_tag = ColorPrinter._color_mapping[role] attempt: if message["content"]: message = message["content"] else: function_name = message["function_call"]["name"] function_args = message["function_call"]["arguments"] message = f"{function_name}({function_args})" besides KeyError: message = "undefined" return f"{color_open_tag}{function} : {message}{color_closing_tag}"
We use an _
in entrance of the identify once more, indicating that this technique is just for use inside the category itself.
We take a message as an argument and can return a string (the -> str
half is elective, however will help with code readability). We first set the color_closing_tag
variable to the closing tag from our _color_mapping
dictionary.
Then we attempt to get the function from the message, extracting its accompanying colour from the _color_mapping
object, and if it’s not there we set it to undefined.
The second attempt will get the content material from the message and saves it in a variable named 'message'
, but when there is no such thing as a content material we assume it’s a operate name and attempt to extract the operate identify and arguments from the message as an alternative. If there is no such thing as a operate name we set the message to undefined. Lastly, we return a string with the function and the message, with the colour tags round it.
Now add the next technique to your ColorPrinter
class under the _color_text_line
technique:
def color_print(messages) -> None: cyan_open_tag = ColorPrinter._color_mapping["header"] color_closing_tag = ColorPrinter._color_mapping["closing_tag"] print(f"n{cyan_open_tag}###### Dialog Historical past ######{color_closing_tag}") for message in messages: print(ColorPrinter._color_text_line(message)) print(f"{cyan_open_tag}##################################{color_closing_tag}n")
That is the general public interface, so we didn’t prepend an _
character. The strategy takes messages as an argument and returns nothing (None
), because it simply prints to the console (once more the -> None
half is elective for readability, we gained’t get deeper into kind hinting right here).
We first set the cyan_open_tag
and color_closing_tag
variables utilizing our _color_mapping
object, after which print a header to the console. We then loop over the messages and print every message to the console utilizing the _color_text_line
technique we outlined earlier. Lastly, we print a closing header to the console.
Your complete helper class contained in the utils/printer.py
file now seems to be like this:
class ColorPrinter: _color_mapping = { "system": " 33[33m", # Yellow "user": " 33[32m", # Green "function": " 33[34m", # Blue "assistant": " 33[35m", # Purple "header": " 33[36m", # Cyan "undefined": " 33[37m", # White "closing_tag": " 33[00m", } def _color_text_line(message) -> str: color_closing_tag = ColorPrinter._color_mapping["closing_tag"] attempt: function = message["role"] color_open_tag = ColorPrinter._color_mapping[role] besides KeyError: function = "undefined" color_open_tag = ColorPrinter._color_mapping[role] attempt: if message["content"]: message = message["content"] else: function_name = message["function_call"]["name"] function_args = message["function_call"]["arguments"] message = f"{function_name}({function_args})" besides KeyError: message = "undefined" return f"{color_open_tag}{function} : {message}{color_closing_tag}" def color_print(messages) -> None: cyan_open_tag = ColorPrinter._color_mapping["header"] color_closing_tag = ColorPrinter._color_mapping["closing_tag"] print(f"n{cyan_open_tag}###### Dialog Historical past ######{color_closing_tag}") for message in messages: print(ColorPrinter._color_text_line(message)) print(f"{cyan_open_tag}##################################{color_closing_tag}n")
That was various work for a easy helper operate, however it is going to assist us see and perceive precisely what’s going on for all of the remaining elements of this tutorial collection.
Calling a Operate from ChatGPT
Create a brand new file in your base listing named Ab_get_joke_w_function.py
.
(Once more, I will probably be prefixing the recordsdata with A/B/C, and so on, to make them alphabetically line up for readability as a tutorial/instructing device. That is clearly not a finest observe it is best to comply with exterior this tutorial collection.)
Inside this file first add some imports:
import openai from decouple import config from apis.random_word import get_random_word from utils.printer import ColorPrinter as Printer
First, we import openai
and config
(to learn the .env
file for our API key), after which we import each helpers we made earlier. Now let’s set our API key within the openai
module and outline our immediate setup in a separate variable.
openai.api_key = config("CHATGPT_API_KEY") JOKE_SETUP = """ You may be given a topic by the consumer. You'll return a joke, but it surely shouldn't be too lengthy (4 traces at most). You'll not present an introduction like 'This is a joke for you' however get straight into the joke. There's a operate known as 'get_random_word'. If the consumer doesn't present a topic, it is best to name this operate and use the end result as the topic. If the consumer does present a topic, you shouldn't name this operate. The one exception is that if the consumer asks for a random joke, during which case it is best to name the operate and use the end result as the topic. Instance: {consumer: 'penguins'} = Don't name the operate => present a joke about penguins. Instance: {consumer: ''} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'soul music'} = Don't name the operate => present a joke about soul music. Instance: {consumer: 'random'} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'guitars'} = Don't name the operate => present a joke about guitars. Instance: {consumer: 'give me a random joke'} = Name the operate => present a joke about the results of the operate. """
We outlined the immediate setup up prime as a result of it is vitally lengthy and higher put in a separate variable for code readability. You’ll discover the outline is kind of prolonged and consists of a variety of examples precisely of what we count on. While you’re having hassle getting ChatGPT to do precisely what you need it to do, examples are a strong device to make use of.
Now outline a operate named 'get_joke_result'
:
def get_joke_result(question): messages = [ {"role": "system", "content": JOKE_SETUP}, {"role": "user", "content": query}, ]
The operate takes a consumer question as an argument after which units up a message historical past checklist of dictionaries, which we’ll use to feed into ChatGPT. We set the primary message to be a system message and move within the setup we outlined earlier as content material. The second message is the consumer question that was handed into the operate.
Under the messages checklist, nonetheless inside your get_joke_result
operate, add a listing named ‘capabilities
‘:
capabilities = [ { "name": "get_random_word", "description": "Get a subject for your joke.", "parameters": { "type": "object", "properties": { "number_of_words": { "type": "integer", "description": "The number of words to generate.", } }, }, } ]
It is a checklist of dictionaries, which comprises the capabilities ChatGPT will be capable to name.
🧑💻 Observe that this isn’t an object that has any actual performance, it’s extra us describing in textual content what this operate does, so ChatGPT has a tough thought of what this operate is and what it may be used for. The operate identify doesn’t even must be the actual identify of the operate, as ChatGPT won’t instantly name the operate itself, which you’ll see in a second. For the outline simply actually say what the operate does.
Within the properties, we will describe the arguments the operate must be known as. ChatGPT will generate these arguments when requesting the operate name from us.
Though the get_random_word
helper operate we outlined doesn’t want any arguments it’s simpler to get ChatGPT to cease complaining if we request an argument, so we faux it wants a number_of_words
argument, which we’ll merely ignore later. We’ll get deeper into arguments within the coming elements.
Now add the next code under the capabilities checklist (nonetheless contained in the get_joke_result
operate):
first_response = openai.ChatCompletion.create( mannequin="gpt-3.5-turbo-0613", messages=messages, capabilities=capabilities, function_call="auto", # auto is default )["choices"][0]["message"] messages.append(first_response)
So we create a ChatCompletion
name utilizing the openai module and save the lead to a variable named first_response
.
We move within the mannequin we wish to use, the messages we outlined earlier, the capabilities we outlined earlier, and set the function_call
to auto
, which implies ChatGPT will get to determine whether or not or not a operate name is required.
The ["choices"][0]["message"]
on the finish is simply to index into the response object and get the message from it, which is what we save in our message historical past utilizing the append technique.
Now under and nonetheless contained in the get_joke_result
operate add:
if first_response.get("function_call"): function_response = get_random_word() messages.append( { "function": "operate", "identify": "get_random_word", "content material": function_response, } ) second_response = openai.ChatCompletion.create( mannequin="gpt-3.5-turbo-0613", messages=messages, )["choices"][0]["message"] messages.append(second_response) Printer.color_print(messages) return second_response["content"] Printer.color_print(messages) return first_response["content"]
We take a look at if the first_response
we acquired again has a property named ‘function_call
‘, indicating that ChatGPT needed to name a operate. If it needed to name a operate, we merely set a variable named ‘function_response
‘ to the results of calling our get_random_word
helper.
We ignore the argument as said earlier than, and solely have a single potential operate that may be known as for now, so we don’t have to fret about that. Observe that we’re those truly calling the operate, not ChatGPT!
We then append a brand new message to our message historical past, which is the operate response. We set the function to operate, the identify to the identify of the operate, and the content material to the operate response.
We then create a brand new ChatCompletion
name, passing within the mannequin and the messages, now containing the results of our operate name as properly, and save the lead to a variable named second_response
. We append the second_response
to our message historical past after which print the message historical past to the console utilizing our helper operate.
Lastly, we return the content material of the second_response
as the ultimate end result.
If the first_response
didn’t have a function_call
property, we merely bypass the if block and print the message historical past to the console and return the content material of the first_response
as the ultimate end result.
Your get_joke_result
operate now seems to be like this:
def get_joke_result(question): messages = [ {"role": "system", "content": JOKE_SETUP}, {"role": "user", "content": query}, ] capabilities = [ { "name": "get_random_word", "description": "Get a subject for your joke.", "parameters": { "type": "object", "properties": { "number_of_words": { "type": "integer", "description": "The number of words to generate.", } }, }, } ] first_response = openai.ChatCompletion.create( mannequin="gpt-3.5-turbo-0613", messages=messages, capabilities=capabilities, function_call="auto", # auto is default )["choices"][0]["message"] messages.append(first_response) if first_response.get("function_call"): function_response = get_random_word() messages.append( { "function": "operate", "identify": "get_random_word", "content material": function_response, } ) second_response = openai.ChatCompletion.create( mannequin="gpt-3.5-turbo-0613", messages=messages, )["choices"][0]["message"] messages.append(second_response) Printer.color_print(messages) return second_response["content"] Printer.color_print(messages) return first_response["content"]
So let’s do this out. First, add the next to the underside of your file:
print(get_joke_result("penguins"))
After which run your file. You need to get a response within the terminal that appears one thing like this:
###### Dialog Historical past ###### system : You may be given a topic by the consumer. You'll return a joke, but it surely shouldn't be too lengthy (4 traces at most). You'll not present an introduction like 'This is a joke for you' however get straight into the joke. There's a operate known as 'get_random_word'. If the consumer doesn't present a topic, it is best to name this operate and use the end result as the topic. If the consumer does present a topic, you shouldn't name this operate. The one exception is that if the consumer asks for a random joke, during which case it is best to name the operate and use the end result as the topic. Instance: {consumer: 'penguins'} = Don't name the operate => present a joke about penguins. Instance: {consumer: ''} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'soul music'} = Don't name the operate => present a joke about soul music. Instance: {consumer: 'random'} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'guitars'} = Don't name the operate => present a joke about guitars. Instance: {consumer: 'give me a random joke'} = Name the operate => present a joke about the results of the operate. consumer : penguins assistant : Why do not penguins like speaking to strangers at events? They discover it onerous to interrupt the ice! ################################## Why do not penguins like speaking to strangers at events? They discover it onerous to interrupt the ice!
As we will see, no operate was known as, because the consumer offered a sound topic. Up to now so good.
Now change the print assertion with the next print assertion:
print(get_joke_result("random"))
And run your file once more. You need to get a response within the terminal that appears one thing like this:
###### Dialog Historical past ###### system : You may be given a topic by the consumer. You'll return a joke, but it surely shouldn't be too lengthy (4 traces at most). You'll not present an introduction like 'This is a joke for you' however get straight into the joke. There's a operate known as 'get_random_word'. If the consumer doesn't present a topic, it is best to name this operate and use the end result as the topic. If the consumer does present a topic, you shouldn't name this operate. The one exception is that if the consumer asks for a random joke, during which case it is best to name the operate and use the end result as the topic. Instance: {consumer: 'penguins'} = Don't name the operate => present a joke about penguins. Instance: {consumer: ''} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'soul music'} = Don't name the operate => present a joke about soul music. Instance: {consumer: 'random'} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'guitars'} = Don't name the operate => present a joke about guitars. Instance: {consumer: 'give me a random joke'} = Name the operate => present a joke about the results of the operate. consumer : random assistant : get_random_word({}) operate : unlikable assistant : Why did the unlikable laptop go to remedy? It had too many unhealthy connections! ################################## Why did the unlikable laptop go to remedy? It had too many unhealthy connections!
We are able to see from our fairly printed historical past (which could have colours in your terminal) that the assistant first requested us to name a operate. The operate returned a phrase and the assistant (ChatGPT) then used that phrase as the topic for the joke. Nice!
Now change the print assertion with the next print assertion to complete up our testing and see what occurs if the consumer gives no question in any respect:
print(get_joke_result(""))
Go forward and run your file once more:
###### Dialog Historical past ###### system : You may be given a topic by the consumer. You'll return a joke, but it surely shouldn't be too lengthy (4 traces at most). You'll not present an introduction like 'This is a joke for you' however get straight into the joke. There's a operate known as 'get_random_word'. If the consumer doesn't present a topic, it is best to name this operate and use the end result as the topic. If the consumer does present a topic, you shouldn't name this operate. The one exception is that if the consumer asks for a random joke, during which case it is best to name the operate and use the end result as the topic. Instance: {consumer: 'penguins'} = Don't name the operate => present a joke about penguins. Instance: {consumer: ''} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'soul music'} = Don't name the operate => present a joke about soul music. Instance: {consumer: 'random'} = Name the operate => present a joke about the results of the operate. Instance: {consumer: 'guitars'} = Don't name the operate => present a joke about guitars. Instance: {consumer: 'give me a random joke'} = Name the operate => present a joke about the results of the operate. consumer : undefined assistant : Are you able to inform me a joke? operate : payee assistant : Why did the payee convey a ladder to the financial institution? As a result of they heard the rates of interest had been climbing! ################################## Why did the payee convey a ladder to the financial institution? As a result of they heard the rates of interest had been climbing!
Yep, nonetheless works wonderful! Observe that the operate name solely triggered after we offered no topic or requested for one thing random. If we offer a topic ChatGPT will simply work as regular because it doesn’t want the operate.
Now that we all know tips on how to make a easy operate name let’s take issues to the following degree partly 2. See you there!
💡 Full Course with Movies and Course Certificates (PDF): https://academy.finxter.com/college/openai-api-function-calls-and-embeddings/