REVAMP ASYNC, fully working now, massively improve immage prompt optimizer

Kaveen Kumarasinghe 2 years ago
parent b3fc6aa503
commit 193a967d41

@ -10,11 +10,16 @@
- **AUTOMATIC CHAT SUMMARIZATION!** - When the context limit of a conversation is reached, the bot will use GPT3 itself to summarize the conversation to reduce the tokens, and continue conversing with you, this allows you to chat for a long time!
- **PERMANENT MEMORY FOR CONVERSATIONS COMING SOON USING EMBEDDINGS!**
- **DALL-E Image Generation**
- **REDO ON EDIT** - When you edit a prompt, it will automatically be resent to GPT3 and the response updated!
- **Fully async!** - The bot will never be blocked when processing someone else's request, allowing for use in large servers with multiple messages per second!
- **Fully async and fault tolerant - REVAMPED** - The bot will never be blocked when processing someone else's request, allowing for use in large servers with multiple messages per second!
- No need for the OpenAI and Asgiref libraries anymore!
# Features
- **Directly prompt GPT3 with `!g <prompt>`**

@ -96,16 +96,17 @@ class DrawDallEService(commands.Cog, name="DrawDallEService"):
message = await response_message.edit(
embed=embed,
file=file,
view=SaveView(image_urls, self, self.converser_cog),
)
await message.edit(view=SaveView(image_urls, self, self.converser_cog, message))
else: # Varying case
if not draw_from_optimizer:
result_message = await response_message.edit_original_response(
content="Image variation completed!",
embed=embed,
file=file,
view=SaveView(image_urls, self, self.converser_cog, True),
)
await result_message.edit(view=SaveView(image_urls, self, self.converser_cog,result_message, True))
redo_users[message.author.id] = RedoUser(
prompt, message, result_message
)
@ -214,7 +215,7 @@ class SaveView(discord.ui.View):
self, image_urls, cog, converser_cog, message, no_retry=False, only_save=None
):
super().__init__(
timeout=10 if not only_save else None
timeout=3600 if not only_save else None
) # 10 minute timeout for Retry, Save
self.image_urls = image_urls
self.cog = cog

