Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added max count events, changed reload to use token count #341

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/config/config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ def __update_config_values_from_current_state(self):

#Conversation
self.automatic_greeting = self.__definitions.get_bool_value("automatic_greeting")
self.max_count_events = self.__definitions.get_int_value("max_count_events")
self.player_character_description: str = self.__definitions.get_string_value("player_character_description")
self.voice_player_input: bool = self.__definitions.get_bool_value("voice_player_input")
self.player_voice_model: str = self.__definitions.get_string_value("player_voice_model")
Expand Down
7 changes: 7 additions & 0 deletions src/config/definitions/other_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def get_automatic_greeting_config_value() -> ConfigValue:
- If disabled: The LLM will not respond until the player speaks first."""
return ConfigValueBool("automatic_greeting","Automatic Greeting",automatic_greeting_description,True)

@staticmethod
def get_max_count_events_config_value() -> ConfigValue:
max_count_events_description = """Maximum number of in-game events that can are sent to the LLM with one player message.
If the maximum number is reached, the oldest events will be dropped.
Increasing this number will cost more prompt tokens and lead to the context limit being reached faster."""
return ConfigValueInt("max_count_events","Max Count Events",max_count_events_description,5,0,999999)

#Player Character
@staticmethod
def get_player_character_description() -> ConfigValue:
Expand Down
1 change: 1 addition & 0 deletions src/config/mantella_config_value_definitions_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def get_config_values(on_value_change_callback: Callable[..., Any] | None = None
other_category.add_config_value(OtherDefinitions.get_show_http_debug_messages_config_value())
other_category.add_config_value(OtherDefinitions.get_remove_mei_folders_config_value())
other_category.add_config_value(OtherDefinitions.get_automatic_greeting_config_value())
other_category.add_config_value(OtherDefinitions.get_max_count_events_config_value())
other_category.add_config_value(OtherDefinitions.get_player_character_description())
other_category.add_config_value(OtherDefinitions.get_voice_player_input())
other_category.add_config_value(OtherDefinitions.get_player_voice_model())
Expand Down
19 changes: 12 additions & 7 deletions src/conversation/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import logging
from threading import Thread, Lock
import time
from typing import Any, Callable
from typing import Any
from src.llm.openai_client import openai_client
from src.characters_manager import Characters
from src.conversation.conversation_log import conversation_log
from src.conversation.action import action
Expand All @@ -26,8 +27,9 @@ class conversation_continue_type(Enum):

class conversation:
TOKEN_LIMIT_PERCENT: float = 0.6 # TODO: check if this is necessary as it has been removed from the main branch
TOKEN_LIMIT_RELOAD_MESSAGES: float = 0.1
"""Controls the flow of a conversation."""
def __init__(self, context_for_conversation: context, output_manager: ChatManager, rememberer: remembering, is_conversation_too_long: Callable[[message_thread, float], bool], actions: list[action]) -> None:
def __init__(self, context_for_conversation: context, output_manager: ChatManager, rememberer: remembering, openai_client: openai_client, actions: list[action]) -> None:

self.__context: context = context_for_conversation
if not self.__context.npcs_in_conversation.contains_player_character(): # TODO: fix this being set to a radiant conversation because of NPCs in conversation not yet being added
Expand All @@ -37,7 +39,7 @@ def __init__(self, context_for_conversation: context, output_manager: ChatManage
self.__messages: message_thread = message_thread(None)
self.__output_manager: ChatManager = output_manager
self.__rememberer: remembering = rememberer
self.__is_conversation_too_long: Callable[[message_thread, float], bool] = is_conversation_too_long
self.__openai_client = openai_client
self.__has_already_ended: bool = False
self.__sentences: sentence_queue = sentence_queue()
self.__generation_thread: Thread | None = None
Expand Down Expand Up @@ -98,7 +100,7 @@ def continue_conversation(self) -> tuple[str, sentence | None]:
"""
if self.has_already_ended:
return comm_consts.KEY_REPLYTYPE_ENDCONVERSATION, None
if self.__is_conversation_too_long(self.__messages, self.TOKEN_LIMIT_PERCENT):
if self.__openai_client.are_messages_too_long(self.__messages, self.TOKEN_LIMIT_PERCENT):
# Check if conversation too long and if yes initiate intermittent reload
self.__initiate_reload_conversation()

