From ac11d9f2a44e654c2e09ed1bd361adcfa7670c59 Mon Sep 17 00:00:00 2001 From: duckdoom4 <60387522+duckdoom4@users.noreply.github.com> Date: Fri, 27 Jan 2023 13:51:51 +0100 Subject: [PATCH] Remove hardcoded 'magic values' --- models/autocomplete_model.py | 36 ++++---- models/openai_model.py | 158 ++++++++++++++++++++------------ services/moderations_service.py | 7 ++ 3 files changed, 127 insertions(+), 74 deletions(-) diff --git a/models/autocomplete_model.py b/models/autocomplete_model.py index 584a752..5cbde5c 100644 --- a/models/autocomplete_model.py +++ b/models/autocomplete_model.py @@ -5,8 +5,9 @@ import re import discord from models.deepl_model import TranslationModel +from services.moderations_service import ModerationOptions from services.usage_service import UsageService -from models.openai_model import Model +from models.openai_model import ImageSize, Model, ModelLimits, Models, Mode from services.environment_service import EnvService usage_service = UsageService(Path(os.environ.get("DATA_DIR", os.getcwd()))) @@ -34,26 +35,27 @@ class Settings_autocompleter: ): # Behaves a bit weird if you go back and edit the parameter without typing in a new command """gets valid values for the value option""" values = { - "max_conversation_length": [str(num) for num in range(1, 500, 2)], - "num_images": [str(num) for num in range(1, 4 + 1)], - "mode": ["temperature", "top_p"], - "model": ["text-davinci-003", "text-curie-001"], + "max_conversation_length": [str(num) for num in range(ModelLimits.MIN_CONVERSATION_LENGTH, ModelLimits.MAX_CONVERSATION_LENGTH + 1, 2)], + "num_images": [str(num) for num in range(ModelLimits.MIN_NUM_IMAGES, ModelLimits.MAX_NUM_IMAGES + 1)], + "mode": Mode.ALL_MODES, + "model": Models.TEXT_MODELS, "low_usage_mode": ["True", "False"], - "image_size": ["256x256", "512x512", "1024x1024"], + "image_size": ImageSize.ALL_SIZES, "summarize_conversation": ["True", "False"], "welcome_message_enabled": ["True", "False"], - "num_static_conversation_items": [str(num) for num in range(5, 20 + 1)], - "num_conversation_lookback": [str(num) for num in range(5, 15 + 1)], - "summarize_threshold": [str(num) for num in range(800, 3500, 50)], + "num_static_conversation_items": [str(num) for num in range(ModelLimits.MIN_NUM_STATIC_CONVERSATION_ITEMS, ModelLimits.MAX_NUM_STATIC_CONVERSATION_ITEMS + 1)], + "num_conversation_lookback": [str(num) for num in range(ModelLimits.MIN_NUM_CONVERSATION_LOOKBACK, ModelLimits.MAX_NUM_CONVERSATION_LOOKBACK + 1)], + "summarize_threshold": [str(num) for num in range(ModelLimits.MIN_SUMMARIZE_THRESHOLD, ModelLimits.MAX_SUMMARIZE_THRESHOLD + 1, 50)], "type": ["warn", "delete"], } - for parameter in values: - if parameter == ctx.options["parameter"]: - return [ - value - for value in values[ctx.options["parameter"]] - if value.startswith(ctx.value.lower()) - ] + options = values.get(ctx.options["parameter"], []) + if options: + return [ + value + for value in options + if value.startswith(ctx.value.lower()) + ] + await ctx.interaction.response.defer() # defer so the autocomplete in int values doesn't error but rather just says not found return [] @@ -64,7 +66,7 @@ class Settings_autocompleter: print(f"The value is {ctx.value}") return [ value - for value in ["warn", "delete", "reset"] + for value in ModerationOptions.OPTIONS if value.startswith(ctx.value.lower()) ] diff --git a/models/openai_model.py b/models/openai_model.py index aa5a9af..f8b1688 100644 --- a/models/openai_model.py +++ b/models/openai_model.py @@ -18,8 +18,10 @@ from discord import File class Mode: - TOP_P = "top_p" TEMPERATURE = "temperature" + TOP_P = "top_p" + + ALL_MODES = [TEMPERATURE, TOP_P] class Models: @@ -40,11 +42,57 @@ class Models: EDIT = "text-davinci-edit-001" CODE_EDIT = "code-davinci-edit-001" + # Model collections + TEXT_MODELS = [DAVINCI, CURIE, BABBAGE, ADA, CODE_DAVINCI, CODE_CUSHMAN] + EDIT_MODELS = [EDIT, CODE_EDIT] + + DEFAULT = DAVINCI + LOW_USAGE_MODEL = CURIE + class ImageSize: - LARGE = "1024x1024" - MEDIUM = "512x512" SMALL = "256x256" + MEDIUM = "512x512" + LARGE = "1024x1024" + + ALL_SIZES = [SMALL, MEDIUM, LARGE] + +class ModelLimits: + MIN_TOKENS = 15 + MAX_TOKENS = 4096 + + MIN_CONVERSATION_LENGTH = 1 + MAX_CONVERSATION_LENGTH = 500 + + MIN_SUMMARIZE_THRESHOLD = 800 + MAX_SUMMARIZE_THRESHOLD = 3500 + + MIN_NUM_IMAGES = 1 + MAX_NUM_IMAGES = 4 + + MIN_NUM_STATIC_CONVERSATION_ITEMS = 5 + MAX_NUM_STATIC_CONVERSATION_ITEMS = 20 + + MIN_NUM_CONVERSATION_LOOKBACK = 5 + MAX_NUM_CONVERSATION_LOOKBACK = 15 + + MIN_TEMPERATURE = 0.0 + MAX_TEMPERATURE = 2.0 + + MIN_TOP_P = 0.0 + MAX_TOP_P = 1.0 + + MIN_PRESENCE_PENALTY = -2.0 + MAX_PRESENCE_PENALTY = 2.0 + + MIN_FREQUENCY_PENALTY = -2.0 + MAX_FREQUENCY_PENALTY = 2.0 + + MIN_BEST_OF = 1 + MAX_BEST_OF = 3 + + MIN_PROMPT_MIN_LENGTH = 10 + MAX_PROMPT_MIN_LENGTH = 4096 class Model: @@ -61,7 +109,7 @@ class Model: self._best_of = 1 # Number of responses to compare the loglikelihoods of self._prompt_min_length = 8 self._max_conversation_length = 100 - self._model = Models.DAVINCI + self._model = Models.DEFAULT self._low_usage_mode = False self.usage_service = usage_service self.DAVINCI_ROLES = ["admin", "Admin", "GPT", "gpt"] @@ -105,11 +153,11 @@ class Model: @num_static_conversation_items.setter def num_static_conversation_items(self, value): value = int(value) - if value < 3: - raise ValueError("num_static_conversation_items must be >= 3") - if value > 20: + if value < ModelLimits.MIN_NUM_STATIC_CONVERSATION_ITEMS: + raise ValueError(f"Number of static conversation items must be >= {ModelLimits.MIN_NUM_STATIC_CONVERSATION_ITEMS}") + if value > ModelLimits.MAX_NUM_STATIC_CONVERSATION_ITEMS: raise ValueError( - "num_static_conversation_items must be <= 20, this is to ensure reliability and reduce token wastage!" + f"Number of static conversation items must be <= {ModelLimits.MAX_NUM_STATIC_CONVERSATION_ITEMS}, this is to ensure reliability and reduce token wastage!" ) self._num_static_conversation_items = value @@ -120,11 +168,11 @@ class Model: @num_conversation_lookback.setter def num_conversation_lookback(self, value): value = int(value) - if value < 3: - raise ValueError("num_conversation_lookback must be >= 3") - if value > 15: + if value < ModelLimits.MIN_NUM_CONVERSATION_LOOKBACK: + raise ValueError(f"Number of conversations to look back on must be >= {ModelLimits.MIN_NUM_CONVERSATION_LOOKBACK}") + if value > ModelLimits.MAX_NUM_CONVERSATION_LOOKBACK: raise ValueError( - "num_conversation_lookback must be <= 15, this is to ensure reliability and reduce token wastage!" + f"Number of conversations to look back on must be <= {ModelLimits.MIN_NUM_CONVERSATION_LOOKBACK}, this is to ensure reliability and reduce token wastage!" ) self._num_conversation_lookback = value @@ -139,7 +187,7 @@ class Model: elif value.lower() == "false": self._welcome_message_enabled = False else: - raise ValueError("Value must be either true or false!") + raise ValueError("Value must be either `true` or `false`!") @property def summarize_threshold(self): @@ -148,9 +196,9 @@ class Model: @summarize_threshold.setter def summarize_threshold(self, value): value = int(value) - if value < 800 or value > 4000: + if value < ModelLimits.MIN_SUMMARIZE_THRESHOLD or value > ModelLimits.MAX_SUMMARIZE_THRESHOLD: raise ValueError( - "Summarize threshold cannot be greater than 4000 or less than 800!" + f"Summarize threshold should be a number between {ModelLimits.MIN_SUMMARIZE_THRESHOLD} and {ModelLimits.MAX_SUMMARIZE_THRESHOLD}!" ) self._summarize_threshold = value @@ -166,7 +214,7 @@ class Model: elif value.lower() == "false": value = False else: - raise ValueError("Value must be either true or false!") + raise ValueError("Value must be either `true` or `false`!") self._summarize_conversations = value @property @@ -175,11 +223,11 @@ class Model: @image_size.setter def image_size(self, value): - if value in ImageSize.__dict__.values(): + if value in ImageSize.ALL_SIZES: self._image_size = value else: raise ValueError( - "Image size must be one of the following: SMALL(256x256), MEDIUM(512x512), LARGE(1024x1024)" + f"Image size must be one of the following: {ImageSize.ALL_SIZES}" ) @property @@ -189,8 +237,8 @@ class Model: @num_images.setter def num_images(self, value): value = int(value) - if value > 4 or value <= 0: - raise ValueError("num_images must be less than 4 and at least 1.") + if value < ModelLimits.MIN_NUM_IMAGES or value > ModelLimits.MAX_NUM_IMAGES: + raise ValueError(f"Number of images to generate should be a number between {ModelLimits.MIN_NUM_IMAGES} and {ModelLimits.MAX_NUM_IMAGES}!") self._num_images = value @property @@ -205,14 +253,14 @@ class Model: elif value.lower() == "false": value = False else: - raise ValueError("Value must be either true or false!") + raise ValueError("Value must be either `true` or `false`!") if value: - self._model = Models.CURIE + self._model = Models.LOW_USAGE_MODEL self.max_tokens = 1900 self.model_max_tokens = 1000 else: - self._model = Models.DAVINCI + self._model = Models.DEFAULT self.max_tokens = 4000 self.model_max_tokens = 4024 @@ -222,10 +270,8 @@ class Model: @model.setter def model(self, model): - if model not in [Models.DAVINCI, Models.CURIE]: - raise ValueError( - "Invalid model, must be text-davinci-003 or text-curie-001" - ) + if model not in Models.TEXT_MODELS: + raise ValueError(f"Invalid model, must be one of: {Models.TEXT_MODELS}") self._model = model @property @@ -235,11 +281,11 @@ class Model: @max_conversation_length.setter def max_conversation_length(self, value): value = int(value) - if value < 1: - raise ValueError("Max conversation length must be greater than 1") - if value > 500: + if value < ModelLimits.MIN_CONVERSATION_LENGTH: + raise ValueError(f"Max conversation length must be greater than {ModelLimits.MIN_CONVERSATION_LENGTH}") + if value > ModelLimits.MAX_CONVERSATION_LENGTH: raise ValueError( - "Max conversation length must be less than 500, this will start using credits quick." + f"Max conversation length must be less than {ModelLimits.MIN_CONVERSATION_LENGTH}, this will start using credits quick." ) self._max_conversation_length = value @@ -249,14 +295,17 @@ class Model: @mode.setter def mode(self, value): - if value not in [Mode.TOP_P, Mode.TEMPERATURE]: - raise ValueError("mode must be either 'top_p' or 'temperature'") + if value not in Mode.ALL_MODES: + raise ValueError(f"Mode must be one of: {Mode.ALL_MODES}") + if value == Mode.TOP_P: self._top_p = 0.1 self._temp = 0.7 elif value == Mode.TEMPERATURE: self._top_p = 0.9 self._temp = 0.6 + else: + raise ValueError(f"Unknown mode: {value}") self._mode = value @@ -267,10 +316,9 @@ class Model: @temp.setter def temp(self, value): value = float(value) - if value < 0 or value > 2: + if value < ModelLimits.MIN_TEMPERATURE or value > ModelLimits.MAX_TEMPERATURE: raise ValueError( - "temperature must be greater than 0 and less than 2, it is currently " - + str(value) + f"Temperature must be between {ModelLimits.MIN_TEMPERATURE} and {ModelLimits.MAX_TEMPERATURE}, it is currently: {value}" ) self._temp = value @@ -282,10 +330,9 @@ class Model: @top_p.setter def top_p(self, value): value = float(value) - if value < 0 or value > 1: + if value < ModelLimits.MIN_TOP_P or value > ModelLimits.MAX_TOP_P: raise ValueError( - "top_p must be greater than 0 and less than 1, it is currently " - + str(value) + f"Top P must be between {ModelLimits.MIN_TOP_P} and {ModelLimits.MAX_TOP_P}, it is currently: {value}" ) self._top_p = value @@ -296,10 +343,9 @@ class Model: @max_tokens.setter def max_tokens(self, value): value = int(value) - if value < 15 or value > 4096: + if value < ModelLimits.MIN_TOKENS or value > ModelLimits.MAX_TOKENS: raise ValueError( - "max_tokens must be greater than 15 and less than 4096, it is currently " - + str(value) + f"Max tokens must be between {ModelLimits.MIN_TOKENS} and {ModelLimits.MAX_TOKENS}, it is currently: {value}" ) self._max_tokens = value @@ -309,9 +355,10 @@ class Model: @presence_penalty.setter def presence_penalty(self, value): - if int(value) < 0: + value = float(value) + if value < ModelLimits.MIN_PRESENCE_PENALTY or value > ModelLimits.MAX_PRESENCE_PENALTY: raise ValueError( - "presence_penalty must be greater than 0, it is currently " + str(value) + f"Presence penalty must be between {ModelLimits.MIN_PRESENCE_PENALTY} and {ModelLimits.MAX_PRESENCE_PENALTY}, it is currently: {value}" ) self._presence_penalty = value @@ -321,10 +368,10 @@ class Model: @frequency_penalty.setter def frequency_penalty(self, value): - if int(value) < 0: + value = float(value) + if value < ModelLimits.MIN_FREQUENCY_PENALTY or value > ModelLimits.MAX_FREQUENCY_PENALTY: raise ValueError( - "frequency_penalty must be greater than 0, it is currently " - + str(value) + f"Frequency penalty must be greater between {ModelLimits.MIN_FREQUENCY_PENALTY} and {ModelLimits.MAX_FREQUENCY_PENALTY}, it is currently: {value}" ) self._frequency_penalty = value @@ -335,10 +382,9 @@ class Model: @best_of.setter def best_of(self, value): value = int(value) - if value < 1 or value > 3: + if value < ModelLimits.MIN_BEST_OF or value > ModelLimits.MAX_BEST_OF: raise ValueError( - "best_of must be greater than 0 and ideally less than 3 to save tokens, it is currently " - + str(value) + f"Best of must be between {ModelLimits.MIN_BEST_OF} and {ModelLimits.MAX_BEST_OF}, it is currently: {value}\nNote that increasing the value of this parameter will act as a multiplier on the number of tokens requested!" ) self._best_of = value @@ -349,10 +395,9 @@ class Model: @prompt_min_length.setter def prompt_min_length(self, value): value = int(value) - if value < 10 or value > 4096: + if value < ModelLimits.MIN_PROMPT_MIN_LENGTH or value > ModelLimits.MAX_PROMPT_MIN_LENGTH: raise ValueError( - "prompt_min_length must be greater than 10 and less than 4096, it is currently " - + str(value) + f"Minimal prompt length must be between {ModelLimits.MIN_PROMPT_MIN_LENGTH} and {ModelLimits.MAX_PROMPT_MIN_LENGTH}, it is currently: {value}" ) self._prompt_min_length = value @@ -553,11 +598,10 @@ class Model: # Validate that all the parameters are in a good state before we send the request if len(prompt) < self.prompt_min_length: raise ValueError( - "Prompt must be greater than 8 characters, it is currently " - + str(len(prompt)) + f"Prompt must be greater than {self.prompt_min_length} characters, it is currently: {len(prompt)} characters" ) - print("The prompt about to be sent is " + prompt) + print(f"The prompt about to be sent is {prompt}") print( f"Overrides -> temp:{temp_override}, top_p:{top_p_override} frequency:{frequency_penalty_override}, presence:{presence_penalty_override}" ) @@ -599,7 +643,7 @@ class Model: async with aiohttp.ClientSession() as session: payload = { - "model": Models.CURIE, + "model": Models.LOW_USAGE_MODEL, "prompt": "test.", "temperature": 1, "top_p": 1, diff --git a/services/moderations_service.py b/services/moderations_service.py index 40970f4..4cd6940 100644 --- a/services/moderations_service.py +++ b/services/moderations_service.py @@ -19,6 +19,13 @@ class ModerationResult: DELETE = "delete" NONE = "none" +class ModerationOptions: + WARN = "warn" + DELETE = "delete" + RESET = "reset" + + OPTIONS = [WARN, DELETE, RESET] + class ThresholdSet: def __init__(self, h_t, hv_t, sh_t, s_t, sm_t, v_t, vg_t):