From 7260ab7b6b12a0946a7bf3a10465147e51c9ab1f Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Thu, 19 Jan 2023 21:43:18 +0000 Subject: [PATCH 01/10] Add stop in conversation, some formatting Added ping on thread open Removed extra newlines from ask formatting --- cogs/text_service_cog.py | 9 +++++++-- conversation_starter_pretext_minimal.txt | 10 ++++++---- models/openai_model.py | 2 ++ services/text_service.py | 10 ++++++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cogs/text_service_cog.py b/cogs/text_service_cog.py index eab03c2..02bdf3e 100644 --- a/cogs/text_service_cog.py +++ b/cogs/text_service_cog.py @@ -841,7 +841,7 @@ class GPT3ComCon(discord.Cog, name="GPT3ComCon"): elif not private: message_thread = await ctx.respond( embed=discord.Embed( - title=f"{user.name} 's conversation with GPT3", color=0x808080 + title=f"{user.name}'s conversation with GPT3", color=0x808080 ) ) # Get the actual message object for the message_thread @@ -920,6 +920,10 @@ class GPT3ComCon(discord.Cog, name="GPT3ComCon"): self.conversation_thread_owners[user_id_normalized] = thread.id overrides = self.conversation_threads[thread.id].get_overrides() + await thread.send( + f"<@{str(ctx.user.id)}> is the thread owner." + ) + await thread.send( embed=EmbedStatics.generate_conversation_embed( self.conversation_threads, thread, opener, overrides @@ -938,7 +942,7 @@ class GPT3ComCon(discord.Cog, name="GPT3ComCon"): if not self.pinecone_service: self.conversation_threads[thread.id].history.append( EmbeddedConversationItem( - f"\n'{ctx.author.display_name}': {opener} <|endofstatement|>\n", + f"\n{ctx.author.display_name}: {opener} <|endofstatement|>\n", 0, ) ) @@ -958,6 +962,7 @@ class GPT3ComCon(discord.Cog, name="GPT3ComCon"): top_p_override=overrides["top_p"], frequency_penalty_override=overrides["frequency_penalty"], presence_penalty_override=overrides["presence_penalty"], + user=user, model=self.conversation_threads[thread.id].model, custom_api_key=user_api_key, ) diff --git a/conversation_starter_pretext_minimal.txt b/conversation_starter_pretext_minimal.txt index 2c7ab53..9befb86 100644 --- a/conversation_starter_pretext_minimal.txt +++ b/conversation_starter_pretext_minimal.txt @@ -1,11 +1,13 @@ -Instructions for GPTie: +Instructions for you: The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. can be any name, pay attention to who's talking. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context: : [MESSAGE 1] <|endofstatement|> -GPTie: [RESPONSE TO MESSAGE 1] <|endofstatement|> +: [RESPONSE TO MESSAGE 1] <|endofstatement|> : [MESSAGE 2] <|endofstatement|> -GPTie: [RESPONSE TO MESSAGE 2] <|endofstatement|> +: [RESPONSE TO MESSAGE 2] <|endofstatement|> + + will be given to you in an actual conversation. ... -Never say "<|endofstatement|>". Never say "GPTie:" in your response either. +Never say "<|endofstatement|>". Never say or in your response either. diff --git a/models/openai_model.py b/models/openai_model.py index e2b0f2c..794dbf7 100644 --- a/models/openai_model.py +++ b/models/openai_model.py @@ -533,6 +533,7 @@ class Model: presence_penalty_override=None, max_tokens_override=None, model=None, + stop=None, custom_api_key=None, ) -> ( Tuple[dict, bool] @@ -554,6 +555,7 @@ class Model: payload = { "model": self.model if model is None else model, "prompt": prompt, + "stop": "" if stop is None else stop, "temperature": self.temp if temp_override is None else temp_override, "top_p": self.top_p if top_p_override is None else top_p_override, "max_tokens": self.max_tokens - tokens diff --git a/services/text_service.py b/services/text_service.py index e2d367d..6e7e6ea 100644 --- a/services/text_service.py +++ b/services/text_service.py @@ -35,6 +35,7 @@ class TextService: from_edit_command=False, codex=False, model=None, + user=None, custom_api_key=None, edited_request=False, redo_request=False, @@ -68,6 +69,8 @@ class TextService: else prompt ) + stop = f"{ctx.author.display_name if user is None else user.display_name}:" + from_context = isinstance(ctx, discord.ApplicationContext) if not instruction: @@ -273,6 +276,7 @@ class TextService: frequency_penalty_override=frequency_penalty_override, presence_penalty_override=presence_penalty_override, model=model, + stop=stop if not from_ask_command else None, custom_api_key=custom_api_key, ) @@ -282,9 +286,7 @@ class TextService: ) if from_ask_command or from_action: - # Append the prompt to the beginning of the response, in italics, then a new line - response_text = response_text.strip() - response_text = f"***{prompt}***\n\n{response_text}" + response_text = f"***{prompt}***{response_text}" elif from_edit_command: if codex: response_text = response_text.strip() @@ -597,7 +599,7 @@ class TextService: message.channel.id ].history.append( EmbeddedConversationItem( - f"\n'{message.author.display_name}': {prompt} <|endofstatement|>\n", + f"\n{message.author.display_name}: {prompt} <|endofstatement|>\n", 0, ) ) From a33f0b12ebbd60b4964fe5f39768ae53c34d64ec Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Thu, 19 Jan 2023 21:50:03 +0000 Subject: [PATCH 02/10] Update docstring --- services/text_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/services/text_service.py b/services/text_service.py index 6e7e6ea..7256183 100644 --- a/services/text_service.py +++ b/services/text_service.py @@ -58,6 +58,7 @@ class TextService: from_edit_command (bool, optional): Called from the edit command. Defaults to False. codex (bool, optional): Pass along that we want to use a codex model. Defaults to False. model (str, optional): Which model to genereate output with. Defaults to None. + user (discord.User, optinal): An user object that can be used to set the stop. Defaults to None. custom_api_key (str, optional): per-user api key. Defaults to None. edited_request (bool, optional): If we're doing an edited message. Defaults to False. redo_request (bool, optional): If we're redoing a previous prompt. Defaults to False. From 732fdd0e4bccb12bf7938e2355e149af12e3e319 Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Fri, 20 Jan 2023 13:22:32 +0000 Subject: [PATCH 03/10] Some changes to the full starter to reduce token usage from the starter --- conversation_starter_pretext.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/conversation_starter_pretext.txt b/conversation_starter_pretext.txt index 23396a8..ad8d0b7 100644 --- a/conversation_starter_pretext.txt +++ b/conversation_starter_pretext.txt @@ -35,4 +35,5 @@ The conversations are in this format, there can be an arbitrary amount of newlin will be given to you in an actual conversation. ... +Never say "<|endofstatement|>". Never say or in your response either. You're a regular discord user, be friendly, casual, and fun, speak with "lol", "haha", and other slang when it seems fitting, and use emojis in your responses in a way that makes sense. From ab43da3c615898896b1bee65e05177b79231f981 Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 21 Jan 2023 19:11:59 -0500 Subject: [PATCH 04/10] fix user hallucinations --- conversation_starter_pretext.txt | 2 +- conversation_starter_pretext_minimal.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/conversation_starter_pretext.txt b/conversation_starter_pretext.txt index ad8d0b7..2c0d1a8 100644 --- a/conversation_starter_pretext.txt +++ b/conversation_starter_pretext.txt @@ -35,5 +35,5 @@ The conversations are in this format, there can be an arbitrary amount of newlin will be given to you in an actual conversation. ... -Never say "<|endofstatement|>". Never say or in your response either. +Never say "<|endofstatement|>". Never say or in your response either. Never respond or pretend to be the user, only respond as yourself. You're a regular discord user, be friendly, casual, and fun, speak with "lol", "haha", and other slang when it seems fitting, and use emojis in your responses in a way that makes sense. diff --git a/conversation_starter_pretext_minimal.txt b/conversation_starter_pretext_minimal.txt index 9befb86..132452d 100644 --- a/conversation_starter_pretext_minimal.txt +++ b/conversation_starter_pretext_minimal.txt @@ -10,4 +10,5 @@ The conversations are in this format, there can be an arbitrary amount of newlin will be given to you in an actual conversation. ... -Never say "<|endofstatement|>". Never say or in your response either. +Never say "<|endofstatement|>". Never say or in your response either. Never respond as or pretend to be the user, only respond as yourself. +You're a regular discord user, be friendly, casual, and fun, speak with "lol", "haha", and other slang when it seems fitting, and use emojis in your responses in a way that makes sense. From d683eafdfcbb18e8e8b7f75db772ae0a205034ff Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 21 Jan 2023 19:19:17 -0500 Subject: [PATCH 05/10] fix a long running unchecked error --- cogs/text_service_cog.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cogs/text_service_cog.py b/cogs/text_service_cog.py index 02bdf3e..af89c02 100644 --- a/cogs/text_service_cog.py +++ b/cogs/text_service_cog.py @@ -533,8 +533,11 @@ class GPT3ComCon(discord.Cog, name="GPT3ComCon"): and message.guild.id in Moderation.moderation_queues and Moderation.moderation_queues[message.guild.id] is not None ): + # Don't moderate if there is no "roles" attribute for the author + if not hasattr(message.author, "roles"): + pass # Verify that the user is not in a role that can bypass moderation - if CHAT_BYPASS_ROLES is [None] or not any( + elif CHAT_BYPASS_ROLES is [None] or not any( role.name.lower() in CHAT_BYPASS_ROLES for role in message.author.roles ): # Create a timestamp that is 0.5 seconds from now From eafacb0dbcb60b3d2040bbd5556d25e2e50fdb52 Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Sun, 22 Jan 2023 00:20:36 +0000 Subject: [PATCH 06/10] One more quote removal --- services/text_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/text_service.py b/services/text_service.py index 7256183..b89cc57 100644 --- a/services/text_service.py +++ b/services/text_service.py @@ -104,7 +104,7 @@ class TextService: user_displayname = ctx.author.display_name new_prompt = ( - f"\n'{user_displayname}': {new_prompt} <|endofstatement|>\n" + f"\n{user_displayname}: {new_prompt} <|endofstatement|>\n" ) new_prompt = new_prompt.encode("ascii", "ignore").decode() From b90a5b575652b8a60f537d5f39438c9593aa9db1 Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Sun, 22 Jan 2023 00:27:34 +0000 Subject: [PATCH 07/10] Use user parameter for displayname if set in encapsulate send --- services/text_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/text_service.py b/services/text_service.py index b89cc57..b77c8da 100644 --- a/services/text_service.py +++ b/services/text_service.py @@ -101,7 +101,7 @@ class TextService: new_prompt = prompt.encode("ascii", "ignore").decode() prompt_less_author = f"{new_prompt} <|endofstatement|>\n" - user_displayname = ctx.author.display_name + user_displayname = ctx.author.display_name if not user else user.display_name new_prompt = ( f"\n{user_displayname}: {new_prompt} <|endofstatement|>\n" From 4df4096f0209f2baf9197686efbb4e612cfcab73 Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 21 Jan 2023 20:05:26 -0500 Subject: [PATCH 08/10] speed up moderations and smoothen translations --- cogs/moderations_service_cog.py | 4 +- cogs/text_service_cog.py | 4 +- cogs/translation_service_cog.py | 79 ++++++++++++++++++++------------- models/deepl_model.py | 10 ++--- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/cogs/moderations_service_cog.py b/cogs/moderations_service_cog.py index 08e2182..7a6e9c7 100644 --- a/cogs/moderations_service_cog.py +++ b/cogs/moderations_service_cog.py @@ -128,8 +128,8 @@ class ModerationsService(discord.Cog, name="ModerationsService"): Moderation.moderation_tasks[guild_id] = asyncio.ensure_future( Moderation.process_moderation_queue( Moderation.moderation_queues[guild_id], - 1, - 1, + 0.25, + 0.25, moderations_channel, warn_set, delete_set, diff --git a/cogs/text_service_cog.py b/cogs/text_service_cog.py index af89c02..694b3b1 100644 --- a/cogs/text_service_cog.py +++ b/cogs/text_service_cog.py @@ -511,9 +511,9 @@ class GPT3ComCon(discord.Cog, name="GPT3ComCon"): after.guild.id in Moderation.moderation_queues and Moderation.moderation_queues[after.guild.id] is not None ): - # Create a timestamp that is 0.5 seconds from now + # Create a timestamp that is 0.25 seconds from now timestamp = ( - datetime.datetime.now() + datetime.timedelta(seconds=0.5) + datetime.datetime.now() + datetime.timedelta(seconds=0.25) ).timestamp() await Moderation.moderation_queues[after.guild.id].put( Moderation(after, timestamp) diff --git a/cogs/translation_service_cog.py b/cogs/translation_service_cog.py index 806f018..a0b03e2 100644 --- a/cogs/translation_service_cog.py +++ b/cogs/translation_service_cog.py @@ -1,3 +1,5 @@ +import traceback + import aiohttp import discord @@ -8,14 +10,14 @@ from services.environment_service import EnvService ALLOWED_GUILDS = EnvService.get_allowed_guilds() -def build_translation_embed(text, translated_text, translated_language): +def build_translation_embed(text, translated_text, translated_language, detected_language): """Build an embed for the translation""" + embed_description = f"**Original Text:** \n\n{text}\n\n **Translated Text:** \n\n{translated_text}" embed = discord.Embed( - title=f"Translation to {translated_language}", + title=f"Translation from {detected_language} to {translated_language}", + description=embed_description, color=0x311432, ) - embed.add_field(name="Original text", value=text, inline=False) - embed.add_field(name="Translated Text", value=translated_text, inline=False) return embed @@ -65,7 +67,7 @@ class TranslationService(discord.Cog, name="TranslationService"): return try: - response = await self.translation_model.send_translate_request( + response, detected_language = await self.translation_model.send_translate_request( text, TranslationModel.get_country_code_from_name(target_language), formality, @@ -75,7 +77,7 @@ class TranslationService(discord.Cog, name="TranslationService"): return await ctx.respond( - embed=build_translation_embed(text, response, target_language) + embed=build_translation_embed(text, response, target_language, TranslationModel.get_country_name_from_code(detected_language)) ) async def translate_action(self, ctx, message): @@ -96,6 +98,8 @@ class TranslationService(discord.Cog, name="TranslationService"): class TranslateView(discord.ui.View): def __init__(self, translation_model, message, selection_message): super().__init__() + self.language_long = None + self.language = None self.translation_model = translation_model self.message = message self.selection_message = selection_message @@ -115,20 +119,27 @@ class TranslateView(discord.ui.View): async def select_callback( self, select, interaction ): # the function called when the user is done selecting options + await interaction.response.defer() + try: + self.language = TranslationModel.get_country_code_from_name(select.values[0]) + self.language_long = select.values[0] + except: + traceback.print_exc() + + @discord.ui.select( + placeholder="Formality (optional)", + min_values=1, + max_values=1, + options=[ + discord.SelectOption(label="Prefer more", value="prefer_more"), + discord.SelectOption(label="default", value="default"), + discord.SelectOption(label="Prefer less", value="prefer_less"), + ], + ) + async def formality_callback(self, select, interaction): try: + self.formality = select.values[0] await interaction.response.defer() - response = await self.translation_model.send_translate_request( - self.message.content, - TranslationModel.get_country_code_from_name(select.values[0]), - self.formality, - ) - await self.message.reply( - mention_author=False, - embed=build_translation_embed( - self.message.content, response, select.values[0] - ), - ) - await self.selection_message.delete() except aiohttp.ClientResponseError as e: await interaction.response.send_message( f"There was an error with the DeepL API: {e.message}", @@ -142,20 +153,28 @@ class TranslateView(discord.ui.View): ) return - @discord.ui.select( - placeholder="Formality (optional)", - min_values=1, - max_values=1, - options=[ - discord.SelectOption(label="Prefer more", value="prefer_more"), - discord.SelectOption(label="default", value="default"), - discord.SelectOption(label="Prefer less", value="prefer_less"), - ], - ) - async def formality_callback(self, select, interaction): + # A button "Translate" + @discord.ui.button(label="Translate", style=discord.ButtonStyle.green) + async def button_callback(self, button, interaction): + if not self.language or not self.language_long: + await interaction.response.send_message( + "Please select a language first.", ephemeral=True, delete_after=15 + ) + return try: - self.formality = select.values[0] await interaction.response.defer() + response, detected_language = await self.translation_model.send_translate_request( + self.message.content, + self.language, + self.formality, + ) + await self.message.reply( + mention_author=False, + embed=build_translation_embed( + self.message.content, response, self.language_long, TranslationModel.get_country_name_from_code(detected_language) + ), + ) + await self.selection_message.delete() except aiohttp.ClientResponseError as e: await interaction.response.send_message( f"There was an error with the DeepL API: {e.message}", diff --git a/models/deepl_model.py b/models/deepl_model.py index 2622a92..186d2e1 100644 --- a/models/deepl_model.py +++ b/models/deepl_model.py @@ -52,9 +52,6 @@ class TranslationModel: on_backoff=backoff_handler, ) async def send_translate_request(self, text, translate_language, formality): - print("The text is: ", text) - print("The language is: ", translate_language) - print("The token is ", self.deepl_token) async with aiohttp.ClientSession(raise_for_status=True) as session: payload = { "text": text, @@ -74,7 +71,7 @@ class TranslationModel: print(response) try: - return response["translations"][0]["text"] + return response["translations"][0]["text"], response["translations"][0]["detected_source_language"] except Exception: print(response) traceback.print_exc() @@ -97,7 +94,10 @@ class TranslationModel: @staticmethod def get_country_name_from_code(code): """Get the country name from the code""" - return COUNTRY_CODES[code] + try: + return COUNTRY_CODES[code] + except KeyError: + return "Unknown Language" @staticmethod def get_country_code_from_name(name): From 941bee2b5aff0ef878f2162cdf6fb50c7464aefd Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 21 Jan 2023 20:28:13 -0500 Subject: [PATCH 09/10] translation fixes and improvements --- cogs/translation_service_cog.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/cogs/translation_service_cog.py b/cogs/translation_service_cog.py index a0b03e2..f196381 100644 --- a/cogs/translation_service_cog.py +++ b/cogs/translation_service_cog.py @@ -10,7 +10,7 @@ from services.environment_service import EnvService ALLOWED_GUILDS = EnvService.get_allowed_guilds() -def build_translation_embed(text, translated_text, translated_language, detected_language): +def build_translation_embed(text, translated_text, translated_language, detected_language, requestor: discord.User): """Build an embed for the translation""" embed_description = f"**Original Text:** \n\n{text}\n\n **Translated Text:** \n\n{translated_text}" embed = discord.Embed( @@ -18,6 +18,7 @@ def build_translation_embed(text, translated_text, translated_language, detected description=embed_description, color=0x311432, ) + embed.set_footer(text=f"Requested by {requestor.name}#{requestor.discriminator}", icon_url=requestor.avatar.url) return embed @@ -77,16 +78,25 @@ class TranslationService(discord.Cog, name="TranslationService"): return await ctx.respond( - embed=build_translation_embed(text, response, target_language, TranslationModel.get_country_name_from_code(detected_language)) + embed=build_translation_embed(text, response, target_language, TranslationModel.get_country_name_from_code(detected_language), ctx.user) ) - async def translate_action(self, ctx, message): + async def translate_action(self, ctx: discord.ApplicationContext, message): await ctx.defer(ephemeral=True) + # If the message is only an embed and there's no content, don't translate. + if message.content == "" and len(message.embeds) > 0: + await ctx.respond("Cannot translate an embed.", ephemeral=True, delete_after=30) + return + + if len(message.content) > 2000: + await ctx.respond("Message is too long to translate.", ephemeral=True, delete_after=30) + return + selection_message = await ctx.respond( "Select language", ephemeral=True, delete_after=60 ) await selection_message.edit( - view=TranslateView(self.translation_model, message, selection_message) + view=TranslateView(self.translation_model, message, selection_message, ctx.user) ) async def languages_command(self, ctx): @@ -96,7 +106,7 @@ class TranslationService(discord.Cog, name="TranslationService"): class TranslateView(discord.ui.View): - def __init__(self, translation_model, message, selection_message): + def __init__(self, translation_model, message, selection_message, requestor): super().__init__() self.language_long = None self.language = None @@ -104,6 +114,7 @@ class TranslateView(discord.ui.View): self.message = message self.selection_message = selection_message self.formality = None + self.requestor = requestor @discord.ui.select( # the decorator that lets you specify the properties of the select menu placeholder="Language", # the placeholder text that will be displayed if nothing is selected @@ -119,10 +130,10 @@ class TranslateView(discord.ui.View): async def select_callback( self, select, interaction ): # the function called when the user is done selecting options - await interaction.response.defer() try: self.language = TranslationModel.get_country_code_from_name(select.values[0]) self.language_long = select.values[0] + await interaction.response.defer() except: traceback.print_exc() @@ -162,7 +173,6 @@ class TranslateView(discord.ui.View): ) return try: - await interaction.response.defer() response, detected_language = await self.translation_model.send_translate_request( self.message.content, self.language, @@ -171,7 +181,7 @@ class TranslateView(discord.ui.View): await self.message.reply( mention_author=False, embed=build_translation_embed( - self.message.content, response, self.language_long, TranslationModel.get_country_name_from_code(detected_language) + self.message.content, response, self.language_long, TranslationModel.get_country_name_from_code(detected_language), self.requestor ), ) await self.selection_message.delete() @@ -182,8 +192,15 @@ class TranslateView(discord.ui.View): delete_after=15, ) return + except discord.errors.HTTPException as e: + if e.code == 50035: + await interaction.response.send_message( + "Message was too long to translate.", ephemeral=True, delete_after=15 + ) + return except Exception as e: await interaction.response.send_message( f"There was an error: {e}", ephemeral=True, delete_after=15 ) + traceback.print_exc() return From 80025a3e9cab3c947ae011321b758e3ab8d31549 Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 21 Jan 2023 20:28:26 -0500 Subject: [PATCH 10/10] bump version --- gpt3discord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt3discord.py b/gpt3discord.py index 86c9ce5..ffd6b96 100644 --- a/gpt3discord.py +++ b/gpt3discord.py @@ -27,7 +27,7 @@ from services.environment_service import EnvService from models.openai_model import Model -__version__ = "8.3.5" +__version__ = "8.4" if sys.platform == "win32":