Expand Down Expand Up @@ -194,13 +196,15 @@ def __update_conversation_type(self):
self.__messages: message_thread = message_thread(new_prompt)
else:
self.__conversation_type.adjust_existing_message_thread(self.__messages, self.__context)
self.__messages.reload_message_thread(new_prompt, 8)
self.__messages.reload_message_thread(new_prompt, self.__openai_client.calculate_tokens_from_text, int(self.__openai_client.token_limit * self.TOKEN_LIMIT_RELOAD_MESSAGES))

@utils.time_it
def update_game_events(self, message: user_message) -> user_message:
"""Add in-game events to player's response"""

message.add_event(self.__context.get_context_ingame_events())
all_ingame_events = self.__context.get_context_ingame_events()
max_events = min(len(all_ingame_events) ,self.__context.config.max_count_events)
message.add_event(all_ingame_events[-max_events:])
self.__context.clear_context_ingame_events()

if message.count_ingame_events() > 0:
Expand Down Expand Up @@ -324,7 +328,8 @@ def reload_conversation(self):
self.__save_conversation(is_reload=True)
# Reload
new_prompt = self.__conversation_type.generate_prompt(self.__context)
self.__messages.reload_message_thread(new_prompt, 8)
self.__messages.reload_message_thread(new_prompt, self.__openai_client.calculate_tokens_from_text, int(self.__openai_client.token_limit * self.TOKEN_LIMIT_RELOAD_MESSAGES))


def __has_conversation_ended(self, last_user_text: str) -> bool:
"""Checks if the last player text has ended the conversation
Expand Down
2 changes: 1 addition & 1 deletion src/game_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def start_conversation(self, input_json: dict[str, Any]) -> dict[str, Any]:
world_id = input_json[comm_consts.KEY_STARTCONVERSATION_WORLDID]
world_id = self.WORLD_ID_CLEANSE_REGEX.sub("", world_id)
context_for_conversation = context(world_id, self.__config, self.__client, self.__rememberer, self.__language_info, self.__client.is_text_too_long)
self.__talk = conversation(context_for_conversation, self.__chat_manager, self.__rememberer, self.__client.are_messages_too_long, self.__actions)
self.__talk = conversation(context_for_conversation, self.__chat_manager, self.__rememberer, self.__client, self.__actions)
self.__update_context(input_json)
self.__talk.start_conversation()

Expand Down
16 changes: 13 additions & 3 deletions src/llm/message_thread.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from copy import deepcopy
from typing import Callable
from src.llm.messages import message, system_message, user_message, assistant_message
from openai.types.chat import ChatCompletionMessageParam

Expand Down Expand Up @@ -60,16 +61,25 @@ def add_non_system_messages(self, new_messages: list[message]):
if not isinstance(message, system_message):
self.__messages.append(new_message)

def reload_message_thread(self, new_prompt: str, last_messages_to_keep: int):
def reload_message_thread(self, new_prompt: str, text_measurer: Callable[[str], int], max_tokens: int):
"""Reloads this message_thread with a new system_message prompt and drops all but the last X messages

Args:
new_prompt (str): the new prompt for the system_message
last_messages_to_keep (int): how many of the last messages to keep
"""
result = []
result: list[message] = []
result.append(system_message(new_prompt))
result.extend(self.get_talk_only()[-last_messages_to_keep:])
messages_to_keep: list[message] = []
used_tokens = 0
for talk_message in reversed(self.get_talk_only()):
used_tokens += text_measurer(talk_message.get_formatted_content())
if used_tokens < max_tokens:
messages_to_keep.append(talk_message)
else:
break
messages_to_keep.reverse()
result.extend(messages_to_keep)
self.__messages = result

def get_talk_only(self, include_system_generated_messages: bool = False) -> list[message]:
Expand Down