├── .gitignore ├── README.md ├── getCurrentTime.js ├── index.js ├── openai-function-calling-tools-test.js ├── package-lock.json ├── package.json └── timeToSport.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAI Function Calling Nodejs Example 2 | 3 | ## Introduction 4 | This is a simple example of using OpenAI's API to call a function in Nodejs. The function is a simple calculator that takes two numbers and an operator and returns the result. The function is called using the OpenAI API and the result is returned to the user. 5 | 6 | ## Installation 7 | 8 | ```bash 9 | npm i 10 | ``` 11 | 12 | ## Usage 13 | there are 3 scripts to play with Function Calling API. You can run them with the following commands: 14 | 15 | ```bash 16 | # calculator 17 | npm start 18 | 19 | # getCurrentTime 20 | # 1. get the time 21 | npm run getCurrentTime 22 | 23 | # timeToSport: 24 | # 1. get the time 25 | # 2. choose a sport according to the time 26 | npm run timeToSport 27 | ``` 28 | 29 | ## Output 30 | 31 | ```bash 32 | ❯ npm start 33 | 34 | > openai-function-calling-nodejs@1.0.0 test 35 | > node index.js 36 | 37 | 22 + 5 = 27 (decimal) 38 | 27 + A = 31 (hex) 39 | The result of adding 22 and 5 in decimal, and then adding the hexadecimal number A, is 31. 40 | ``` 41 | 42 | ## License 43 | MIT -------------------------------------------------------------------------------- /getCurrentTime.js: -------------------------------------------------------------------------------- 1 | import { Configuration, OpenAIApi } from "openai"; 2 | 3 | const configuration = new Configuration({ 4 | apiKey: process.env.OPENAI_API_KEY, 5 | }); 6 | const openai = new OpenAIApi(configuration); 7 | 8 | const QUESTION = "现在几点?"; 9 | 10 | const messages = [ 11 | { 12 | role: "user", 13 | content: QUESTION, 14 | }, 15 | ]; 16 | 17 | // Define the functions that you want to be able to call from the chat 18 | // 定义 chatGPT 可以使用的函数 19 | const functions = { 20 | // 获取当前时间 21 | // get current time 22 | getCurrentTime: function () { 23 | var date = new Date(); 24 | var hours = date.getHours(); 25 | var minutes = date.getMinutes(); 26 | 27 | var time = hours + ":" + minutes; 28 | 29 | return time; 30 | }, 31 | }; 32 | 33 | const getCompletion = async (messages) => { 34 | const response = await openai.createChatCompletion({ 35 | model: "gpt-3.5-turbo-0613", 36 | messages, 37 | functions: [ 38 | { 39 | name: "getCurrentTime", 40 | description: "Get the current time", 41 | parameters: { 42 | type: "object", 43 | properties: {}, 44 | }, 45 | }, 46 | ], 47 | temperature: 0, 48 | }); 49 | 50 | return response; 51 | }; 52 | 53 | let response = await getCompletion(messages); 54 | 55 | if (response.data.choices[0].finish_reason === "function_call") { 56 | const fnName = response.data.choices[0].message.function_call.name; 57 | const args = response.data.choices[0].message.function_call.arguments; 58 | 59 | console.log("Function call: " + fnName); 60 | console.log("Arguments: " + args); 61 | 62 | // call the function 63 | const fn = functions[fnName]; 64 | const result = fn(...Object.values(args)); 65 | 66 | console.log("Calling Function Result: " + result); 67 | 68 | messages.push({ 69 | role: "assistant", 70 | content: null, 71 | function_call: { 72 | name: fnName, 73 | arguments: args, 74 | }, 75 | }); 76 | 77 | messages.push({ 78 | role: "function", 79 | name: fnName, 80 | content: JSON.stringify({ result: result }), 81 | }); 82 | 83 | // call the completion again 84 | response = await getCompletion(messages); 85 | 86 | console.log(response.data.choices[0].message.content); 87 | } 88 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { Configuration, OpenAIApi } from "openai"; 2 | 3 | const configuration = new Configuration({ 4 | apiKey: process.env.OPENAI_API_KEY, 5 | }); 6 | const openai = new OpenAIApi(configuration); 7 | 8 | const QUESTION = 9 | "What's the result of 22 plus 5 in decimal added to the hexadecimal number A?"; 10 | 11 | const messages = [ 12 | { 13 | role: "user", 14 | content: QUESTION, 15 | }, 16 | ]; 17 | 18 | // Define the functions that you want to be able to call from the chat 19 | // 定义了两个函数,分别用于将十进制和十六进制的值相加 20 | const functions = { 21 | // add decimal values 22 | // 两个十进制的值相加 23 | addDecimalValues: function (value1, value2) { 24 | var result = value1 + value2; 25 | console.log(value1 + " + " + value2 + " = " + result + " (decimal)"); 26 | 27 | return result; 28 | }, 29 | 30 | // add hexadecimal values 31 | // 两个十六进制的值相加 32 | addHexadecimalValues: function (value1, value2) { 33 | var decimal1 = parseInt(value1, 16); 34 | var decimal2 = parseInt(value2, 16); 35 | 36 | var result = (decimal1 + decimal2).toString(16); 37 | console.log(value1 + " + " + value2 + " = " + result + " (hex)"); 38 | 39 | return result; 40 | }, 41 | }; 42 | 43 | const getCompletion = async (messages) => { 44 | const response = await openai.createChatCompletion({ 45 | model: "gpt-3.5-turbo-0613", 46 | messages, 47 | // 重点:这里定义了 ChatCompletion 可以使用的函数 48 | // important: define the functions that can be used by the ChatCompletion 49 | functions: [ 50 | { 51 | name: "addDecimalValues", 52 | description: "Add two decimal values", 53 | parameters: { 54 | type: "object", 55 | properties: { 56 | value1: { 57 | type: "integer", 58 | description: "The first decimal value to add. For example, 5", 59 | }, 60 | value2: { 61 | type: "integer", 62 | description: "The second decimal value to add. For example, 10", 63 | }, 64 | }, 65 | required: ["value1", "value2"], 66 | }, 67 | }, 68 | { 69 | name: "addHexadecimalValues", 70 | description: "Add two hexadecimal values", 71 | parameters: { 72 | type: "object", 73 | properties: { 74 | value1: { 75 | type: "string", 76 | description: "The first hexadecimal value to add. For example, 5", 77 | }, 78 | value2: { 79 | type: "string", 80 | description: 81 | "The second hexadecimal value to add. For example, A", 82 | }, 83 | }, 84 | required: ["value1", "value2"], 85 | }, 86 | }, 87 | ], 88 | temperature: 0, 89 | }); 90 | 91 | return response; 92 | }; 93 | 94 | let response; 95 | while (true) { 96 | response = await getCompletion(messages); 97 | 98 | // the output: 99 | // finish_reason: function_call 100 | // 101 | // message: { 102 | // role: 'assistant', 103 | // content: null, 104 | // function_call: { 105 | // name: 'addDecimalValues', 106 | // arguments: '{\n "value1": 22,\n "value2": 5\n}' 107 | // } 108 | // } 109 | 110 | if (response.data.choices[0].finish_reason === "stop") { 111 | console.log(response.data.choices[0].message.content); 112 | break; 113 | } else if (response.data.choices[0].finish_reason === "function_call") { 114 | // get the function name and arguments from the response 115 | // 从响应中获取函数名和参数 116 | const fnName = response.data.choices[0].message.function_call.name; 117 | const args = response.data.choices[0].message.function_call.arguments; 118 | 119 | // call the function 120 | // 调用函数 121 | const functionToCall = functions[fnName]; 122 | const { value1, value2 } = JSON.parse(args); 123 | const result = functionToCall(value1, value2); 124 | 125 | messages.push({ 126 | role: "assistant", 127 | content: null, 128 | function_call: { 129 | name: fnName, 130 | arguments: args, 131 | }, 132 | }); 133 | 134 | messages.push({ 135 | role: "function", 136 | name: fnName, 137 | content: JSON.stringify({ result: result }), 138 | }); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /openai-function-calling-tools-test.js: -------------------------------------------------------------------------------- 1 | import { Configuration, OpenAIApi } from "openai"; 2 | import { JavaScriptInterpreter } from "openai-function-calling-tools" 3 | 4 | const configuration = new Configuration({ 5 | apiKey: process.env.OPENAI_API_KEY, 6 | }); 7 | const openai = new OpenAIApi(configuration); 8 | 9 | const QUESTION = "What is 0.1 + 0.2 ?"; 10 | const messages = [ 11 | { 12 | role: "user", 13 | content: QUESTION, 14 | }, 15 | ]; 16 | 17 | const { javaScriptInterpreter, javaScriptInterpreterSchema } = 18 | new JavaScriptInterpreter(); 19 | 20 | const functions = { 21 | javaScriptInterpreter, 22 | }; 23 | 24 | const getCompletion = async (messages) => { 25 | const response = await openai.createChatCompletion({ 26 | model: "gpt-3.5-turbo-0613", 27 | messages, 28 | functions: [javaScriptInterpreterSchema], 29 | temperature: 0, 30 | }); 31 | 32 | return response; 33 | }; 34 | 35 | console.log("Question: " + QUESTION); 36 | 37 | let response; 38 | while (true) { 39 | response = await getCompletion(messages); 40 | const { finish_reason, message } = response.data.choices[0]; 41 | 42 | if (finish_reason === "stop") { 43 | console.log(message.content); 44 | break; 45 | } else if (finish_reason === "function_call") { 46 | const { name: fnName, arguments: args } = message.function_call; 47 | 48 | const fn = functions[fnName]; 49 | const parsedArgs = JSON.parse(args); 50 | const argValues = Object.values(parsedArgs); 51 | const result = await fn(...argValues); 52 | 53 | messages.push({ 54 | role: "assistant", 55 | content: null, 56 | function_call: { 57 | name: fnName, 58 | arguments: args, 59 | }, 60 | }); 61 | 62 | messages.push({ 63 | role: "function", 64 | name: fnName, 65 | content: JSON.stringify({ result: result }), 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openai-function-calling-nodejs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "openai-function-calling-nodejs", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "openai": "^3.3.0", 13 | "openai-function-calling-tools": "^2.0.1" 14 | } 15 | }, 16 | "node_modules/acorn": { 17 | "version": "8.9.0", 18 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", 19 | "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", 20 | "bin": { 21 | "acorn": "bin/acorn" 22 | }, 23 | "engines": { 24 | "node": ">=0.4.0" 25 | } 26 | }, 27 | "node_modules/acorn-walk": { 28 | "version": "8.2.0", 29 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 30 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 31 | "engines": { 32 | "node": ">=0.4.0" 33 | } 34 | }, 35 | "node_modules/asynckit": { 36 | "version": "0.4.0", 37 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 38 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 39 | }, 40 | "node_modules/axios": { 41 | "version": "0.26.1", 42 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", 43 | "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", 44 | "dependencies": { 45 | "follow-redirects": "^1.14.8" 46 | } 47 | }, 48 | "node_modules/boolbase": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 51 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" 52 | }, 53 | "node_modules/cheerio": { 54 | "version": "1.0.0-rc.12", 55 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", 56 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", 57 | "dependencies": { 58 | "cheerio-select": "^2.1.0", 59 | "dom-serializer": "^2.0.0", 60 | "domhandler": "^5.0.3", 61 | "domutils": "^3.0.1", 62 | "htmlparser2": "^8.0.1", 63 | "parse5": "^7.0.0", 64 | "parse5-htmlparser2-tree-adapter": "^7.0.0" 65 | }, 66 | "engines": { 67 | "node": ">= 6" 68 | }, 69 | "funding": { 70 | "url": "https://github.com/cheeriojs/cheerio?sponsor=1" 71 | } 72 | }, 73 | "node_modules/cheerio-select": { 74 | "version": "2.1.0", 75 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", 76 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", 77 | "dependencies": { 78 | "boolbase": "^1.0.0", 79 | "css-select": "^5.1.0", 80 | "css-what": "^6.1.0", 81 | "domelementtype": "^2.3.0", 82 | "domhandler": "^5.0.3", 83 | "domutils": "^3.0.1" 84 | }, 85 | "funding": { 86 | "url": "https://github.com/sponsors/fb55" 87 | } 88 | }, 89 | "node_modules/combined-stream": { 90 | "version": "1.0.8", 91 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 92 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 93 | "dependencies": { 94 | "delayed-stream": "~1.0.0" 95 | }, 96 | "engines": { 97 | "node": ">= 0.8" 98 | } 99 | }, 100 | "node_modules/css-select": { 101 | "version": "5.1.0", 102 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", 103 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", 104 | "dependencies": { 105 | "boolbase": "^1.0.0", 106 | "css-what": "^6.1.0", 107 | "domhandler": "^5.0.2", 108 | "domutils": "^3.0.1", 109 | "nth-check": "^2.0.1" 110 | }, 111 | "funding": { 112 | "url": "https://github.com/sponsors/fb55" 113 | } 114 | }, 115 | "node_modules/css-what": { 116 | "version": "6.1.0", 117 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 118 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", 119 | "engines": { 120 | "node": ">= 6" 121 | }, 122 | "funding": { 123 | "url": "https://github.com/sponsors/fb55" 124 | } 125 | }, 126 | "node_modules/delayed-stream": { 127 | "version": "1.0.0", 128 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 129 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 130 | "engines": { 131 | "node": ">=0.4.0" 132 | } 133 | }, 134 | "node_modules/dom-serializer": { 135 | "version": "2.0.0", 136 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", 137 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", 138 | "dependencies": { 139 | "domelementtype": "^2.3.0", 140 | "domhandler": "^5.0.2", 141 | "entities": "^4.2.0" 142 | }, 143 | "funding": { 144 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" 145 | } 146 | }, 147 | "node_modules/domelementtype": { 148 | "version": "2.3.0", 149 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 150 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", 151 | "funding": [ 152 | { 153 | "type": "github", 154 | "url": "https://github.com/sponsors/fb55" 155 | } 156 | ] 157 | }, 158 | "node_modules/domhandler": { 159 | "version": "5.0.3", 160 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", 161 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", 162 | "dependencies": { 163 | "domelementtype": "^2.3.0" 164 | }, 165 | "engines": { 166 | "node": ">= 4" 167 | }, 168 | "funding": { 169 | "url": "https://github.com/fb55/domhandler?sponsor=1" 170 | } 171 | }, 172 | "node_modules/domutils": { 173 | "version": "3.1.0", 174 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", 175 | "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", 176 | "dependencies": { 177 | "dom-serializer": "^2.0.0", 178 | "domelementtype": "^2.3.0", 179 | "domhandler": "^5.0.3" 180 | }, 181 | "funding": { 182 | "url": "https://github.com/fb55/domutils?sponsor=1" 183 | } 184 | }, 185 | "node_modules/entities": { 186 | "version": "4.5.0", 187 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", 188 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", 189 | "engines": { 190 | "node": ">=0.12" 191 | }, 192 | "funding": { 193 | "url": "https://github.com/fb55/entities?sponsor=1" 194 | } 195 | }, 196 | "node_modules/esprima": { 197 | "version": "4.0.1", 198 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 199 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 200 | "bin": { 201 | "esparse": "bin/esparse.js", 202 | "esvalidate": "bin/esvalidate.js" 203 | }, 204 | "engines": { 205 | "node": ">=4" 206 | } 207 | }, 208 | "node_modules/expr-eval": { 209 | "version": "2.0.2", 210 | "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", 211 | "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==" 212 | }, 213 | "node_modules/follow-redirects": { 214 | "version": "1.15.2", 215 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 216 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 217 | "funding": [ 218 | { 219 | "type": "individual", 220 | "url": "https://github.com/sponsors/RubenVerborgh" 221 | } 222 | ], 223 | "engines": { 224 | "node": ">=4.0" 225 | }, 226 | "peerDependenciesMeta": { 227 | "debug": { 228 | "optional": true 229 | } 230 | } 231 | }, 232 | "node_modules/form-data": { 233 | "version": "4.0.0", 234 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 235 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 236 | "dependencies": { 237 | "asynckit": "^0.4.0", 238 | "combined-stream": "^1.0.8", 239 | "mime-types": "^2.1.12" 240 | }, 241 | "engines": { 242 | "node": ">= 6" 243 | } 244 | }, 245 | "node_modules/htmlparser2": { 246 | "version": "8.0.2", 247 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", 248 | "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", 249 | "funding": [ 250 | "https://github.com/fb55/htmlparser2?sponsor=1", 251 | { 252 | "type": "github", 253 | "url": "https://github.com/sponsors/fb55" 254 | } 255 | ], 256 | "dependencies": { 257 | "domelementtype": "^2.3.0", 258 | "domhandler": "^5.0.3", 259 | "domutils": "^3.0.1", 260 | "entities": "^4.4.0" 261 | } 262 | }, 263 | "node_modules/mime-db": { 264 | "version": "1.52.0", 265 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 266 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 267 | "engines": { 268 | "node": ">= 0.6" 269 | } 270 | }, 271 | "node_modules/mime-types": { 272 | "version": "2.1.35", 273 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 274 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 275 | "dependencies": { 276 | "mime-db": "1.52.0" 277 | }, 278 | "engines": { 279 | "node": ">= 0.6" 280 | } 281 | }, 282 | "node_modules/moment": { 283 | "version": "2.29.4", 284 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", 285 | "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", 286 | "engines": { 287 | "node": "*" 288 | } 289 | }, 290 | "node_modules/moment-timezone": { 291 | "version": "0.5.43", 292 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", 293 | "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", 294 | "dependencies": { 295 | "moment": "^2.29.4" 296 | }, 297 | "engines": { 298 | "node": "*" 299 | } 300 | }, 301 | "node_modules/nth-check": { 302 | "version": "2.1.1", 303 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", 304 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", 305 | "dependencies": { 306 | "boolbase": "^1.0.0" 307 | }, 308 | "funding": { 309 | "url": "https://github.com/fb55/nth-check?sponsor=1" 310 | } 311 | }, 312 | "node_modules/openai": { 313 | "version": "3.3.0", 314 | "resolved": "https://registry.npmjs.org/openai/-/openai-3.3.0.tgz", 315 | "integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==", 316 | "dependencies": { 317 | "axios": "^0.26.0", 318 | "form-data": "^4.0.0" 319 | } 320 | }, 321 | "node_modules/openai-function-calling-tools": { 322 | "version": "2.0.1", 323 | "resolved": "https://registry.npmjs.org/openai-function-calling-tools/-/openai-function-calling-tools-2.0.1.tgz", 324 | "integrity": "sha512-1wnSzBZG7UP/9hb6164bC/1DlNfWioOUgGbftp2uMylvDQ1QJWrIVlkYmu55Gxmrrf8n/bcf8zKcsM/easn+wA==", 325 | "dependencies": { 326 | "axios": "^1.4.0", 327 | "cheerio": "1.0.0-rc.12", 328 | "esprima": "^4.0.1", 329 | "expr-eval": "^2.0.2", 330 | "moment": "^2.29.4", 331 | "moment-timezone": "^0.5.43", 332 | "openai": "^3.3.0", 333 | "vm2": "^3.9.19", 334 | "zod": "^3.21.4" 335 | } 336 | }, 337 | "node_modules/openai-function-calling-tools/node_modules/axios": { 338 | "version": "1.4.0", 339 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", 340 | "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", 341 | "dependencies": { 342 | "follow-redirects": "^1.15.0", 343 | "form-data": "^4.0.0", 344 | "proxy-from-env": "^1.1.0" 345 | } 346 | }, 347 | "node_modules/parse5": { 348 | "version": "7.1.2", 349 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", 350 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", 351 | "dependencies": { 352 | "entities": "^4.4.0" 353 | }, 354 | "funding": { 355 | "url": "https://github.com/inikulin/parse5?sponsor=1" 356 | } 357 | }, 358 | "node_modules/parse5-htmlparser2-tree-adapter": { 359 | "version": "7.0.0", 360 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", 361 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", 362 | "dependencies": { 363 | "domhandler": "^5.0.2", 364 | "parse5": "^7.0.0" 365 | }, 366 | "funding": { 367 | "url": "https://github.com/inikulin/parse5?sponsor=1" 368 | } 369 | }, 370 | "node_modules/proxy-from-env": { 371 | "version": "1.1.0", 372 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 373 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 374 | }, 375 | "node_modules/vm2": { 376 | "version": "3.9.19", 377 | "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", 378 | "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", 379 | "dependencies": { 380 | "acorn": "^8.7.0", 381 | "acorn-walk": "^8.2.0" 382 | }, 383 | "bin": { 384 | "vm2": "bin/vm2" 385 | }, 386 | "engines": { 387 | "node": ">=6.0" 388 | } 389 | }, 390 | "node_modules/zod": { 391 | "version": "3.21.4", 392 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", 393 | "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", 394 | "funding": { 395 | "url": "https://github.com/sponsors/colinhacks" 396 | } 397 | } 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openai-function-calling-nodejs", 3 | "version": "1.0.0", 4 | "description": "open ai function calling nodejs demo", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "getCurrentTime": "node getCurrentTime.js", 9 | "timeToSport": "node timeToSport.js", 10 | "start": "node index.js", 11 | "test": "node index.js" 12 | }, 13 | "keywords": [ 14 | "openai" 15 | ], 16 | "author": "johannli", 17 | "license": "MIT", 18 | "dependencies": { 19 | "openai": "^3.3.0", 20 | "openai-function-calling-tools": "^2.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /timeToSport.js: -------------------------------------------------------------------------------- 1 | import { Configuration, OpenAIApi } from "openai"; 2 | 3 | const configuration = new Configuration({ 4 | apiKey: process.env.OPENAI_API_KEY, 5 | }); 6 | const openai = new OpenAIApi(configuration); 7 | 8 | const QUESTION = "现在我应该去玩什么运动?"; 9 | 10 | const messages = [ 11 | { 12 | role: "user", 13 | content: QUESTION, 14 | }, 15 | ]; 16 | 17 | // Define the functions that you want to be able to call from the chat 18 | // 定义 chatGPT 可以使用的函数 19 | const functions = { 20 | // 获取当前时间 21 | // get current time 22 | getCurrentTime: function () { 23 | var date = new Date(); 24 | var hours = date.getHours(); 25 | var minutes = date.getMinutes(); 26 | 27 | var time = hours + ":" + minutes; 28 | 29 | return time; 30 | }, 31 | 32 | chooseASportAccordingToTime: function (time) { 33 | // time is like 10:30 34 | const hour = parseInt(time.split(":")[0]); 35 | if (hour >= 6 && hour < 12) { 36 | return "running"; 37 | } 38 | if (hour >= 12 && hour < 18) { 39 | return "swimming"; 40 | } 41 | if (hour >= 18 && hour < 24) { 42 | return "basketball"; 43 | } 44 | }, 45 | }; 46 | 47 | const getCompletion = async (messages) => { 48 | const response = await openai.createChatCompletion({ 49 | model: "gpt-3.5-turbo-0613", 50 | messages, 51 | functions: [ 52 | // tools.SerpAPI.desc 用于获取搜索引擎结果 53 | { 54 | name: "getCurrentTime", 55 | description: "Get the current time", 56 | parameters: { 57 | type: "object", 58 | properties: {}, 59 | }, 60 | }, 61 | { 62 | name: "chooseASportAccordingToTime", 63 | description: "Choose a sport according to the time", 64 | parameters: { 65 | type: "object", 66 | properties: { 67 | time: { 68 | type: "string", 69 | description: "The current time", 70 | }, 71 | }, 72 | }, 73 | }, 74 | ], 75 | temperature: 0, 76 | }); 77 | 78 | return response; 79 | }; 80 | 81 | let response; 82 | 83 | console.log("Question: " + QUESTION); 84 | 85 | while (true) { 86 | response = await getCompletion(messages); 87 | 88 | if (response.data.choices[0].finish_reason === "stop") { 89 | console.log(response.data.choices[0].message.content); 90 | break; 91 | } else if (response.data.choices[0].finish_reason === "function_call") { 92 | const fnName = response.data.choices[0].message.function_call.name; 93 | const args = response.data.choices[0].message.function_call.arguments; 94 | 95 | const fn = functions[fnName]; 96 | const result = fn(...Object.values(JSON.parse(args))); 97 | 98 | console.log(`Calling Function ${fnName} Result: ` + result); 99 | 100 | messages.push({ 101 | role: "assistant", 102 | content: null, 103 | function_call: { 104 | name: fnName, 105 | arguments: args, 106 | }, 107 | }); 108 | 109 | messages.push({ 110 | role: "function", 111 | name: fnName, 112 | content: JSON.stringify({ result: result }), 113 | }); 114 | } 115 | } 116 | --------------------------------------------------------------------------------