Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 400 Can't add messages to thread_X while a run run_X is active and more. #879

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/discord/commands/global/d.ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import {
import { SlashCommand } from '../../@types/commandDef';
import { embedTemplate } from '../../utils/embedTemplate';
import commandContext from '../../utils/context';
import aiChat, { aiModerate } from '../../../global/commands/g.ai';
import { aiModerate, handleAiMessageQueue } from '../../../global/commands/g.ai';

/* TODO
* only direct @ message should trigger a response
Expand Down Expand Up @@ -2175,7 +2175,7 @@ export async function aiMessage(
}, 30000); // Failsafe to stop typing indicator after 30 seconds

try {
const chatResponse = await aiChat(aiPersona, messageList, messageData, attachmentInfo);
const chatResponse = await handleAiMessageQueue(aiPersona, messageList, messageData, attachmentInfo);
response = chatResponse.response;
promptTokens = chatResponse.promptTokens;
completionTokens = chatResponse.completionTokens;
Expand Down
12 changes: 6 additions & 6 deletions src/discord/commands/guild/d.admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@
'Content-Type': 'application/json',
},
}).then(() => {
console.log('Avatar set successfully');
log.info(F, 'Avatar set successfully');
interaction.editReply('Avatar set successfully');
return true;
}).catch((error: Error) => {
console.error(`Error setting avatar: ${error.message}`);
log.error(F, `Error setting avatar: ${error.message}`);
interaction.editReply('Error setting avatar');
return false;
});
Expand All @@ -107,11 +107,11 @@
'Content-Type': 'application/json',
},
}).then(() => {
console.log('Banner set successfully');
log.info(F, 'Banner set successfully');
interaction.editReply('Banner set successfully');
return true;
}).catch((error: Error) => {
console.error(`Error setting banner: ${error.message}`);
log.error(F, `Error setting banner: ${error.message}`);
interaction.editReply('Error setting banner');
return false;
});
Expand Down Expand Up @@ -191,7 +191,7 @@
}

const levelPoints = await findXPfromLevel(level);
log.debug(F, `Overwriting user data for user ${userData.id} in category ${category} type ${type} to level ${level} with ${levelPoints} XP points.`);

Check warning on line 194 in src/discord/commands/guild/d.admin.ts

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 150. Maximum allowed is 120

try {
const result = await db.user_experience.updateMany({
Expand All @@ -206,12 +206,12 @@
total_points: levelPoints,
},
});
console.log(`Update result: ${JSON.stringify(result)}`);
log.info(F, `Update result: ${JSON.stringify(result)}`);
} catch (error) {
console.error(`Error updating database: ${(error as Error).message}`);
log.error(F, `Error updating database: ${(error as Error).message}`);
}

await interaction.editReply(`User level and points updated for category ${category} to level ${level} with ${levelPoints} points.`);

Check warning on line 214 in src/discord/commands/guild/d.admin.ts

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 134. Maximum allowed is 120
}

export const dAdmin: SlashCommand = {
Expand Down Expand Up @@ -287,7 +287,7 @@
if (!interaction.channel) return false;
if (!interaction.guild) return false;
log.info(F, await commandContext(interaction));
const command = interaction.options.getSubcommand() as 'restart' | 'rebuild' | 'deploy' | 'setstatus' | 'setavatar' | 'setbanner' | 'overwriteuserdata';

Check warning on line 290 in src/discord/commands/guild/d.admin.ts

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 156. Maximum allowed is 120
// By default we want to make the reply private
await interaction.deferReply({ ephemeral: true });
// eslint-disable-next-line sonarjs/no-small-switch
Expand All @@ -313,7 +313,7 @@
break;
}
case 'setstatus': {
await setStatus(interaction, interaction.options.getString('prefix') as string, interaction.options.getString('status') as string);

Check warning on line 316 in src/discord/commands/guild/d.admin.ts

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 139. Maximum allowed is 120
break;
}
case 'overwriteuserdata': {
Expand Down
106 changes: 105 additions & 1 deletion src/global/commands/g.ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ const openAi = new OpenAI({

const googleAi = new GoogleGenerativeAI(env.GEMINI_KEY);

type UserQueue = {
queue: {
aiPersona: ai_personas;
messages: { role: 'user'; content: string }[];
messageData: Message<boolean>;
attachmentInfo: {
url: string | null;
mimeType: string | null;
};
resolve: (value: {
response: string;
promptTokens: number;
completionTokens: number;
}) => void;
}[];
isProcessing: boolean;
};

const userQueues = new Map<string, UserQueue>();

type ModerationResult = {
category: string,
value: number,
Expand Down Expand Up @@ -888,6 +908,90 @@ export default async function aiChat(
return openAiConversation(aiPersona, messages, messageData);
}

// Function to process the next message in the user's queue
async function processNextMessage(userId: string) {
const userQueue = userQueues.get(userId);

// If userQueue is null or undefined, exit the function immediately
if (!userQueue) return;

// If the queue is empty, reset isProcessing to false and exit
if (userQueue.queue.length === 0) {
userQueue.isProcessing = false;
return;
}

userQueue.isProcessing = true; // Mark as processing

// Ensure the queue has an item before destructuring
const nextMessage = userQueue.queue.shift();
if (!nextMessage) {
// Handle case where there’s no next message in the queue, if needed
return;
}

const {
aiPersona, messages, messageData, attachmentInfo, resolve,
} = nextMessage;

try {
// Call the aiChat function and destructure the needed response data
const { response, promptTokens, completionTokens } = await aiChat(
aiPersona,
messages,
messageData,
attachmentInfo,
);

resolve({ response, promptTokens, completionTokens });
} catch (error) {
log.error(F, `Error processing message for user: ${userId} - error: ${error}`);
resolve({ response: 'Error', promptTokens: 0, completionTokens: 0 });
} finally {
// Process the next message after this one is done
processNextMessage(userId);
}
}

// Main function for aiChat to handle incoming messages and return a Promise with response data
export function handleAiMessageQueue(
aiPersona: ai_personas,
messages: { role: 'user'; content: string }[],
messageData: Message<boolean>,
attachmentInfo: { url: string | null; mimeType: string | null },
): Promise<{
response: string;
promptTokens: number;
completionTokens: number;
}> {
if (!userQueues.has(messageData.author.id)) {
userQueues.set(messageData.author.id, { queue: [], isProcessing: false });
}

const userQueue = userQueues.get(messageData.author.id);

if (!userQueue) {
// Return a rejected promise if userQueue is undefined
return Promise.reject(new Error(`User queue could not be initialized for user ${messageData.author.id}`));
}

// Push the new message data into the user's queue
return new Promise(resolve => {
userQueue.queue.push({
aiPersona,
messages,
messageData,
attachmentInfo,
resolve,
});

// If the user is not currently processing, start processing
if (!userQueue.isProcessing) {
processNextMessage(messageData.author.id);
}
});
}

export async function aiFlairMod(
aiPersona:ai_personas,
messages: OpenAI.Chat.ChatCompletionMessageParam [],
Expand Down Expand Up @@ -1038,7 +1142,7 @@ export async function aiTranslate(
let completionTokens = 0;
if (!env.OPENAI_API_ORG || !env.OPENAI_API_KEY) return { response, promptTokens, completionTokens };

const model = 'gpt-3.5-turbo-1106';
const model = 'gpt-4o-mini';
const chatCompletionMessages = [{
role: 'system',
content: `You will translate whatever the user sends to their desired language. Their desired language or language code is: ${target_language}.`,
Expand Down
Loading