diff --git a/src/discordBot/modules/gptHandler.mjs b/src/discordBot/modules/gptHandler.mjs index 6710ea1..9e2f056 100644 --- a/src/discordBot/modules/gptHandler.mjs +++ b/src/discordBot/modules/gptHandler.mjs @@ -1,231 +1,94 @@ import { DebugBuilder } from "../../modules/debugger.mjs"; -import { requestNodeJoinSystem } from "../../modules/socketServerWrappers.mjs"; -import { getSystemByName } from "../../modules/mongo-wrappers/mongoSystemsWrappers.mjs"; -import { getConfig } from "../../modules/mongo-wrappers/mongoConfigWrappers.mjs"; -import { getAvailableTokensInGuild } from "../modules/wrappers.mjs"; +const log = new DebugBuilder("server", "discordBot.modules.gptHandler"); import dotenv from "dotenv"; +dotenv.config(); + import { OpenAI } from "openai"; import { EventEmitter } from "events"; -// Initialize environment variables -dotenv.config(); +const openai = new OpenAI(process.env.OPENAI_API_KEY); -const log = new DebugBuilder("server", "discordBot.modules.gptHandler"); +const assistant = await openai.beta.assistants.create({ + name: "Emmelia", + instructions: process.env.DRB_SERVER_INITIAL_PROMPT, + model: "gpt-4o", +}); -let assistant; -let eventHandler; - -(async () => { - try { - const openai = new OpenAI(process.env.OPENAI_API_KEY); - - assistant = await openai.beta.assistants.create({ - name: "Emmelia", - instructions: (await getConfig("emmeliaInitialPrompt")) || "", - model: "gpt-4o-mini", - tools: [ - { - type: "function", - function: { - name: "checkUserVoiceChannel", - description: "Check if the user is in a voice channel", - parameters: { - type: "object", - properties: { - userId: { - type: "string", - description: "The ID of the user", - }, - guildId: { - type: "string", - description: "The ID of the guild", - }, - }, - required: ["userId", "guildId"], - }, - }, - }, - { - type: "function", - function: { - name: "getSelectedSystem", - description: "Retrieve the selected system details", - parameters: { - type: "object", - properties: { - systemName: { - type: "string", - description: "The name of the system", - }, - }, - required: ["systemName"], - }, - }, - }, - { - type: "function", - function: { - name: "joinSelectedSystem", - description: "Join the selected system in the user's voice channel", - parameters: { - type: "object", - properties: { - userId: { - type: "string", - description: "The ID of the user", - }, - guildId: { - type: "string", - description: "The ID of the guild", - }, - systemName: { - type: "string", - description: "The name of the system", - }, - channelId: { - type: "string", - description: "The ID of the voice channel", - }, - }, - required: ["userId", "guildId", "systemName", "channelId"], - }, - }, - }, - ], - }); - - class EventHandler extends EventEmitter { - constructor(client) { - super(); - this.client = client; - } - - async onEvent(event) { - try { - console.log(event); - if (event.event === "thread.run.requires_action") { - await this.handleRequiresAction( - event.data, - event.data.id, - event.data.thread_id, - ); - } - } catch (error) { - console.error("Error handling event:", error); - } - } - - async handleRequiresAction(data, runId, threadId) { - try { - const toolOutputs = await Promise.all( - data.required_action.submit_tool_outputs.tool_calls.map( - async (toolCall) => { - switch (toolCall.function.name) { - case "checkUserVoiceChannel": - return await this.checkUserVoiceChannel(toolCall); - case "getSelectedSystem": - return await this.getSelectedSystem(toolCall); - case "joinSelectedSystem": - return await this.joinSelectedSystem(toolCall); - default: - throw new Error( - `Unknown function: ${toolCall.function.name}`, - ); - } - }, - ), - ); - await this.submitToolOutputs(toolOutputs, runId, threadId); - } catch (error) { - console.error("Error processing required action:", error); - } - } - - async checkUserVoiceChannel(toolCall) { - const { userId, guildId } = JSON.parse(toolCall.function.arguments); - const guild = await this.client.guilds.get(guildId); - const member = await guild.members.get(userId); - const isInVoiceChannel = !!member.voice.channel; - - return { - tool_call_id: toolCall.id, - output: JSON.stringify({ - isInVoiceChannel, - channelId: member.voice.channelId, - }), - }; - } - - async getSelectedSystem(toolCall) { - const { systemName } = JSON.parse(toolCall.function.arguments); - const system = await getSystemByName(systemName); - - return { - tool_call_id: toolCall.id, - output: JSON.stringify(system), - }; - } - - async joinSelectedSystem(toolCall) { - const { userId, guildId, systemName, channelId } = JSON.parse( - toolCall.function.arguments, - ); - const system = await getSystemByName(systemName); - const guild = await this.client.guilds.fetch(guildId); - const discordToken = await getAvailableTokensInGuild(guildId); - - if (discordToken) { - const result = await requestNodeJoinSystem( - system, - channelId, - discordToken, - ); - return { - tool_call_id: toolCall.id, - output: JSON.stringify({ success: true, result }), - }; - } else { - return { - tool_call_id: toolCall.id, - output: JSON.stringify({ - success: false, - message: "No available bots.", - }), - }; - } - } - - async submitToolOutputs(toolOutputs, runId, threadId) { - try { - const stream = this.client.beta.threads.runs.submitToolOutputsStream( - threadId, - runId, - { tool_outputs: toolOutputs }, - ); - for await (const event of stream) { - this.emit("event", event); - } - } catch (error) { - console.error("Error submitting tool outputs:", error); - } - } - } - - eventHandler = new EventHandler(openai); - eventHandler.on("event", eventHandler.onEvent.bind(eventHandler)); - } catch (error) { - console.error("Initialization error:", error); +class EventHandler extends EventEmitter { + constructor(client) { + super(); + this.client = client; } -})(); + + async onEvent(event) { + try { + console.log(event); + // Retrieve events that are denoted with 'requires_action' + // since these will have our tool_calls + if (event.event === "thread.run.requires_action") { + await this.handleRequiresAction( + event.data, + event.data.id, + event.data.thread_id, + ); + } + } catch (error) { + console.error("Error handling event:", error); + } + } + + async handleRequiresAction(data, runId, threadId) { + try { + const toolOutputs = + data.required_action.submit_tool_outputs.tool_calls.map((toolCall) => { + // Call the function + switch (toolCall.function.name) { + case "getCurrentTemperature": + return { + tool_call_id: toolCall.id, + output: "57", + }; + } + }); + // Submit all the tool outputs at the same time + await this.submitToolOutputs(toolOutputs, runId, threadId); + } catch (error) { + console.error("Error processing required action:", error); + } + } + + async submitToolOutputs(toolOutputs, runId, threadId) { + try { + // Use the submitToolOutputsStream helper + const stream = this.client.beta.threads.runs.submitToolOutputsStream( + threadId, + runId, + { tool_outputs: toolOutputs }, + ); + for await (const event of stream) { + this.emit("event", event); + } + } catch (error) { + console.error("Error submitting tool outputs:", error); + } + } +} + +const eventHandler = new EventHandler(openai); +eventHandler.on("event", eventHandler.onEvent.bind(eventHandler)); export const gptHandler = async (additionalMessages) => { + const thread = await openai.beta.threads.create(); + + // Add the additional messages to the conversation + for (const msgObj of additionalMessages) { + await openai.beta.threads.messages.create(thread.id, msgObj); + } + + log.DEBUG("AI Conversation:", thread); + + // Run the thread to get a response try { - const thread = await openai.beta.threads.create(); - - for (const msgObj of additionalMessages) { - await openai.beta.threads.messages.create(thread.id, msgObj); - } - const stream = await openai.beta.threads.runs.stream( thread.id, { assistant_id: assistant.id }, @@ -236,8 +99,9 @@ export const gptHandler = async (additionalMessages) => { eventHandler.emit("event", event); } + let response; const messages = await openai.beta.threads.messages.list(thread.id); - const response = messages.data[0].content[0].text.value; + response = messages.data[0].content[0].text.value; log.DEBUG("AI Response:", response);