I just finished prototyping a project that uses the ChatGPT API, and I used its function call feature to streamline my code. However, I didn't find much online about how to use this feature with the NodeJS openai library, so I thought I'd do a write up on that here.

The function call feature allows you to describe a function to ChatGPT, and get a JSON string in return with arguments which you can then use to call that function. Note that ChatGPT will not actually call the function itself as part of this API - instead it returns JSON which you need to then parse, validate, and then use to call the function yourself later in your code.

The basics

This tutorial assumes you've set up your configuration already, which will look something like this:

const { Configuration, OpenAIApi } = require('openai')
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
})
const openai = new OpenAIApi(configuration)

Now, you have an openai object which you can use to make the API calls.

Now, to make an API call, you'll be making a call to createChatCompletion() which looks something like this:

  const completion = await openai.createChatCompletion({
    model: 'gpt-3.5-turbo',
    messages: [
      {
        role: 'user',
        content: `Grade the following homework answer with a grade from 0 to 100. Add some comments on the student's work. Homework question: ${question} Homework answer: ${answer}`
      }
    ],
    functions: [
       // ... your functions will be described here!
    ]
 })

First, you'll specify the model, and then you'll specify the messages you want to send to ChatGPT. You can give it some instructions in this message about the function you want it to call. This tutorial will focus on the function part, so I won't go into further detail about the rest here.

For this simple example, imagine we are asking ChatGPT to act as a grader for a homework question and answer given by a student. We'll ask it to grade the assignment, and then return JSON that can be used to call the following function:

const gradeAssignment = (grade, comments) => {
  // ... code that processes the grade and comments
}

Describing functions

The next item, functions, should be an array where each element is an object describing a function that can be called. First, the object will contain a name and description of the function:

   functions: [
     {
       name: "gradeAssignment",
       description: "Grades the homework assignment",
       parameters: {
         'type': 'object',
     'properties': {
       // ... 
     }
       }
     }
   ]

The third item, parameters, is an object that describes what inputs the function takes. Function parameters are described using JSON Schema, and the top level must be type "object". One way to think about it is that the Schema describes an object whose properties are the function inputs.

Our gradeAssignment() function takes two inputs, an integer grade, and a string comments. We use the JSON Schema Reference in the link above to find out how to represent these types. We list them under properties like this:

'properties': {
  'grade': {
    'type': 'integer',
    'description': 'The grade for the assignment, between 0 and 100'
  },
  'comments': {
    'type': 'string',
    'description': 'Additional comments on the student work'
  }
}

Our full code will now look like this:

  const completion = await openai.createChatCompletion({
    model: 'gpt-3.5-turbo',
    messages: [
      {
        role: 'user',
        content: `Grade the following homework answer with a grade from 0 to 100. Add some comments on the student's work. Homework question: ${question} Homework answer: ${answer}`
      }
    ],
    functions: [
      {
        name: "gradeAssignment",
        description: "Grades the homework assignment",
        parameters: {
          'type': 'object',
          'properties': {
            'grade': {
              'type': 'integer',
              'description': 'The grade for the assignment, between 0 and 100'
            },
            'comments': {
              'type': 'string',
              'description': 'Additional comments on the student work'
            }     
      }
        }
      }
    ]
 })

As you can see, it gets very nested, but hopefully you can understand it since we've broken it down step by step.

Getting the response

The code above asynchronously calls the API and gets the result in the variable completion. Now, it's relatively simple to use this result.

(The OpenAI API Reference describes the structure of the result you will get here, but that doesn't contain a function calling example. The function calling result is described by the code example here. This is a bit hard to understand, so I'll just explain what you need to do.)

First, get the completion response:

const completionResponse = completion.data.choices[0].message

Now, if completionResponse.function_call exists, then that means ChatGPT wanted to call a function. completionResponse.function_call.arguments will give you the arguments as a JSON string, which you'll want to parse using JSON.parse().

if(completionResponse.function_call) {
  const arguments = JSON.parse(completionResponse.function_call.arguments)
}

Now, you can call the function like this:

gradeAssignment(arguments.grade, arguments.comments)

And that's it!

Further notes

Multiple functions

You can also give ChatGPT multiple functions to choose from. As mentioned above, functions in your request is an array describing all of the functions we can call. Additional functions will be described the same way, and simply added to this array.

When parsing the response, completionResponse.function_call.name will give you the name of the function it wants to call.

Using without calling a function

As you may have figured out, this API feature can also be used to get a response from ChatGPT in an easy JSON format. You can use its returned JSON for anything - the function you describe to it does not actually have to exist in your code.