Last active
February 7, 2024 20:27
-
-
Save stephenlb/de558e9802a6f950951eb0939ab71bcd to your computer and use it in GitHub Desktop.
Using OpenAI's Function Calling with a JavaScript Fetch API.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Import Modules | |
// | |
const pubnub = require('pubnub'); | |
const xhr = require('xhr'); | |
const vault = require('vault'); | |
// | |
// Request Event Handler (Main) | |
// | |
export default (request, response) => { | |
// Natural language request input for AI model | |
let naturalCommand = request.body; | |
// Ask AI to make decisions and provide progress updates | |
return getOpenaiApiKey().then(apikey => { | |
return openAI(naturalCommand).then(aiResponse => { | |
console.log("OPENAI TASK RESULT:", aiResponse); | |
return response.send(aiResponse); | |
}); | |
}); | |
}; | |
// | |
// Get API Key for OpenAI | |
// Key is populated via Vault Secret Manager | |
// | |
let OPENAI_API_KEY = null; | |
function getOpenaiApiKey() { | |
// Use cached key | |
if (OPENAI_API_KEY) { | |
return new Promise(resolve => resolve(OPENAI_API_KEY)); | |
} | |
// Fetch key from vault | |
return vault.get("OPENAI_API_KEY").then(apikey => { | |
OPENAI_API_KEY = apikey; | |
return new Promise(resolve => resolve(OPENAI_API_KEY)); | |
}); | |
} | |
// | |
// Publish a Message to a Channel | |
// | |
function publishMessage(channel, message) { | |
return pubnub.publish({ | |
channel: channel, | |
message: message, | |
}); | |
} | |
publishMessage.instructions = { | |
"name": "publishMessage", | |
"description": "Send a message to a channel", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"channel": { | |
"type": "string", | |
"description": "The name of the channel to send a message to", | |
}, | |
"message": {"type": "object", "properties": { | |
"text" : { "type": "string", "description": "The message text that will be sent to the channel"} | |
}}, | |
}, | |
"required": ["channel", "message"], | |
} | |
}; | |
// | |
// Get the Last Message from a Channel | |
// | |
function lastMessage(channel) { | |
return pubnub.history({ | |
channel: channel, | |
count: 1, | |
}); | |
} | |
lastMessage.instructions = { | |
"name": "lastMessage", | |
"description": "Get the most recent message that was sent to a channel", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"channel": { | |
"type": "string", | |
"description": "The name of the channel to send a message to", | |
}, | |
}, | |
"required": ["channel"], | |
} | |
}; | |
// | |
// API Call to OpenAI asking the AI to run functions if it thinks it needs to | |
// | |
function openAI(naturalCommand, messages) { | |
// Generate Function Instructions for the AI | |
const functions = [publishMessage, lastMessage].map(fn => fn.instructions); | |
const url = 'https://api.openai.com/v1/chat/completions'; | |
let msgs = messages || [{"role": "user", "content": naturalCommand}]; | |
const http_options = { | |
'method': 'POST', | |
'headers': { | |
"Content-Type": "application/json", | |
"Authorization": `Bearer ${OPENAI_API_KEY}`, | |
}, | |
'body': JSON.stringify({ | |
"model": "gpt-3.5-turbo-0613", | |
"messages": msgs, | |
"functions": functions, | |
"function_call": "auto", | |
}), | |
}; | |
return xhr.fetch(url, http_options) | |
.then((resp) => { | |
const body = JSON.parse(resp.body); | |
const function_call = body.choices[0].finish_reason === "function_call"; | |
const message = body.choices[0].message; | |
// Call function and resend to OpenAI the result | |
if (function_call) { | |
return handelFunctionCalls(message).then(result => { | |
msgs.push(message); | |
msgs.push({ | |
"role": "function", | |
"name": message.function_call.name, | |
"content": JSON.stringify(result), | |
}); | |
return openAI(naturalCommand, msgs); | |
}); | |
} | |
// Send status update about completed task | |
const content = message.content; | |
return new Promise(resolve => resolve(content)); | |
}) | |
.catch((err) => { | |
return new Promise((_, reject) => reject(err)); | |
}); | |
} | |
// | |
// Handel Function Calls from OpenAI Requests | |
// | |
function handelFunctionCalls(message) { | |
let args = JSON.parse(message.function_call.arguments); | |
switch (message.function_call.name) { | |
case "publishMessage": | |
return publishMessage(args.channel, args.message); | |
break; | |
case "lastMessage": | |
return lastMessage(args.channel); | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Read more about this at: OpenAI Function Calling
Here is an example of what the user would supply as input:
That's it! Yes your user can ask simple questions. And your API can respond with with natural language as the output after completing the task:
The AI requested the API to be called, and provided a natural language result as the task was completed successfully. Amazing!