diff --git a/fjerkroa_bot/ai_responder.py b/fjerkroa_bot/ai_responder.py index 23ad1c8..7ddd8b6 100644 --- a/fjerkroa_bot/ai_responder.py +++ b/fjerkroa_bot/ai_responder.py @@ -231,34 +231,58 @@ class AIResponder(object): pickle.dump(self.history, fd) async def send(self, message: AIMessage) -> AIResponse: + # Get the history limit from the configuration limit = self.config["history-limit"] + + # Check if a short path applies, return an empty AIResponse if it does if self.short_path(message, limit): return AIResponse(None, False, None, None, None, False) + + # Number of retries for sending the message retries = 3 + while retries > 0: + # Get the message queue messages = self._message(message, limit) logging.info(f"try to send this messages:\n{pp(messages)}") + + # Attempt to send the message to the AI answer, limit = await self._acreate(messages, limit) + if answer is None: continue + + # Attempt to parse the AI's response try: response = parse_response(answer['content']) except Exception as err: logging.warning(f"failed to parse the answer: {pp(err)}\n{repr(answer['content'])}") answer['content'] = await self.fix(answer['content']) + + # Retry parsing the fixed content try: response = parse_response(answer['content']) except Exception as err: logging.error(f"failed to parse the fixed answer: {pp(err)}\n{repr(answer['content'])}") retries -= 1 continue - if type(response.get('picture')) not in (type(None), str): + + # Check if the response has the correct picture format + if not isinstance(response.get("picture"), (type(None), str)): logging.warning(f"picture key is wrong in response: {pp(response)}") retries -= 1 continue + + # Post-process the message and update the answer's content answer_message = await self.post_process(message, response) answer['content'] = str(answer_message) + + # Update message history self.update_history(messages[-1], answer, limit) logging.info(f"got this answer:\n{str(answer_message)}") + + # Return the updated answer message return answer_message + + # Raise an error if the process failed after all retries raise RuntimeError("Failed to generate answer after multiple retries") diff --git a/fjerkroa_bot/discord_bot.py b/fjerkroa_bot/discord_bot.py index 9d91c82..c55cfc3 100644 --- a/fjerkroa_bot/discord_bot.py +++ b/fjerkroa_bot/discord_bot.py @@ -122,24 +122,44 @@ class FjerkroaBot(commands.Bot): else: await answer_channel.send(response.answer, suppress_embeds=True) - async def respond(self, - message: AIMessage, - channel: Union[TextChannel, DMChannel] - ) -> None: + # This is an asynchronous function to generate AI responses + async def respond( + self, + message: AIMessage, # Incoming message object with user message and metadata + channel: Union[TextChannel, DMChannel] # Channel (Text or Direct Message) the message is coming from + ) -> None: + + # Get the name of the channel where the message was sent channel_name = self.get_channel_name(channel) + + # Check if the message should be ignored, log and return if so if self.ignore_message(channel_name, message): self.log_message_action("ignore", message, channel_name) return + + # In case the message shouldn't be ignored, log the handling action self.log_message_action("handle", message, channel_name) + + # Get the AI responder based on the channel name airesponder = self.get_ai_responder(channel_name) + + # Send the user message to the AI responder, with typing indicators response = await self.send_message_with_typing(airesponder, channel, message) + + # Check if the user tried to hack the system, log if so if response.hack: logging.warning(f"User {message.user} tried to hack the system.") if response.staff is None: response.staff = f"User {message.user} try to hack the AI." + + # Get the answer channel based on the requested response channel answer_channel = self.channel_by_name(response.channel, channel) + + # If there is no need for an answer or the answer channel is not found, return if not (response.answer_needed and answer_channel is not None): return + + # Send the AI's answer to the specified answer channel, with typing indicators await self.send_answer_with_typing(response, answer_channel, airesponder) async def close(self):