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;