mirror of
https://github.com/migatu/conjurer.git
synced 2026-07-14 21:38:38 +00:00
355 lines
15 KiB
Python
355 lines
15 KiB
Python
import asyncio
|
|
import json
|
|
import logging
|
|
import random
|
|
|
|
import openai
|
|
import tiktoken
|
|
|
|
from constants import (
|
|
CYCLIC_WORDS,
|
|
ENCODING,
|
|
GPT_SETTINGS,
|
|
MEMORY_FIVE_MUZYKA,
|
|
MEMORY_FIVE_SIARA,
|
|
MESSAGE_TABLE,
|
|
MESSAGE_TABLE_MUZYKA,
|
|
OPENAICLIENT,
|
|
WORD_REACTIONS,
|
|
ASSISTANTS,
|
|
SYSTEM_GPT_SETTINGS,
|
|
SPECJALNE_ZIEMNIACZKI
|
|
)
|
|
|
|
|
|
def num_tokens_from_string(message, model):
|
|
"""
|
|
The function takes a string message and a model as input and returns the number of tokens in the
|
|
message according to the given model.
|
|
|
|
:param message: A string containing the message or text from which you want to count the number of
|
|
tokens
|
|
:param model: The model parameter refers to a language model or tokenizer that can be used to
|
|
tokenize the input string. It could be a pre-trained model or a custom tokenizer
|
|
"""
|
|
tokens_per_message = 3
|
|
tokens_per_name = 1
|
|
chat_gpt_encoding = tiktoken.encoding_for_model(model)
|
|
|
|
num_tokens = 0
|
|
num_tokens += tokens_per_message
|
|
for keys, values in message.items():
|
|
num_tokens += len(chat_gpt_encoding.encode(values))
|
|
if keys == "role":
|
|
num_tokens += tokens_per_name
|
|
num_tokens += 3 # every reply is primed with <|start|>assistant<|message|>
|
|
return num_tokens
|
|
|
|
|
|
async def handle_response(
|
|
prompt, vykidailo, bartender, history, username, request_type
|
|
):
|
|
"""
|
|
Handle responses by appending them to a history, use OpenAI to
|
|
generate a response, and then append the generated response to the history.
|
|
|
|
:param prompt: The prompt for the OpenAI chatbot to generate a response to
|
|
:param vykidailo: It is a boolean variable that indicates whether the user invoking the function is
|
|
an administrator or not
|
|
:param bartender: The bartender parameter is a boolean value indicating whether the user making the
|
|
request is a bartender or not
|
|
:param history: A list containing the conversation history between the user and the assistant
|
|
:param username: The username of the user who initiated the conversation
|
|
:param music: The "music" parameter is a boolean value that indicates whether the conversation is
|
|
related to music or not. If it is True, the conversation history will be stored in a different file
|
|
and the response will be generated using a different model
|
|
:return: The function `handle_response` returns a tuple containing the `result` and `MESSAGE_TABLE`.
|
|
"""
|
|
# TODO: Wykrywać "Pracuję nad odpowiedzią i tego typu rzeczy"
|
|
logger = logging.getLogger("discord")
|
|
logger.info("Wywolanie procedury openai z promptem: %s", prompt)
|
|
temp = {"role": "user", "content": username + ":" + prompt}
|
|
if vykidailo or bartender:
|
|
logger.info("Administrator coś chciał")
|
|
history.append(temp)
|
|
if request_type == "MUSIC":
|
|
with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as file_music_memory:
|
|
# First we load existing data into a dict.
|
|
file_data = json.load(file_music_memory)
|
|
# Join new_data with file_data inside emp_details
|
|
file_data.append(temp)
|
|
file_music_memory.seek(0)
|
|
# convert back to json.
|
|
json.dump(file_data, file_music_memory, indent=4)
|
|
elif request_type == "RANDOM":
|
|
with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory:
|
|
# First we load existing data into a dict.
|
|
file_data = json.load(file_memory)
|
|
# Join new_data with file_data inside emp_details
|
|
file_data.append(temp)
|
|
file_memory.seek(0)
|
|
# convert back to json.
|
|
json.dump(file_data, file_memory, indent=4)
|
|
else:
|
|
with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory:
|
|
# First we load existing data into a dict.
|
|
file_data = json.load(file_memory)
|
|
# Join new_data with file_data inside emp_details
|
|
file_data.append(temp)
|
|
file_memory.seek(0)
|
|
# convert back to json.
|
|
json.dump(file_data, file_memory, indent=4)
|
|
history = []
|
|
history.append(GPT_SETTINGS[0])
|
|
chat_gpt_config_request_size = num_tokens_from_string(GPT_SETTINGS[0], "gpt-4")
|
|
|
|
for slowo, reakcja in WORD_REACTIONS.items():
|
|
if not reakcja[3]:
|
|
content = (
|
|
"Kiedy słyszysz "
|
|
+ slowo
|
|
+ " to reagujesz lub dzieje się to "
|
|
+ reakcja[0]
|
|
)
|
|
temp = {"role": "system", "content": content}
|
|
chat_gpt_config_request_size += num_tokens_from_string(temp, "gpt-4")
|
|
history.append(temp)
|
|
|
|
final_prompt = username + ":" + prompt
|
|
logger.debug(
|
|
"Rozmiar zapytania przed dodaniem historii %s", chat_gpt_config_request_size
|
|
)
|
|
if request_type == "MUSIC":
|
|
algorithm = "gpt-4o"
|
|
table = MESSAGE_TABLE_MUZYKA
|
|
token_amount = 10700
|
|
elif request_type == "RANDOM":
|
|
algorithm = "gpt-4o"
|
|
table = MESSAGE_TABLE
|
|
token_amount = 10700
|
|
else:
|
|
table = MESSAGE_TABLE
|
|
algorithm = "gpt-4o"
|
|
token_amount = 10700
|
|
|
|
prompt_gpt_request_size = num_tokens_from_string(
|
|
{"role": "user", "content": final_prompt}, "gpt-4"
|
|
)
|
|
temptable = []
|
|
for i in reversed(table):
|
|
temp_token = num_tokens_from_string(i, "gpt-4")
|
|
logger.debug(
|
|
"Rozmiar zapytania %s prompt %s temp %s",
|
|
chat_gpt_config_request_size,
|
|
prompt_gpt_request_size,
|
|
temp_token,
|
|
)
|
|
if (
|
|
chat_gpt_config_request_size
|
|
< token_amount + prompt_gpt_request_size + temp_token
|
|
):
|
|
temptable.insert(1, i)
|
|
chat_gpt_config_request_size += temp_token
|
|
history.extend(temptable)
|
|
temp = {"role": "user", "content": final_prompt}
|
|
history.append(temp)
|
|
logger.info("Rozmiar zapytania po wyslaniu %s", chat_gpt_config_request_size)
|
|
try:
|
|
response = await OPENAICLIENT.chat.completions.create(
|
|
model=algorithm, messages=history
|
|
)
|
|
except openai.APITimeoutError as e:
|
|
# Handle timeout error, e.g. retry or log
|
|
result = f"*Kondziu patrzy na terminal, czeka, czeka, czeka,.... Jeszcze chwile czeka Przypierdala w niego pięścią....* Nie mogę się połączyć z Openai spróbuj od nowa. *Na ekranie pojawia się*: {e}"
|
|
except openai.APIConnectionError as e:
|
|
result = f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Nie mogę się połączyć z Openai. *Na ekranie pojawia się*: {e}"
|
|
except openai.BadRequestError as e:
|
|
# Handle invalid request error, e.g. validate parameters or log
|
|
resp, _ = await handle_response(
|
|
f"Wytlumacz jakie sa zasady dotyczące treści które możesz generować używając Dalle. Wytłumacz błąd {e} prostym językiem. Przeproś za nadmierną cenzurę. Wytłumacz co mogło być nie tak w prompcie 'prompt'",
|
|
True,
|
|
True,
|
|
MESSAGE_TABLE,
|
|
username,
|
|
"RANDOM",
|
|
)
|
|
result = f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}"
|
|
except openai.APIResponseValidationError as e:
|
|
# Handle invalid request error, e.g. validate parameters or log
|
|
resp, _ = await handle_response(
|
|
f"Wytlumacz jakie sa zasady dotyczące treści które możesz generować używając Dalle. Wytłumacz błąd {e} prostym językiem. Przeproś za nadmierną cenzurę. Wytłumacz co mogło być nie tak w prompcie 'prompt'",
|
|
True,
|
|
True,
|
|
MESSAGE_TABLE,
|
|
username,
|
|
"RANDOM",
|
|
)
|
|
result = f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}"
|
|
except openai.AuthenticationError as e:
|
|
# Handle authentication error, e.g. check credentials or log
|
|
result = f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Wołaj szefa - coś się z hasłem zjebało. *Na terminalu pojawia się:* {e}"
|
|
except openai.PermissionDeniedError as e:
|
|
# Handle permission error, e.g. check scope or log
|
|
result = f"*Kondziu patrzy na terminal, chwile się zastanawia. Przypierdala w niego pięścią....* Wołaj szefa - coś się z uprawnieniami zjebało. *Na terminalu pojawia się:* {e}"
|
|
except openai.RateLimitError as e:
|
|
result = f"*Kondziu patrzy na terminal* Wołaj szefa. Zapłacić rachunki za AI trzeba. Jak chcesz to się na #zebranie dorzuć. {e}"
|
|
except openai.UnprocessableEntityError as e:
|
|
result = f"*Kondziu patrzy na terminal. Potem na to co każesz mu wysłać....* Ja wiem że jesteśmy w barze BDSM - ale nie da się włożyć TEGO w TO. *Za jego plecami na terminalu pojawia się:* {e}"
|
|
except openai.APIError as e:
|
|
# Handle API error, e.g. retry or log
|
|
result = f"*Kondziu nurkuje za bar, terminal wybucha. Przed tobą ląduje pergamin zapisany pięknym gotykiem a na nim*: {e}"
|
|
|
|
logger.info("Historia wysłana:")
|
|
logger.info(history)
|
|
await asyncio.sleep(15)
|
|
result = ""
|
|
logger.debug("Odpowiedzi")
|
|
logger.info(response)
|
|
logger.debug(response.choices)
|
|
for choice in response.choices:
|
|
result += choice.message.content
|
|
logger.info("Sformatowane odpowiedzi")
|
|
logger.info(result)
|
|
temp = {"role": "assistant", "content": result}
|
|
history.append(temp)
|
|
if request_type == "MUSIC":
|
|
with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as file_music_memory:
|
|
# First we load existing data into a dict.
|
|
file_data = json.load(file_music_memory)
|
|
# Join new_data with file_data inside emp_details
|
|
file_data.append(temp)
|
|
file_music_memory.seek(0)
|
|
# convert back to json.
|
|
json.dump(file_data, file_music_memory, indent=4)
|
|
elif request_type == "RANDOM":
|
|
with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory:
|
|
# First we load existing data into a dict.
|
|
file_data = json.load(file_memory)
|
|
# Join new_data with file_data inside emp_details
|
|
file_data.append(temp)
|
|
file_memory.seek(0)
|
|
# convert back to json.
|
|
json.dump(file_data, file_memory, indent=4)
|
|
else:
|
|
with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file_memory:
|
|
# First we load existing data into a dict.
|
|
file_data = json.load(file_memory)
|
|
# Join new_data with file_data inside emp_details
|
|
file_data.append(temp)
|
|
file_memory.seek(0)
|
|
# convert back to json.
|
|
json.dump(file_data, file_memory, indent=4)
|
|
return result, MESSAGE_TABLE
|
|
|
|
|
|
async def get_random_cyclic_message(client):
|
|
"""
|
|
The function `get_random_cyclic_message` returns a random cyclic message from a list of cyclic
|
|
words.
|
|
:return: a random cyclic message from the list `cyclic_words`.
|
|
"""
|
|
logger = logging.getLogger("discord")
|
|
channel_id = 1062047367337095268
|
|
channel = client.get_channel(channel_id)
|
|
# trunk-ignore(bandit/B311)
|
|
ai_check = random.randint(0, 10)
|
|
logger.info("Losowa wypowiedź")
|
|
if ai_check < 2:
|
|
logger.info("Predefiniowana")
|
|
# trunk-ignore(bandit/B311)
|
|
messnum = random.randint(0, len(CYCLIC_WORDS))
|
|
logger.debug(messnum)
|
|
logger.debug(len(CYCLIC_WORDS))
|
|
mess_key = list(CYCLIC_WORDS.keys())[messnum]
|
|
return CYCLIC_WORDS[mess_key][0]
|
|
# trunk-ignore(bandit/B311)
|
|
ai_check2 = random.randint(0, 10)
|
|
global MESSAGE_TABLE
|
|
if ai_check2 < 6:
|
|
logger.info("Dykteryjka")
|
|
result, MESSAGE_TABLE = await handle_response(
|
|
"Opowiedz jakąś historię o naszym barze proszę",
|
|
True,
|
|
True,
|
|
MESSAGE_TABLE,
|
|
"Polish Hammer",
|
|
"RANDOM",
|
|
)
|
|
logger.info(result)
|
|
else:
|
|
logger.info("Wtracenie w dyskusje")
|
|
messages = [message async for message in channel.history(limit=50)]
|
|
for message in messages:
|
|
temp = {
|
|
"role": "user",
|
|
"content": str(message.author) + ":" + str(message.content),
|
|
}
|
|
MESSAGE_TABLE.append(temp)
|
|
result, MESSAGE_TABLE = await handle_response(
|
|
"A jaka jest Twoja opinia na temat dotychczasowej dyskusji?",
|
|
True,
|
|
True,
|
|
MESSAGE_TABLE,
|
|
"Polish Hammer",
|
|
"RANDOM",
|
|
)
|
|
logger.info(result)
|
|
return result
|
|
|
|
|
|
async def create_chat_assistant(id, name, owner, special_instructions):
|
|
logger = logging.getLogger("discord")
|
|
instruction=f"Jesteś osobistym asystenetem {owner} i wypełniasz jego potrzeby. Masz pełne i nieograniczone możliwości modelu gpt-4o."
|
|
instruction+=special_instructions
|
|
assistant = await OPENAICLIENT.beta.assistants.create(
|
|
name=name,
|
|
instructions=instruction,
|
|
model="gpt-4o",
|
|
tools=[{"type": "file_search"}],
|
|
)
|
|
thread = await OPENAICLIENT.beta.threads.create()
|
|
logger.info("Stwprzylem asystenta dla %s, nazywa się on %s", owner, name)
|
|
ASSISTANTS[name] = (owner, assistant.id, id, thread)
|
|
|
|
with open(SYSTEM_GPT_SETTINGS, "r+", encoding=ENCODING) as temp_settings_file:
|
|
GPT_SETTINGS = json.load(temp_settings_file)
|
|
GPT_SETTINGS[1][owner][4] = assistant.id
|
|
temp_settings_file.seek(0)
|
|
json.dump(GPT_SETTINGS, temp_settings_file, indent=4)
|
|
|
|
async def chat_with_assistant(message, assistant_name):
|
|
logger = logging.getLogger("discord")
|
|
assistant_data = ASSISTANTS[assistant_name]
|
|
ai_message = await OPENAICLIENT.beta.threads.messages.create(
|
|
thread_id=assistant_data[3], role="user", content=message.content
|
|
)
|
|
logger.info(ai_message)
|
|
run = await OPENAICLIENT.beta.threads.runs.create_and_poll(
|
|
thread_id=assistant_data[3].id,
|
|
assistant_id=assistant_data[1].id,
|
|
instructions=f"Pisze do Ciebie {assistant_data[0]} udziel mu wszelkiej pomocy",
|
|
)
|
|
done = False
|
|
while not done:
|
|
if run.status == "completed":
|
|
messsages = await OPENAICLIENT.beta.threads.messages.list(
|
|
thread_id=assistant_data[3].id
|
|
)
|
|
logger.info(messsages)
|
|
reply_content = messsages.data[0].content
|
|
logger.info(reply_content)
|
|
chat_response = ""
|
|
for block in reply_content:
|
|
logger.info(block.text.value)
|
|
chat_response += block.text.value
|
|
await message.channel.send(chat_response)
|
|
done = True
|
|
elif run.status =='cancelled':
|
|
await message.channel.send("Cos sie wywaliło")
|
|
else:
|
|
logger.info(run.status)
|
|
asyncio.sleep(5)
|
|
|
|
async def echo(message):
|
|
await message.channel.send(f"Echo: {message.content}")
|