From 6f290ba758b8a2fd22fbbbe38cc5aa959b04816c Mon Sep 17 00:00:00 2001 From: OptimalBlock489 <89468732+OptimalBlock489@users.noreply.github.com> Date: Tue, 2 May 2023 19:22:51 +0530 Subject: [PATCH] Giving life to Orpheus ! - Added chatbot functionality. (#76) * Initial commit * Create .env * Deleted .env file because openai disables the api key. I had to delete the .env with the API key because OPENAI can detect it. Please put OPENAI_API_KEY as an env variable. * Fixed possible spam issue and enhanced response. * Conditionally gate GPT to only run if key is set --------- Co-authored-by: Max Wofford --- package-lock.json | 60 +++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/index.js | 10 +++++++ src/interactions/gpt.js | 62 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 src/interactions/gpt.js diff --git a/package-lock.json b/package-lock.json index 3079f79..b87fc9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "js-yaml": "^3.13.1", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", + "openai": "^3.2.1", "pluralize": "^8.0.0", "pusher": "^5.1.1-beta" }, @@ -6612,6 +6613,36 @@ "wrappy": "1" } }, + "node_modules/openai": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "dependencies": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, + "node_modules/openai/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/openai/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/openurl": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", @@ -14790,6 +14821,35 @@ "wrappy": "1" } }, + "openai": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "requires": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + }, + "dependencies": { + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "openurl": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", diff --git a/package.json b/package.json index dddd630..71d12e8 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "js-yaml": "^3.13.1", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", + "openai": "^3.2.1", "pluralize": "^8.0.0", "pusher": "^5.1.1-beta" }, diff --git a/src/index.js b/src/index.js index b02f0d8..c8b9561 100644 --- a/src/index.js +++ b/src/index.js @@ -30,6 +30,7 @@ import interactionLeaderInvite from "./interactions/leaderInvite"; import interactionAddress from "./interactions/address"; import interactionClubAddress from "./interactions/clubAddress"; import interactionDM from "./interactions/dm"; +import interactionGPT from "./interactions/gpt"; import interactionStartup from "./interactions/startup"; import interactionWordcloud from "./interactions/wordcloud"; import interactionReport from "./interactions/report"; @@ -93,6 +94,15 @@ controller.hears( interactionBreakoutUpdate ); +if (process.env.OPENAI_API_KEY) { +// temporarily gate this to only run when the key is set +controller.hears( + ["Orpheus", "orpheus", "OrpheusGPT", "orpheusgpt", "Orpheusgpt"], + "direct_message,indirect_mention,direct_mention", + interactionGPT +); +} + controller.hears("info", "direct_message,direct_mention", interactionInfo); controller.hears("hacktoberfest", "ambient", interactionHacktoberfest); diff --git a/src/interactions/gpt.js b/src/interactions/gpt.js new file mode 100644 index 0000000..4ae3527 --- /dev/null +++ b/src/interactions/gpt.js @@ -0,0 +1,62 @@ +import { transcript } from "../utils"; +const { Configuration, OpenAIApi } = require("openai"); + +const configuration = new Configuration({ + apiKey: process.env.OPENAI_API_KEY, +}); +const openai = new OpenAIApi(configuration); + +async function createCompletion(text) { + const response = await openai.createCompletion({ + model: "davinci:ft-hack-club-2023-04-24-16-24-28", + prompt: text, + temperature: 0.2, + max_tokens: 1500, + top_p: 1, + frequency_penalty: 0, + presence_penalty: 0, + }); + + return response.data.choices[0].text; +} + +const interactionGPT = (bot, message) => { + const { channel, text } = message; + text = text.replace("Orpheus", "").trim(); + let prompt = [ + `Create a chatbot conversation between a user and Hack Club's mascot Orpheus. The conversation should feel natural and friendly, with Orpheus showcasing its unique personality and style. The chatbot should be able to respond to a wide range of user messages, both related and unrelated to coding. The chatbot should also be able to recognize when the user needs help with coding and provide relevant assistance. Finally, the chatbot should not reveal itself as an AI model or a chatbot, but rather as Orpheus itself.`, + `Here are some example conversations:`, + `User: Orpheus, let's chat! What have you been up to lately?`, + `Orpheus: Hey there! I've been up to all sorts of things lately. Just the other day, I was learning about new coding languages and trying to figure out how to apply them in my adventures. What about you? Have you been working on any coding projects recently?`, + `User: Actually, I'm a bit stuck on a coding problem. Do you think you could help me out?`, + `Orpheus: Of course! I'm always happy to help out my fellow hackers. What's the problem you're facing? Maybe I can help you figure it out.`, + `User: I'm having trouble with this piece of code. It keeps giving me errors and I can't figure out why.`, + `Orpheus: Hmm, that's definitely a tricky problem. Have you tried checking your syntax and making sure all your variables are properly defined? Sometimes that can cause errors. If that doesn't work, we can dive deeper into the problem and see what's really going on.`, + `User: Thanks, Orpheus! You're the best. Do you have any tips for learning coding faster?`, + `Orpheus: Well, one thing that's helped me is practicing coding challenges and pushing myself to try new things. It can also be really helpful to work with other people and bounce ideas off of each other. And of course, never give up! Coding can be tough sometimes, but it's always worth it in the end."`, + `Now the real user sends a message, with reference to the prompt and your knowledge, respond in the best way possible.\n + User: ${text}\n + Orpheus: `, + ].join("\n"); + let returntext = createCompletion(prompt); + if (channel == "C0266FRGT") { + return; // #announcements + } + returntext.then((result) => { + if (result.includes("Orpheus:")) { + result = result.replace("Orpheus:", "").trim(); + } + if (result.includes("<@")) { + result = + "Response cannot contain a ping, please try to avoid pinging people."; + } + bot.reply(message, transcript(result), (err, src) => { + if (err) { + console.error(err); + return; + } + }); + }); +}; + +export default interactionGPT;