Merge pull request #120 from duckdoom4/remove-magic-values

Add missing models / Code cleanup
Kaveen Kumarasinghe 2 years ago committed by GitHub
commit e4e390c640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,8 +5,9 @@ import re
import discord import discord
from models.deepl_model import TranslationModel from models.deepl_model import TranslationModel
from services.moderations_service import ModerationOptions
from services.usage_service import UsageService 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 from services.environment_service import EnvService
usage_service = UsageService(Path(os.environ.get("DATA_DIR", os.getcwd()))) 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 ): # 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""" """gets valid values for the value option"""
values = { values = {
"max_conversation_length": [str(num) for num in range(1, 500, 2)], "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(1, 4 + 1)], "num_images": [str(num) for num in range(ModelLimits.MIN_NUM_IMAGES, ModelLimits.MAX_NUM_IMAGES + 1)],
"mode": ["temperature", "top_p"], "mode": Mode.ALL_MODES,
"model": ["text-davinci-003", "text-curie-001"], "model": Models.TEXT_MODELS,
"low_usage_mode": ["True", "False"], "low_usage_mode": ["True", "False"],
"image_size": ["256x256", "512x512", "1024x1024"], "image_size": ImageSize.ALL_SIZES,
"summarize_conversation": ["True", "False"], "summarize_conversation": ["True", "False"],
"welcome_message_enabled": ["True", "False"], "welcome_message_enabled": ["True", "False"],
"num_static_conversation_items": [str(num) for num in range(5, 20 + 1)], "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(5, 15 + 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(800, 3500, 50)], "summarize_threshold": [str(num) for num in range(ModelLimits.MIN_SUMMARIZE_THRESHOLD, ModelLimits.MAX_SUMMARIZE_THRESHOLD + 1, 50)],
"type": ["warn", "delete"], "type": ["warn", "delete"],
} }
for parameter in values: options = values.get(ctx.options["parameter"], [])
if parameter == ctx.options["parameter"]: if options:
return [ return [
value value
for value in values[ctx.options["parameter"]] for value in options
if value.startswith(ctx.value.lower()) 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 await ctx.interaction.response.defer() # defer so the autocomplete in int values doesn't error but rather just says not found
return [] return []
@ -64,7 +66,7 @@ class Settings_autocompleter:
print(f"The value is {ctx.value}") print(f"The value is {ctx.value}")
return [ return [
value value
for value in ["warn", "delete", "reset"] for value in ModerationOptions.OPTIONS
if value.startswith(ctx.value.lower()) if value.startswith(ctx.value.lower())
] ]

@ -18,29 +18,102 @@ from discord import File
class Mode: class Mode:
TOP_P = "top_p"
TEMPERATURE = "temperature" TEMPERATURE = "temperature"
TOP_P = "top_p"
ALL_MODES = [TEMPERATURE, TOP_P]
class Models: class Models:
# Text models
DAVINCI = "text-davinci-003" DAVINCI = "text-davinci-003"
CURIE = "text-curie-001" CURIE = "text-curie-001"
BABBAGE = "text-babbage-001"
ADA = "text-ada-001"
# Code models
CODE_DAVINCI = "code-davinci-002"
CODE_CUSHMAN = "code-cushman-001"
# Embedding models
EMBEDDINGS = "text-embedding-ada-002" EMBEDDINGS = "text-embedding-ada-002"
# Edit models
EDIT = "text-davinci-edit-001" EDIT = "text-davinci-edit-001"
CODE_EDIT = "code-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
# Tokens Mapping
TOKEN_MAPPING = {
"text-davinci-003": 4024,
"text-curie-001": 2024,
"text-babbage-001": 2024,
"text-ada-001": 2024,
"code-davinci-002": 7900,
"code-cushman-001": 2024,
}
@staticmethod
def get_max_tokens(model: str) -> int:
return Models.TOKEN_MAPPING.get(model, 4024)
class ImageSize: class ImageSize:
LARGE = "1024x1024"
MEDIUM = "512x512"
SMALL = "256x256" 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: class Model:
def __init__(self, usage_service): def __init__(self, usage_service):
self._mode = Mode.TEMPERATURE self._mode = Mode.TEMPERATURE
self._temp = 0.8 # Higher value means more random, lower value means more likely to be a coherent sentence self._temp = 0.8 # Higher value means more random, lower value means more likely to be a coherent sentence
self._top_p = 0.95 # 1 is equivalent to greedy sampling, 0.1 means that the model will only consider the top 10% of the probability distribution self._top_p = 1 # 1 is equivalent to greedy sampling, 0.1 means that the model will only consider the top 10% of the probability distribution
self._max_tokens = 4000 # The maximum number of tokens the model can generate self._max_tokens = 4000 # The maximum number of tokens the model can generate
self._presence_penalty = ( self._presence_penalty = (
0 # Penalize new tokens based on whether they appear in the text so far 0 # Penalize new tokens based on whether they appear in the text so far
@ -50,7 +123,7 @@ class Model:
self._best_of = 1 # Number of responses to compare the loglikelihoods of self._best_of = 1 # Number of responses to compare the loglikelihoods of
self._prompt_min_length = 8 self._prompt_min_length = 8
self._max_conversation_length = 100 self._max_conversation_length = 100
self._model = Models.DAVINCI self._model = Models.DEFAULT
self._low_usage_mode = False self._low_usage_mode = False
self.usage_service = usage_service self.usage_service = usage_service
self.DAVINCI_ROLES = ["admin", "Admin", "GPT", "gpt"] self.DAVINCI_ROLES = ["admin", "Admin", "GPT", "gpt"]
@ -94,11 +167,11 @@ class Model:
@num_static_conversation_items.setter @num_static_conversation_items.setter
def num_static_conversation_items(self, value): def num_static_conversation_items(self, value):
value = int(value) value = int(value)
if value < 3: if value < ModelLimits.MIN_NUM_STATIC_CONVERSATION_ITEMS:
raise ValueError("num_static_conversation_items must be >= 3") raise ValueError(f"Number of static conversation items must be >= {ModelLimits.MIN_NUM_STATIC_CONVERSATION_ITEMS}")
if value > 20: if value > ModelLimits.MAX_NUM_STATIC_CONVERSATION_ITEMS:
raise ValueError( 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 self._num_static_conversation_items = value
@ -109,11 +182,11 @@ class Model:
@num_conversation_lookback.setter @num_conversation_lookback.setter
def num_conversation_lookback(self, value): def num_conversation_lookback(self, value):
value = int(value) value = int(value)
if value < 3: if value < ModelLimits.MIN_NUM_CONVERSATION_LOOKBACK:
raise ValueError("num_conversation_lookback must be >= 3") raise ValueError(f"Number of conversations to look back on must be >= {ModelLimits.MIN_NUM_CONVERSATION_LOOKBACK}")
if value > 15: if value > ModelLimits.MAX_NUM_CONVERSATION_LOOKBACK:
raise ValueError( 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 self._num_conversation_lookback = value
@ -128,7 +201,7 @@ class Model:
elif value.lower() == "false": elif value.lower() == "false":
self._welcome_message_enabled = False self._welcome_message_enabled = False
else: else:
raise ValueError("Value must be either true or false!") raise ValueError("Value must be either `true` or `false`!")
@property @property
def summarize_threshold(self): def summarize_threshold(self):
@ -137,9 +210,9 @@ class Model:
@summarize_threshold.setter @summarize_threshold.setter
def summarize_threshold(self, value): def summarize_threshold(self, value):
value = int(value) value = int(value)
if value < 800 or value > 4000: if value < ModelLimits.MIN_SUMMARIZE_THRESHOLD or value > ModelLimits.MAX_SUMMARIZE_THRESHOLD:
raise ValueError( 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 self._summarize_threshold = value
@ -155,7 +228,7 @@ class Model:
elif value.lower() == "false": elif value.lower() == "false":
value = False value = False
else: else:
raise ValueError("Value must be either true or false!") raise ValueError("Value must be either `true` or `false`!")
self._summarize_conversations = value self._summarize_conversations = value
@property @property
@ -164,11 +237,11 @@ class Model:
@image_size.setter @image_size.setter
def image_size(self, value): def image_size(self, value):
if value in ImageSize.__dict__.values(): if value in ImageSize.ALL_SIZES:
self._image_size = value self._image_size = value
else: else:
raise ValueError( 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 @property
@ -178,8 +251,8 @@ class Model:
@num_images.setter @num_images.setter
def num_images(self, value): def num_images(self, value):
value = int(value) value = int(value)
if value > 4 or value <= 0: if value < ModelLimits.MIN_NUM_IMAGES or value > ModelLimits.MAX_NUM_IMAGES:
raise ValueError("num_images must be less than 4 and at least 1.") 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 self._num_images = value
@property @property
@ -194,14 +267,14 @@ class Model:
elif value.lower() == "false": elif value.lower() == "false":
value = False value = False
else: else:
raise ValueError("Value must be either true or false!") raise ValueError("Value must be either `true` or `false`!")
if value: if value:
self._model = Models.CURIE self._model = Models.LOW_USAGE_MODEL
self.max_tokens = 1900 self.max_tokens = 1900
self.model_max_tokens = 1000 self.model_max_tokens = 1000
else: else:
self._model = Models.DAVINCI self._model = Models.DEFAULT
self.max_tokens = 4000 self.max_tokens = 4000
self.model_max_tokens = 4024 self.model_max_tokens = 4024
@ -211,12 +284,13 @@ class Model:
@model.setter @model.setter
def model(self, model): def model(self, model):
if model not in [Models.DAVINCI, Models.CURIE]: if model not in Models.TEXT_MODELS:
raise ValueError( raise ValueError(f"Invalid model, must be one of: {Models.TEXT_MODELS}")
"Invalid model, must be text-davinci-003 or text-curie-001"
)
self._model = model self._model = model
# Set the token count
self._max_tokens = Models.get_max_tokens(self._model)
@property @property
def max_conversation_length(self): def max_conversation_length(self):
return self._max_conversation_length return self._max_conversation_length
@ -224,11 +298,11 @@ class Model:
@max_conversation_length.setter @max_conversation_length.setter
def max_conversation_length(self, value): def max_conversation_length(self, value):
value = int(value) value = int(value)
if value < 1: if value < ModelLimits.MIN_CONVERSATION_LENGTH:
raise ValueError("Max conversation length must be greater than 1") raise ValueError(f"Max conversation length must be greater than {ModelLimits.MIN_CONVERSATION_LENGTH}")
if value > 500: if value > ModelLimits.MAX_CONVERSATION_LENGTH:
raise ValueError( 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 self._max_conversation_length = value
@ -238,14 +312,17 @@ class Model:
@mode.setter @mode.setter
def mode(self, value): def mode(self, value):
if value not in [Mode.TOP_P, Mode.TEMPERATURE]: if value not in Mode.ALL_MODES:
raise ValueError("mode must be either 'top_p' or 'temperature'") raise ValueError(f"Mode must be one of: {Mode.ALL_MODES}")
# Set the other mode to 1 (the default) so that it is not used
# See https://beta.openai.com/docs/api-reference/completions/create#completions/create-temperature
if value == Mode.TOP_P: if value == Mode.TOP_P:
self._top_p = 0.1 self._temp = 1
self._temp = 0.7
elif value == Mode.TEMPERATURE: elif value == Mode.TEMPERATURE:
self._top_p = 0.9 self._top_p = 1
self._temp = 0.6 else:
raise ValueError(f"Unknown mode: {value}")
self._mode = value self._mode = value
@ -256,10 +333,9 @@ class Model:
@temp.setter @temp.setter
def temp(self, value): def temp(self, value):
value = float(value) value = float(value)
if value < 0 or value > 2: if value < ModelLimits.MIN_TEMPERATURE or value > ModelLimits.MAX_TEMPERATURE:
raise ValueError( raise ValueError(
"temperature must be greater than 0 and less than 2, it is currently " f"Temperature must be between {ModelLimits.MIN_TEMPERATURE} and {ModelLimits.MAX_TEMPERATURE}, it is currently: {value}"
+ str(value)
) )
self._temp = value self._temp = value
@ -271,10 +347,9 @@ class Model:
@top_p.setter @top_p.setter
def top_p(self, value): def top_p(self, value):
value = float(value) value = float(value)
if value < 0 or value > 1: if value < ModelLimits.MIN_TOP_P or value > ModelLimits.MAX_TOP_P:
raise ValueError( raise ValueError(
"top_p must be greater than 0 and less than 1, it is currently " f"Top P must be between {ModelLimits.MIN_TOP_P} and {ModelLimits.MAX_TOP_P}, it is currently: {value}"
+ str(value)
) )
self._top_p = value self._top_p = value
@ -285,10 +360,9 @@ class Model:
@max_tokens.setter @max_tokens.setter
def max_tokens(self, value): def max_tokens(self, value):
value = int(value) value = int(value)
if value < 15 or value > 4096: if value < ModelLimits.MIN_TOKENS or value > ModelLimits.MAX_TOKENS:
raise ValueError( raise ValueError(
"max_tokens must be greater than 15 and less than 4096, it is currently " f"Max tokens must be between {ModelLimits.MIN_TOKENS} and {ModelLimits.MAX_TOKENS}, it is currently: {value}"
+ str(value)
) )
self._max_tokens = value self._max_tokens = value
@ -298,9 +372,10 @@ class Model:
@presence_penalty.setter @presence_penalty.setter
def presence_penalty(self, value): 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( 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 self._presence_penalty = value
@ -310,10 +385,10 @@ class Model:
@frequency_penalty.setter @frequency_penalty.setter
def frequency_penalty(self, value): 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( raise ValueError(
"frequency_penalty must be greater than 0, it is currently " f"Frequency penalty must be greater between {ModelLimits.MIN_FREQUENCY_PENALTY} and {ModelLimits.MAX_FREQUENCY_PENALTY}, it is currently: {value}"
+ str(value)
) )
self._frequency_penalty = value self._frequency_penalty = value
@ -324,10 +399,9 @@ class Model:
@best_of.setter @best_of.setter
def best_of(self, value): def best_of(self, value):
value = int(value) value = int(value)
if value < 1 or value > 3: if value < ModelLimits.MIN_BEST_OF or value > ModelLimits.MAX_BEST_OF:
raise ValueError( raise ValueError(
"best_of must be greater than 0 and ideally less than 3 to save tokens, it is currently " 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!"
+ str(value)
) )
self._best_of = value self._best_of = value
@ -338,10 +412,9 @@ class Model:
@prompt_min_length.setter @prompt_min_length.setter
def prompt_min_length(self, value): def prompt_min_length(self, value):
value = int(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( raise ValueError(
"prompt_min_length must be greater than 10 and less than 4096, it is currently " f"Minimal prompt length must be between {ModelLimits.MIN_PROMPT_MIN_LENGTH} and {ModelLimits.MAX_PROMPT_MIN_LENGTH}, it is currently: {value}"
+ str(value)
) )
self._prompt_min_length = value self._prompt_min_length = value
@ -542,11 +615,10 @@ class Model:
# Validate that all the parameters are in a good state before we send the request # Validate that all the parameters are in a good state before we send the request
if len(prompt) < self.prompt_min_length: if len(prompt) < self.prompt_min_length:
raise ValueError( raise ValueError(
"Prompt must be greater than 8 characters, it is currently " f"Prompt must be greater than {self.prompt_min_length} characters, it is currently: {len(prompt)} characters"
+ str(len(prompt))
) )
print("The prompt about to be sent is " + prompt) print(f"The prompt about to be sent is {prompt}")
print( print(
f"Overrides -> temp:{temp_override}, top_p:{top_p_override} frequency:{frequency_penalty_override}, presence:{presence_penalty_override}" f"Overrides -> temp:{temp_override}, top_p:{top_p_override} frequency:{frequency_penalty_override}, presence:{presence_penalty_override}"
) )
@ -588,7 +660,7 @@ class Model:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
payload = { payload = {
"model": Models.CURIE, "model": Models.LOW_USAGE_MODEL,
"prompt": "test.", "prompt": "test.",
"temperature": 1, "temperature": 1,
"top_p": 1, "top_p": 1,

@ -19,6 +19,13 @@ class ModerationResult:
DELETE = "delete" DELETE = "delete"
NONE = "none" NONE = "none"
class ModerationOptions:
WARN = "warn"
DELETE = "delete"
RESET = "reset"
OPTIONS = [WARN, DELETE, RESET]
class ThresholdSet: class ThresholdSet:
def __init__(self, h_t, hv_t, sh_t, s_t, sm_t, v_t, vg_t): def __init__(self, h_t, hv_t, sh_t, s_t, sm_t, v_t, vg_t):

Loading…
Cancel
Save