Update the node example as "function_call" is deprecated. (#934)

Co-authored-by: Logan Kilpatrick <23kilpatrick23@gmail.com>
This commit is contained in:
Haomin Liu 2023-12-19 23:29:31 +08:00 committed by GitHub
parent 561b3afd6e
commit 82efc971d1

View File

@ -4,10 +4,6 @@ OpenAI functions enable your app to take action based on user inputs. This means
In this tutorial, you will build an app that uses OpenAI functions along with the latest version of the Node.js SDK. The app runs in the browser, so you only need a code editor and, e.g., VS Code Live Server to follow along locally. Alternatively, write your code directly in the browser via [this code playground at Scrimba.](https://scrimba.com/scrim/c6r3LkU9) In this tutorial, you will build an app that uses OpenAI functions along with the latest version of the Node.js SDK. The app runs in the browser, so you only need a code editor and, e.g., VS Code Live Server to follow along locally. Alternatively, write your code directly in the browser via [this code playground at Scrimba.](https://scrimba.com/scrim/c6r3LkU9)
If you prefer watching screencasts over reading, then you can check out [this scrim, which walks through the code line-by-line:](https://scrimba.com/scrim/co0044b2d9b9b7f5bf16e0391)
<ScrimPlayer scrimId="co0044b2d9b9b7f5bf16e0391" />
## What you will build ## What you will build
Our app is a simple agent that helps you find activities in your area. Our app is a simple agent that helps you find activities in your area.
@ -71,34 +67,41 @@ async function getCurrentWeather(latitude, longitude) {
For OpenAI to understand the purpose of these functions, we need to For OpenAI to understand the purpose of these functions, we need to
describe them using a specific schema. We'll create an array called describe them using a specific schema. We'll create an array called
`functionDefinitions` that contains one object per function. Each object `tools` that contains one object per function. Each object
will have three keys: `name`, `description`, and `parameters`. will have two keys: `type`, `function`, and the `function` key has
three subkeys: `name`, `description`, and `parameters`.
```js ```js
const functionDefinitions = [ const tools = [
{ {
type: "function",
function: {
name: "getCurrentWeather", name: "getCurrentWeather",
description: "Get the current weather in a given location", description: "Get the current weather in a given location",
parameters: { parameters: {
type: "object", type: "object",
properties: { properties: {
longitude: { latitude: {
type: "string", type: "string",
}, },
latitude: { longitude: {
type: "string", type: "string",
}, },
}, },
required: ["longitude", "latitude"], required: ["longitude", "latitude"],
}, },
}
}, },
{ {
type: "function",
function: {
name: "getLocation", name: "getLocation",
description: "Get the user's location based on their IP address", description: "Get the user's location based on their IP address",
parameters: { parameters: {
type: "object", type: "object",
properties: {}, properties: {},
}, },
}
}, },
]; ];
``` ```
@ -136,7 +139,7 @@ async function agent(userInput) {
const response = await openai.chat.completions.create({ const response = await openai.chat.completions.create({
model: "gpt-4", model: "gpt-4",
messages: messages, messages: messages,
functions: functionDefinitions, tools: tools,
}); });
console.log(response); console.log(response);
} }
@ -151,9 +154,9 @@ properties:
GPT-4). GPT-4).
- `messages` - The entire history of messages between the user and the - `messages` - The entire history of messages between the user and the
AI up until this point. AI up until this point.
- `functions` - A description of the functions our app has access to. - `tools` - A list of tools the model may call. Currently, only
Here, we'll we use the `functionDefinitions` array we created functions are supported as a tool., we'll we use the `tools` array we
earlier. created earlier.
## Running our app with a simple input ## Running our app with a simple input
@ -177,35 +180,41 @@ to the console like this:
message: { message: {
role: "assistant", role: "assistant",
content: null, content: null,
function_call: { tool_calls: [
name: "getLocation", // The function OpenAI wants us to call id: "call_CBwbo9qoXUn1kTR5pPuv6vR1",
type: "function",
function: {
name: "getLocation",
arguments: "{}" arguments: "{}"
} }
]
}, },
finish_reason: "function_call" // OpenAI wants us to call a function logprobs: null,
finish_reason: "tool_calls" // OpenAI wants us to call a function
}], }],
usage: { usage: {
prompt_tokens: 134, prompt_tokens: 134,
completion_tokens: 6, completion_tokens: 6,
total_tokens: 140 total_tokens: 140
} }
system_fingerprint: null
} }
``` ```
This response tells us that we should call one of our functions, as it contains the following key: `finish:_reason: "function_call"`. This response tells us that we should call one of our functions, as it contains the following key: `finish_reason: "tool_calls"`.
The name of the function can be found in the The name of the function can be found in the
`response.choices[0].message.function_call.name` key, which is set to `response.choices[0].message.tool_calls[0].function.name` key, which is set to
`"getLocation"`. `"getLocation"`.
## Turning the OpenAI response into a function call ## Turning the OpenAI response into a function call
Now that we have the name of the function as a string, we'll need to Now that we have the name of the function as a string, we'll need to
translate that into a function call. To help us with that, we'll gather translate that into a function call. To help us with that, we'll gather
both of our functions in an object called `availableFunctions`: both of our functions in an object called `availableTools`:
```js ```js
const availableFunctions = { const availableTools = {
getCurrentWeather, getCurrentWeather,
getLocation, getLocation,
}; };
@ -213,15 +222,15 @@ const availableFunctions = {
This is handy because we'll be able to access the `getLocation` function This is handy because we'll be able to access the `getLocation` function
via bracket notation and the string we got back from OpenAI, like this: via bracket notation and the string we got back from OpenAI, like this:
`availableFunctions["getLocation"]`. `availableTools["getLocation"]`.
```js ```js
const { finish_reason, message } = response.choices[0]; const { finish_reason, message } = response.choices[0];
if (finish_reason === "function_call") { if (finish_reason === "tool_calls" && message.tool_calls) {
const functionName = message.function_call.name; const functionName = message.tool_calls[0].function.name;
const functionToCall = availableFunctions[functionName]; const functionToCall = availableTools[functionName];
const functionArgs = JSON.parse(message.function_call.arguments); const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
const functionArgsArr = Object.values(functionArgs); const functionArgsArr = Object.values(functionArgs);
const functionResponse = await functionToCall.apply(null, functionArgsArr); const functionResponse = await functionToCall.apply(null, functionArgsArr);
console.log(functionResponse); console.log(functionResponse);
@ -229,7 +238,7 @@ if (finish_reason === "function_call") {
``` ```
We're also grabbing ahold of any arguments OpenAI wants us to pass into We're also grabbing ahold of any arguments OpenAI wants us to pass into
the function: `message.function_call.arguments`. the function: `message.tool_calls[0].function.arguments`.
However, we won't need any arguments for this first function call. However, we won't need any arguments for this first function call.
If we run the code again with the same input If we run the code again with the same input
@ -273,7 +282,7 @@ simplicity.
At the top of the `agent` function, we'll create a loop that lets us run At the top of the `agent` function, we'll create a loop that lets us run
the entire procedure up to five times. the entire procedure up to five times.
If we get back `finish_reason: "function_call"` from GPT, we'll just If we get back `finish_reason: "tool_calls"` from GPT, we'll just
push the result of the function call to the `messages` array and jump to push the result of the function call to the `messages` array and jump to
the next iteration of the loop, triggering a new request. the next iteration of the loop, triggering a new request.
@ -285,14 +294,14 @@ for (let i = 0; i < 5; i++) {
const response = await openai.chat.completions.create({ const response = await openai.chat.completions.create({
model: "gpt-4", model: "gpt-4",
messages: messages, messages: messages,
functions: functionDefinitions, tools: tools,
}); });
const { finish_reason, message } = response.choices[0]; const { finish_reason, message } = response.choices[0];
if (finish_reason === "function_call") { if (finish_reason === "tool_calls" && message.tool_calls) {
const functionName = message.function_call.name; const functionName = message.tool_calls[0].function.name;
const functionToCall = availableFunctions[functionName]; const functionToCall = availableTools[functionName];
const functionArgs = JSON.parse(message.function_call.arguments); const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
const functionArgsArr = Object.values(functionArgs); const functionArgsArr = Object.values(functionArgs);
const functionResponse = await functionToCall.apply(null, functionArgsArr); const functionResponse = await functionToCall.apply(null, functionArgsArr);
@ -354,8 +363,8 @@ to call the `getCurrentWeather` function with
arguments. This is data it got back from the first function call we did. arguments. This is data it got back from the first function call we did.
```js ```js
{role: "assistant", content: null, function_call: {name: "getLocation", arguments: "{}"}} {"role":"assistant","content":null,"tool_calls":[{"id":"call_Cn1KH8mtHQ2AMbyNwNJTweEP","type":"function","function":{"name":"getLocation","arguments":"{}"}}]}
{role: "assistant", content: null, function_call: {name: "getCurrentWeather", arguments: " { "longitude": "10.859", "latitude": "59.955" }"}} {"role":"assistant","content":null,"tool_calls":[{"id":"call_uc1oozJfGTvYEfIzzcsfXfOl","type":"function","function":{"name":"getCurrentWeather","arguments":"{\n\"latitude\": \"10.859\",\n\"longitude\": \"59.955\"\n}"}}]}
``` ```
You've now built an AI agent using OpenAI functions and the Node.js SDK! If you're looking for an extra challenge, consider enhancing this app. For example, you could add a function that fetches up-to-date information on events and activities in the user's location. You've now built an AI agent using OpenAI functions and the Node.js SDK! If you're looking for an extra challenge, consider enhancing this app. For example, you could add a function that fetches up-to-date information on events and activities in the user's location.
@ -386,11 +395,12 @@ async function getCurrentWeather(latitude, longitude) {
return weatherData; return weatherData;
} }
const functionDefinitions = [ const tools = [
{ {
type: "function",
function: {
name: "getCurrentWeather", name: "getCurrentWeather",
description: description: "Get the current weather in a given location",
"Get the current weather in a given location given in latitude and longitude",
parameters: { parameters: {
type: "object", type: "object",
properties: { properties: {
@ -403,18 +413,22 @@ const functionDefinitions = [
}, },
required: ["longitude", "latitude"], required: ["longitude", "latitude"],
}, },
}
}, },
{ {
type: "function",
function: {
name: "getLocation", name: "getLocation",
description: "Get the user's location based on their IP address", description: "Get the user's location based on their IP address",
parameters: { parameters: {
type: "object", type: "object",
properties: {}, properties: {},
}, },
}
}, },
]; ];
const availableFunctions = { const availableTools = {
getCurrentWeather, getCurrentWeather,
getLocation, getLocation,
}; };
@ -436,15 +450,15 @@ async function agent(userInput) {
const response = await openai.chat.completions.create({ const response = await openai.chat.completions.create({
model: "gpt-4", model: "gpt-4",
messages: messages, messages: messages,
functions: functionDefinitions, tools: tools,
}); });
const { finish_reason, message } = response.choices[0]; const { finish_reason, message } = response.choices[0];
if (finish_reason === "function_call") { if (finish_reason === "tool_calls" && message.tool_calls) {
const functionName = message.function_call.name; const functionName = message.tool_calls[0].function.name;
const functionToCall = availableFunctions[functionName]; const functionToCall = availableTools[functionName];
const functionArgs = JSON.parse(message.function_call.arguments); const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
const functionArgsArr = Object.values(functionArgs); const functionArgsArr = Object.values(functionArgs);
const functionResponse = await functionToCall.apply( const functionResponse = await functionToCall.apply(
null, null,