From 7ef1502249dc1fce31e1d5749cdb8ad2a6f78bb9 Mon Sep 17 00:00:00 2001 From: Michal Tuszowski Date: Wed, 20 Aug 2025 21:20:00 +0200 Subject: [PATCH] upgrade to latest OpenAI API usage and logging improvements --- ai_functions.py | 238 +++++++++++++++++++++++------------------------- constants.py | 4 +- 2 files changed, 116 insertions(+), 126 deletions(-) diff --git a/ai_functions.py b/ai_functions.py index 9ae5330..ef48fb7 100644 --- a/ai_functions.py +++ b/ai_functions.py @@ -19,13 +19,26 @@ from constants import ( OPENAICLIENT, SYSTEM_GPT_SETTINGS, WORD_REACTIONS, + CHEAP_MODEL, + LATEST_MODEL ) # this do per user VECTOR_STORE_ID = -1 +def select_model(req_type: str, algo: str) -> str: + # Jeżeli jawnie podano algorithm (i nie jest 'auto'/''): + if algo and str(algo).strip().lower() not in ("auto",): + # wyjątek: MUZYKA ma zawsze być tania — nadpisujemy TYLKO jeśli przyszedł domyślny 'gpt-4o' + if req_type == "MUSIC" and algo.strip() in (LATEST_MODEL,): + return CHEAP_MODEL + return algo + # Auto-dobór: + if req_type == "MUSIC": + return CHEAP_MODEL + return LATEST_MODEL -async def _openai_call(messages, model, temperature=0.2): +async def openai_call(messages, model, temperature=0.2): """ Responses API dla 4o/4.1*, fallback Chat Completions dla gpt-3.5-turbo. Zwraca czysty string odpowiedzi. @@ -144,6 +157,7 @@ async def handle_response( request_type, algorithm="gpt-4o", none_request="", + internal_retry: bool = False ): """ Handle responses by appending them to a history, use OpenAI to @@ -167,102 +181,82 @@ async def handle_response( """ 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) - elif request_type == "GENERAL": - 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 = [] - if request_type != "NONE": - history.append(GPT_SETTINGS[0]) - chat_gpt_config_request_size = num_tokens_from_string(GPT_SETTINGS[0], "gpt-4") + model_to_use = select_model(request_type, algorithm) + logger.info("Wybrany model: %s", model_to_use) + if request_type == "MUSIC" and model_to_use == "gpt-4o-mini": + try: + # nic — normalnie pójdzie Responses API + pass + except Exception: + model_to_use = "gpt-3.5-turbo" + # --- 2) Budowa historii (token budget + reguły systemowe) --- + # NOTE: ignorujemy przekazany 'history' jako listę (tak było wcześniej), + # ale zwracamy aktualną tablicę do nadpisania w miejscach wołania (back-compat). + base_system = GPT_SETTINGS[0] # zakładamy {"role":"system","content":...} + history_msgs = [] + if request_type != "NONE": + history_msgs.append(base_system) + chat_gpt_config_request_size = num_tokens_from_string(base_system, "gpt-4") + + # Dynamiczne mikro-reguły (WORD_REACTIONS), jak w Twoim kodzie 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) + content = f"Kiedy słyszysz {slowo} to reagujesz lub dzieje się to {reakcja[0]}" + sys_msg = {"role": "system", "content": content} + chat_gpt_config_request_size += num_tokens_from_string(sys_msg, "gpt-4") + history_msgs.append(sys_msg) - final_prompt = username + ":" + prompt - logger.debug( - "Rozmiar zapytania przed dodaniem historii %s", chat_gpt_config_request_size - ) + # wybór właściwej tablicy pamięci i budżetu if request_type == "MUSIC": table = MESSAGE_TABLE_MUZYKA token_amount = 10700 - elif request_type == "RANDOM": - table = MESSAGE_TABLE - token_amount = 10700 - elif request_type == "GENERAL": + elif request_type in ("RANDOM", "GENERAL"): table = MESSAGE_TABLE token_amount = 10700 else: table = [] token_amount = 10000 - 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) + # doklejanie historii od końca aż do limitu (zachowana kolejność czasowa) + final_prompt = f"{username}:{prompt}" + prompt_gpt_request_size = num_tokens_from_string({"role": "user", "content": final_prompt}, "gpt-4") + + acc = [] + for msg in reversed(table): + t = num_tokens_from_string(msg, "gpt-4") + if chat_gpt_config_request_size + prompt_gpt_request_size + t <= token_amount: + acc.append(msg) + chat_gpt_config_request_size += t + else: + break + # przywróć chronologicznie + history_msgs.extend(reversed(acc)) + + # aktualny prompt + history_msgs.append({"role": "user", "content": final_prompt}) + logger.info("Rozmiar zapytania (tok): %s", prompt_gpt_request_size) # tokeny już policzone wyżej + else: - history = none_request - logger.info("Rozmiar zapytania po wyslaniu %s", chat_gpt_config_request_size) + # --- tryb NONE: nie dotykamy pamięci i pozwalamy przekazać własny 'none_request' --- + if isinstance(none_request, list): + history_msgs = none_request + elif isinstance(none_request, str) and none_request.strip(): + history_msgs = [{"role": "user", "content": none_request}] + else: + history_msgs = [{"role": "user", "content": f"{username}:{prompt}"}] + + logger.info("Rozmiar zapytania (tok): %s", "n/a") # tokeny już policzone wyżej + try: # ...przygotowanie messages/system prompt/itp. jak masz... # retry/backoff + deadline (zachowuje Twoją semantykę logowania) timeout_sec = 120 deadline = time.time() + timeout_sec response = await asyncio.wait_for( - _openai_call(messages=history, model=algorithm), + openai_call(messages=history_msgs, model=model_to_use), timeout=max(0.1, deadline - time.time()), ) @@ -273,26 +267,33 @@ async def handle_response( response = 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", - ) - response = f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}" + if internal_retry: + resp = "Nie umiem tego teraz ładnie wytłumaczyć — OpenAI mnie zastrzeliło." + else: + 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", + ) + response = 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", - ) - response = f"Sorki, cenzura: {resp}. Jak chcesz to są kanały na nudle #sexy-foteczky i #kanal-do-fapania *Na ekranie pojawia się: {e}" + if internal_retry: + resp = "Nie umiem tego teraz ładnie wytłumaczyć — OpenAI mnie zastrzeliło." + else: + 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", + ) + response = 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 response = 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}" @@ -308,41 +309,28 @@ async def handle_response( response = 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) - temp = {"role": "assistant", "content": response} - history.append(temp) + temp_assistant = {"role": "assistant", "content": response} + logger.info(temp_assistant) 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) + # zapis do pliku MUZYKA + with open(MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING) as fh: + file_data = json.load(fh) + file_data.append({"role": "user", "content": f"{username}:{prompt}"}) + file_data.append(temp_assistant) + fh.seek(0) + json.dump(file_data, fh, indent=4) return response, MESSAGE_TABLE_MUZYKA - 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) + + elif request_type in ("RANDOM", "GENERAL"): + with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as fh: + file_data = json.load(fh) + file_data.append({"role": "user", "content": f"{username}:{prompt}"}) + file_data.append(temp_assistant) + fh.seek(0) + json.dump(file_data, fh, indent=4) return response, MESSAGE_TABLE - elif request_type == "GENERAL": - 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 response, MESSAGE_TABLE - else: + + else: # NONE return response, [] diff --git a/constants.py b/constants.py index f120878..2185187 100644 --- a/constants.py +++ b/constants.py @@ -150,4 +150,6 @@ LATEX_MAX_ZIP_MB = 25 OPENAI_MODEL = "gpt-4o-mini" ALLOWED_ROLES = ["Nocna Zmiana", "Jarl", "Thane", "Bartender"] -GUILD_ID = 664789470779932693 \ No newline at end of file +GUILD_ID = 664789470779932693 +LATEST_MODEL = "gpt-4o" # najnowszy/do rozmów (możesz zmienić w jednym miejscu) +CHEAP_MODEL = "gpt-4o-mini" # najtańszy (fallback do 3.5 niżej)