@ -48,13 +48,14 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
"that'll be all",
]
self.last_used = {}
self.GLOBAL_COOLDOWN_TIME = 1
self.GLOBAL_COOLDOWN_TIME = 0.25
self.usage_service = usage_service
self.model = model
self.summarize = self.model.summarize_conversations
self.deletion_queue = deletion_queue
self.users_to_interactions = defaultdict(list)
self.redo_users = {}
self.awaiting_responses = []
try:
# Attempt to read a conversation starter text string from the file.
@ -438,6 +439,9 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
)
self.check_conversing(message)
# We got a response, we can allow the user to type again
self.awaiting_responses.remove(message.author.id)
# If the response text is > 3500 characters, paginate and send
debug_message = self.generate_debug_message(prompt, response)
@ -458,9 +462,6 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
self.redo_users[message.author.id].add_interaction(
response_message.id
)
print(
f"Added the interaction {response_message.id} to the redo user {message.author.id}"
)
original_message[message.author.id] = message.id
else:
# We have response_text available, this is the original message that we want to edit
@ -602,13 +603,15 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
if prompt == "converse" or prompt == "converse nothread":
# If the user is already conversating, don't let them start another conversation
if message.author.id in self.conversating_users:
await message.reply(
message = await message.reply(
"You are already conversating with GPT3. End the conversation with !g end or just say 'end' in a supported channel"
)
await self.deletion_queue(message)
return
# If the user is not already conversating, start a conversation with GPT3
self.conversating_users[message.author.id] = User(message.author.id)
# Append the starter text for gpt3 to the user's history so it gets concatenated with the prompt later
self.conversating_users[message.author.id].history.append(
self.CONVERSATION_STARTER_TEXT
@ -653,6 +656,26 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"):
# history to the prompt. We can do this by checking if the user is in the conversating_users dictionary, and if they are,
# we can append their history to the prompt.
if message.author.id in self.conversating_users:
# Since this is async, we don't want to allow the user to send another prompt while a conversation
# prompt is processing, that'll mess up the conversation history!
if message.author.id in self.awaiting_responses:
message = await message.reply(
"You are already waiting for a response from GPT3. Please wait for it to respond before sending another message."
)
# get the current date, add 10 seconds to it, and then turn it into a timestamp.
# we need to use our deletion service because this isn't an interaction, it's a regular message.
deletion_time = datetime.datetime.now() + datetime.timedelta(seconds=10)
deletion_time = deletion_time.timestamp()
deletion_message = Deletion(message, deletion_time)
await self.deletion_queue.put(deletion_message)
return
self.awaiting_responses.append(message.author.id)
self.conversating_users[message.author.id].history.append(
"\nHuman: " + prompt + "<|endofstatement|>\n"
)

@ -2,6 +2,8 @@ import asyncio
import traceback
from datetime import datetime
import discord
class Deletion:
def __init__(self, message, timestamp):
@ -26,7 +28,11 @@ class Deletion:
# Check if the current timestamp is greater than the deletion timestamp
if datetime.now().timestamp() > deletion.timestamp:
# If the deletion timestamp has passed, delete the message
await deletion.message.delete_original_response()
# check if deletion.message is of type discord.Message
if isinstance(deletion.message, discord.Message):
await deletion.message.delete()
else:
await deletion.message.delete_original_response()
else:
await deletion_queue.put(deletion)

@ -4,14 +4,13 @@ import tempfile
import uuid
from typing import Tuple, List, Any
import aiohttp
import discord
import openai
# An enum of two modes, TOP_P or TEMPERATURE
import requests
from PIL import Image
from discord import File
from asgiref.sync import sync_to_async
class Mode:
@ -72,8 +71,7 @@ class Model:
"model_max_tokens",
]
openai.api_key = os.getenv("OPENAI_TOKEN")
self.openai_key = os.getenv("OPENAI_TOKEN")
# Use the @property and @setter decorators for all the self fields to provide value checking
@property
def summarize_threshold(self):
@ -305,22 +303,29 @@ class Model:
tokens = self.usage_service.count_tokens(summary_request_text)
response = await sync_to_async(openai.Completion.create)(
model=Models.DAVINCI,
prompt=summary_request_text,
temperature=0.5,
top_p=1,
max_tokens=self.max_tokens - tokens,
presence_penalty=self.presence_penalty,
frequency_penalty=self.frequency_penalty,
best_of=self.best_of,
)
print(response["choices"][0]["text"])
tokens_used = int(response["usage"]["total_tokens"])
self.usage_service.update_usage(tokens_used)
return response
async with aiohttp.ClientSession() as session:
payload = {
"model": Models.DAVINCI,
"prompt": summary_request_text,
"temperature": 0.5,
"top_p": 1,
"max_tokens": self.max_tokens - tokens,
"presence_penalty": self.presence_penalty,
"frequency_penalty": self.frequency_penalty,
"best_of": self.best_of,
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.openai_key}"
}
async with session.post("https://api.openai.com/v1/completions", json=payload, headers=headers) as resp:
response = await resp.json()
print(response["choices"][0]["text"])
tokens_used = int(response["usage"]["total_tokens"])
self.usage_service.update_usage(tokens_used)
return response
async def send_request(
self,
@ -347,31 +352,28 @@ class Model:
print("The prompt about to be sent is " + prompt)
response = await sync_to_async(openai.Completion.create)(
model=Models.DAVINCI
if any(role.name in self.DAVINCI_ROLES for role in message.author.roles)
else self.model, # Davinci override for admin users
prompt=prompt,
temperature=self.temp if not temp_override else temp_override,
top_p=self.top_p if not top_p_override else top_p_override,
max_tokens=self.max_tokens - tokens
if not max_tokens_override
else max_tokens_override,
presence_penalty=self.presence_penalty
if not presence_penalty_override
else presence_penalty_override,
frequency_penalty=self.frequency_penalty
if not frequency_penalty_override
else frequency_penalty_override,
best_of=self.best_of if not best_of_override else best_of_override,
)
# print(response.__dict__)
# Parse the total tokens used for this request and response pair from the response
tokens_used = int(response["usage"]["total_tokens"])
self.usage_service.update_usage(tokens_used)
return response
async with aiohttp.ClientSession() as session:
payload = {
"model": Models.DAVINCI if any(
role.name in self.DAVINCI_ROLES for role in message.author.roles) else self.model,
"prompt": prompt,
"temperature": self.temp if not temp_override else temp_override,
"top_p": self.top_p if not top_p_override else top_p_override,
"max_tokens": self.max_tokens - tokens if not max_tokens_override else max_tokens_override,
"presence_penalty": self.presence_penalty if not presence_penalty_override else presence_penalty_override,
"frequency_penalty": self.frequency_penalty if not frequency_penalty_override else frequency_penalty_override,
"best_of": self.best_of if not best_of_override else best_of_override,
}
headers = {
"Authorization": f"Bearer {self.openai_key}"
}
async with session.post("https://api.openai.com/v1/completions", json=payload, headers=headers) as resp:
response = await resp.json()
# Parse the total tokens used for this request and response pair from the response
tokens_used = int(response["usage"]["total_tokens"])
self.usage_service.update_usage(tokens_used)
return response
async def send_image_request(self, prompt, vary=None) -> tuple[File, list[Any]]:
# Validate that all the parameters are in a good state before we send the request
@ -385,20 +387,39 @@ class Model:
# print("The prompt about to be sent is " + prompt)
self.usage_service.update_usage_image(self.image_size)
response = None
if not vary:
response = await sync_to_async(openai.Image.create)(
prompt=prompt,
n=self.num_images,
size=self.image_size,
)
payload = {
"prompt": prompt,
"n": self.num_images,
"size": self.image_size
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.openai_key}"
}
async with aiohttp.ClientSession() as session:
async with session.post("https://api.openai.com/v1/images/generations", json=payload, headers=headers) as resp:
response = await resp.json()
else:
response = await sync_to_async(openai.Image.create_variation)(
image=open(vary, "rb"),
n=self.num_images,
size=self.image_size,
)
print(response.__dict__)
async with aiohttp.ClientSession() as session:
data = aiohttp.FormData()
data.add_field("n", str(self.num_images))
data.add_field("size", self.image_size)
with open(vary, "rb") as f:
data.add_field("image", f, filename="file.png", content_type="image/png")
async with session.post(
"https://api.openai.com/v1/images/variations",
headers={
"Authorization": "Bearer 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",
},
data=data
) as resp:
response = await resp.json()
print(response)
image_urls = []
for result in response["data"]:

@ -1,7 +1,5 @@
py-cord==2.3.2
openai==0.25.0
Pillow==9.3.0
python-dotenv==0.21.0
requests==2.28.1
transformers==4.25.1
asgiref==3.6.0
transformers==4.25.1
Loading…
Cancel
Save