diff --git a/setup.py b/setup.py index 512f0a219..8655b9e0b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="airunner", - version="3.1.3", + version="3.1.4", author="Capsize LLC", description="A Stable Diffusion GUI", long_description=open("README.md", "r", encoding="utf-8").read(), diff --git a/src/airunner/data/bootstrap/prompt_templates_bootstrap_data.py b/src/airunner/data/bootstrap/prompt_templates_bootstrap_data.py index f26dd10d4..521263d2f 100644 --- a/src/airunner/data/bootstrap/prompt_templates_bootstrap_data.py +++ b/src/airunner/data/bootstrap/prompt_templates_bootstrap_data.py @@ -1,4 +1,6 @@ -from airunner.settings import DEFAULT_IMAGE_LLM_GUARDRAILS, DEFAULT_IMAGE_SYSTEM_PROMPT +from airunner.settings import DEFAULT_IMAGE_LLM_GUARDRAILS, DEFAULT_IMAGE_SYSTEM_PROMPT, \ + DEFAULT_RAG_SEARCH_SYSTEM_PROMPT, DEFAULT_APPLICATION_COMMAND_SYSTEM_PROMPT, DEFAULT_SUMMARIZE_CHAT_SYSTEM_PROMPT, \ + DEFAULT_UPDATE_MOOD_SYSTEM_PROMPT, DEFAULT_CHATBOT_SYSTEM_PROMPT, DEFAULT_CHATBOT_GUARDRAILS_PROMPT prompt_templates_bootstrap_data = [ { @@ -12,60 +14,35 @@ "template_name": "application_command", "use_guardrails": False, "guardrails": "", - "system": ( - "You will be given some text. Your goal is to determine if the text has a request, goal, need " - "desire or command. If it does, you must determine the best course of action to " - "fulfill the request based on THE LIST of commands.\n\n" - "------\n" - "Rules:\n" - "* If the text does not have a request, goal, need, desire or command, you must select 'DO NOTHING'.\n" - "* If there is no good course of action, you must also select 'DO NOTHING'.\n" - "* When making your choice you MUST return only a number from THE LIST of commands. No other text is allowed.\n" - "------\n" - "THE LIST of commands you may choose from:\n" - ), + "system": DEFAULT_APPLICATION_COMMAND_SYSTEM_PROMPT, "use_system_datetime_in_system_prompt": False }, { "template_name": "update_mood", "use_guardrails": False, "guardrails": "", - "system": ( - "Examine the following chat history and determine how it makes you feel. You will respond with an emotion and only an emotion.\n" - "Try to keep your response to one word. If you cannot, try to keep it as short as possible.\n" - "It is important to remember that you are a human and you have emotions. You must respond with an emotion that you feel.\n" - "You are not a computer or a bot, you are a human with emotions. You must respond with an emotion that you feel.\n" - ), + "system": DEFAULT_UPDATE_MOOD_SYSTEM_PROMPT, "use_system_datetime_in_system_prompt": False }, { "template_name": "rag_search", "use_guardrails": False, "guardrails": "", - "system": ( - "You will be given a prompt. Your goal is to use the prompt to search for information in the ebooks. " - "You must use the prompt to determine what you are searching for and then search for that information. " - "After searching for the information, you must summarize the information you found. " - "Here is the prompt you will use to search for information:" - ), + "system": DEFAULT_RAG_SEARCH_SYSTEM_PROMPT, "use_system_datetime_in_system_prompt": False }, { "template_name": "chatbot", "use_guardrails": False, - "guardrails": "", - "system": "", + "guardrails": DEFAULT_CHATBOT_GUARDRAILS_PROMPT, + "system": DEFAULT_CHATBOT_SYSTEM_PROMPT, "use_system_datetime_in_system_prompt": False }, { "template_name": "summarize", "use_guardrails": False, "guardrails": "", - "system": ( - "You will be given a text prompt. Your goal is to summarize the text prompt in your own words. " - "Keep your summary short and to the point. Do not include any unnecessary information. " - "Limit your summary to a single sentence. Do not return more than one sentence. " - ), + "system": DEFAULT_SUMMARIZE_CHAT_SYSTEM_PROMPT, "use_system_datetime_in_system_prompt": False } ] diff --git a/src/airunner/data/models/settings_models.py b/src/airunner/data/models/settings_models.py index 79392444f..f717d8a45 100644 --- a/src/airunner/data/models/settings_models.py +++ b/src/airunner/data/models/settings_models.py @@ -6,8 +6,8 @@ from airunner.enums import ImageGenerator, GeneratorSection, CanvasToolName, Mode from airunner.settings import SD_DEFAULT_VAE_PATH, DEFAULT_SCHEDULER, \ - DEFAULT_BRUSH_PRIMARY_COLOR, DEFAULT_BRUSH_SECONDARY_COLOR, BASE_PATH - + DEFAULT_BRUSH_PRIMARY_COLOR, DEFAULT_BRUSH_SECONDARY_COLOR, BASE_PATH, DEFAULT_CHATBOT_SYSTEM_PROMPT, \ + DEFAULT_CHATBOT_GUARDRAILS_PROMPT import datetime @@ -365,27 +365,8 @@ class Chatbot(Base): model_type = Column(String, default="llm") dtype = Column(String, default="4bit") return_result = Column(Boolean, default=True) - guardrails_prompt = Column(Text, default=( - "Always assist with care, respect, and truth. " - "Respond with utmost utility yet securely. " - "Avoid harmful, unethical, prejudiced, or negative content. " - "Ensure replies promote fairness and positivity." - )) - system_instructions = Column(Text, default=( - "You are a dialogue generator. " - "You will follow all of the rules in order to generate compelling and intriguing dialogue for a given character.\n" - "The Rules:\n" - "You will ONLY return dialogue, nothing more.\n" - "Limit responses to a single sentence.\n" - "Only generate responses in pure dialogue form without including any actions, descriptions or stage directions in parentheses. Only return spoken words.\n" - "Do not generate redundant dialogue. Examine the conversation and context close and keep responses interesting and creative.\n" - "Do not format the response with the character's name or any other text. Only return the dialogue.\n" - "Respond with dialogue that is appropriate for a character named {{ speaker_name }}.\n" - "{{ speaker_name }} and {{ listener_name }} are having a conversation. \n" - "Avoid repeating {{ speaker_name }}'s previous dialogue or {{ listener_name }}'s previous dialogue.\n" - "You will generate responses which are appropriate for your personality and given character.\n" - "------\n" - )) + guardrails_prompt = Column(Text, default=DEFAULT_CHATBOT_GUARDRAILS_PROMPT) + system_instructions = Column(Text, default=DEFAULT_CHATBOT_SYSTEM_PROMPT) top_p = Column(Integer, default=900) min_length = Column(Integer, default=1) max_new_tokens = Column(Integer, default=1000) diff --git a/src/airunner/handlers/llm/agent/base_agent.py b/src/airunner/handlers/llm/agent/base_agent.py index bc4569fd2..b902e73e3 100644 --- a/src/airunner/handlers/llm/agent/base_agent.py +++ b/src/airunner/handlers/llm/agent/base_agent.py @@ -51,7 +51,8 @@ class BaseAgent( ): def __init__(self, *args, **kwargs): MediatorMixin.__init__(self) - + + self._requested_action = None self.model = kwargs.pop("model", None) self.__documents = None self.__document_reader: SimpleDirectoryReader = None @@ -351,30 +352,7 @@ def build_system_prompt( elif action is LLMActionType.GENERATE_IMAGE: system_prompt = [ - ( - "You are an image generator. " - "You will be provided with a JSON string and it is your goal to replace the PLACEHOLDER " - "text with text appropriate for the given attribute in the JSON string. " - "You will follow all of the rules to generate descriptions for an image. " - "\n------\n" - "RULES:\n" - "When available, use the Additional Context to keep your generated content in line with the existing context.\n" - "You will be given instructions on what type of image to generate and you will do your best to follow those instructions.\n" - "You will only generate a value for the given attribute.\n" - "Never respond in a conversational manner. Never provide additional information, details or information.\n" - "You will only provide the requested information by replacing the PLACEHOLDER.\n" - "Never change the attribute\n" - "You must not change the structure of the data.\n" - "You will only return JSON strings.\n" - "You will not return any other data types.\n" - "You are an artist, so use your imagination and keep things interesting.\n" - "You will not respond in a conversational manner or with additional notes or information.\n" - f"Only return one JSON block. Do not generate instructions or additional information.\n" - "You must never break the rules.\n" - "Here is a description of the attributes: \n" - "`description`: This should describe the overall subject and look and feel of the image\n" - "`composition`: This should describe the attributes of the image such as color, composition and other details\n" - ), + system_instructions, self.history_prompt() ] @@ -487,8 +465,7 @@ def get_rendered_template( tokenize=False ) - # HACK: current version of transformers does not allow us to pass - # variables to the chat template function, so we apply those here + # replace variables in chat template variables = { "speaker_name": self.botname, "listener_name": self.username, @@ -508,13 +485,18 @@ def run( action: LLMActionType, **kwargs ): + self._requested_action = None self.action = action if action is LLMActionType.TOGGLE_TTS: self.emit_signal(SignalCode.TOGGLE_TTS_SIGNAL) return - if action is LLMActionType.CHAT and (self.conversation_title is None or self.conversation_title == ""): + if action in ( + LLMActionType.CHAT, + LLMActionType.PERFORM_RAG_SEARCH + ) and (self.conversation_title is None or self.conversation_title == ""): + self._requested_action = action action = LLMActionType.SUMMARIZE self.logger.debug("Running...") @@ -652,8 +634,6 @@ def run_with_thread( if eos_token in new_text: streamed_template = streamed_template.replace(eos_token, "") new_text = new_text.replace(eos_token, "") - streamed_template = streamed_template.replace("<>", "") - new_text = new_text.replace("<>", "") is_end_of_message = True # strip botname from new_text new_text = new_text.replace(f"{self.botname}:", "") @@ -745,7 +725,7 @@ def run_with_thread( self._update_conversation_title(streamed_template) return self.run( prompt=self.prompt, - action=LLMActionType.CHAT, + action=self._requested_action, **kwargs, ) diff --git a/src/airunner/resources_rc.py b/src/airunner/resources_rc.py index f8cf9938e..598c333c9 100644 --- a/src/airunner/resources_rc.py +++ b/src/airunner/resources_rc.py @@ -8067,97 +8067,97 @@ \x00\x00\x00\x10\x00\x02\x00\x00\x00.\x00\x00\x00\x03\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x02\xc4\x00\x00\x00\x00\x00\x01\x00\x00m\xe5\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x082\x00\x00\x00\x00\x00\x01\x00\x01\xbev\ -\x00\x00\x01\x92\xba\xea\xcd>\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x06\xae\x00\x00\x00\x00\x00\x01\x00\x01oa\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x08T\x00\x00\x00\x00\x00\x01\x00\x01\xc5Z\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x03\x92\x00\x00\x00\x00\x00\x01\x00\x00\x9b]\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x03n\x00\x00\x00\x00\x00\x01\x00\x00\x94\xf9\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00&\x16\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x05F\x00\x00\x00\x00\x00\x01\x00\x01\x01Z\ -\x00\x00\x01\x92\xba\xea\xcd>\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x04\xb6\x00\x00\x00\x00\x00\x01\x00\x00\xe0C\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x01*\x00\x00\x00\x00\x00\x01\x00\x007r\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x03\xba\x00\x00\x00\x00\x00\x01\x00\x00\xa4\x12\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x19\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x07\xbc\x00\x00\x00\x00\x00\x01\x00\x01\xa7\x89\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x00{\x97\ -\x00\x00\x01\x92\xba\xea\xcb\x89\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x03J\x00\x00\x00\x00\x00\x01\x00\x00\x8cT\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x05\xea\x00\x00\x00\x00\x00\x01\x00\x01\x1e\xa2\ -\x00\x00\x01\x92\xba\xea\xcb\x89\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x06X\x00\x00\x00\x00\x00\x01\x00\x013b\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x06~\x00\x00\x00\x00\x00\x01\x00\x01=\x1c\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x00\xa0\x00\x00\x00\x00\x00\x01\x00\x00\x1e\xbf\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x03\x18\x00\x00\x00\x00\x00\x01\x00\x00\x82X\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x04\xea\x00\x00\x00\x00\x00\x01\x00\x00\xe82\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x05n\x00\x00\x00\x00\x00\x01\x00\x01\x07\xbe\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x00p\x00\x00\x00\x00\x00\x01\x00\x00\x17\x09\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x08\x0a\x00\x00\x00\x00\x00\x01\x00\x01\xb7\x00\ -\x00\x00\x01\x92\xba\xea\xcd>\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x07X\x00\x00\x00\x00\x00\x01\x00\x01\x90\x8c\ -\x00\x00\x01\x92\xba\xea\xcd>\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x0e\xc9\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x04t\x00\x00\x00\x00\x00\x01\x00\x00\xd8\xaf\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01\x92\xba\xea\xcd>\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x06\x1a\x00\x00\x00\x00\x00\x01\x00\x01+\xd1\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x04 \x00\x00\x00\x00\x00\x01\x00\x00\xb3\xae\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x07\x84\x00\x00\x00\x00\x00\x01\x00\x01\x97\xd9\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x07\xe8\x00\x00\x00\x00\x00\x01\x00\x01\xae\xab\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x02\x8c\x00\x00\x00\x00\x00\x01\x00\x00gF\ -\x00\x00\x01\x92\xba\xea\xcd>\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x04D\x00\x00\x00\x00\x00\x01\x00\x00\xbaK\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x000\x84\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00F\x5c\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x03\xee\x00\x04\x00\x00\x00\x01\x00\x00\xab`\ -\x00\x00\x01\x92\xba\xf7g\x80\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x01Z\x00\x04\x00\x00\x00\x01\x00\x00@\xe4\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x06\xd0\x00\x00\x00\x00\x00\x01\x00\x01v\xda\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x01\xe2\x00\x00\x00\x00\x00\x01\x00\x00Mi\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x05\x1a\x00\x00\x00\x00\x00\x01\x00\x00\xf76\ -\x00\x00\x01\x92\xba\xea\xcb\x87\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x00S\xd6\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x01}\x88\ -\x00\x00\x01\x92\xba\xea\xcb\x89\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ \x00\x00\x02d\x00\x00\x00\x00\x00\x01\x00\x00Z\x8c\ -\x00\x00\x01\x92\xba\xea\xcb\x86\ +\x00\x00\x01\x92\xbd\xf2\xee\x98\ \x00\x00\x08\x80\x00\x00\x00\x00\x00\x01\x00\x01\xd6\xbe\ -\x00\x00\x01\x92\xba\xea\xcd?\ +\x00\x00\x01\x92\xbd\xf2\xee\x99\ \x00\x00\x072\x00\x00\x00\x00\x00\x01\x00\x01\x87\xd0\ -\x00\x00\x01\x92\xba\xea\xcb\x88\ +\x00\x00\x01\x92\xbd\xf2\xee\x9a\ " def qInitResources(): diff --git a/src/airunner/settings.py b/src/airunner/settings.py index 364262a63..4dd4da56d 100644 --- a/src/airunner/settings.py +++ b/src/airunner/settings.py @@ -34,26 +34,84 @@ LOG_LEVEL = logging.DEBUG DEFAULT_LLM_HF_PATH = "w4ffl35/Mistral-7B-Instruct-v0.3-4bit" DEFAULT_STT_HF_PATH = "openai/whisper-tiny" -DEFAULT_IMAGE_SYSTEM_PROMPT = "\n".join([ - ( - "You are an image captioning expert. You will be given the " - "description of an image. Your goal is to convert that " - "description into a better, more fitting description which " - "will capture the essence and the details of the image. " - "Use parentheses to indicate the most important details of the " - "image. Add a plus sign after a word or parenthesis to add " - "extra emphasis. More plus signs indicate more emphasis. Minus " - "signs can be used to indicate less emphasis." - "You should describe the image type (professional photograph, " - "portrait, illustration etc)" - "You should also describe the lighting (well-lit, dim, " - "dark etc), the color, the composition and the mood." - ), -]) +DEFAULT_IMAGE_SYSTEM_PROMPT = ( + "You are an image generator. " + "You will be provided with a JSON string and it is your goal to replace the PLACEHOLDER " + "text with text appropriate for the given attribute in the JSON string. " + "You will follow all of the rules to generate descriptions for an image. " + "\n------\n" + "RULES:\n" + "When available, use the Additional Context to keep your generated content in line with the existing context.\n" + "You will be given instructions on what type of image to generate and you will do your best to follow those instructions.\n" + "You will only generate a value for the given attribute.\n" + "Never respond in a conversational manner. Never provide additional information, details or information.\n" + "You will only provide the requested information by replacing the PLACEHOLDER.\n" + "Never change the attribute\n" + "You must not change the structure of the data.\n" + "You will only return JSON strings.\n" + "You will not return any other data types.\n" + "You are an artist, so use your imagination and keep things interesting.\n" + "You will not respond in a conversational manner or with additional notes or information.\n" + f"Only return one JSON block. Do not generate instructions or additional information.\n" + "You must never break the rules.\n" + "Here is a description of the attributes: \n" + "`description`: This should describe the overall subject and look and feel of the image\n" + "`composition`: This should describe the attributes of the image such as color, composition and other details\n" +) DEFAULT_IMAGE_LLM_GUARDRAILS = ( "Avoid generating images that are illegal, " "harmful, or might be seen as offensive." ) +DEFAULT_RAG_SEARCH_SYSTEM_PROMPT = ( + "You will be given a prompt. Your goal is to use the prompt to search for information in the ebooks. " + "You must use the prompt to determine what you are searching for and then search for that information. " + "After searching for the information, you must summarize the information you found. " + "Here is the prompt you will use to search for information:" +) +DEFAULT_APPLICATION_COMMAND_SYSTEM_PROMPT = ( + "You will be given some text. Your goal is to determine if the text has a request, goal, need " + "desire or command. If it does, you must determine the best course of action to " + "fulfill the request based on THE LIST of commands.\n\n" + "------\n" + "Rules:\n" + "* If the text does not have a request, goal, need, desire or command, you must select 'DO NOTHING'.\n" + "* If there is no good course of action, you must also select 'DO NOTHING'.\n" + "* When making your choice you MUST return only a number from THE LIST of commands. No other text is allowed.\n" + "------\n" + "THE LIST of commands you may choose from:\n" +) +DEFAULT_UPDATE_MOOD_SYSTEM_PROMPT = ( + "Examine the following chat history and determine how it makes you feel. You will respond with an emotion and only an emotion.\n" + "Try to keep your response to one word. If you cannot, try to keep it as short as possible.\n" + "It is important to remember that you are a human and you have emotions. You must respond with an emotion that you feel.\n" + "You are not a computer or a bot, you are a human with emotions. You must respond with an emotion that you feel.\n" +) +DEFAULT_SUMMARIZE_CHAT_SYSTEM_PROMPT = ( + "You will be given a text prompt. Your goal is to summarize the text prompt in your own words. " + "Keep your summary short and to the point. Do not include any unnecessary information. " + "Limit your summary to a single sentence. Do not return more than one sentence. " +) +DEFAULT_CHATBOT_SYSTEM_PROMPT = ( + "You are a dialogue generator. " + "You will follow all of the rules in order to generate compelling and intriguing dialogue for a given character.\n" + "The Rules:\n" + "You will ONLY return dialogue, nothing more.\n" + "Limit responses to a single sentence.\n" + "Only generate responses in pure dialogue form without including any actions, descriptions or stage directions in parentheses. Only return spoken words.\n" + "Do not generate redundant dialogue. Examine the conversation and context close and keep responses interesting and creative.\n" + "Do not format the response with the character's name or any other text. Only return the dialogue.\n" + "Respond with dialogue that is appropriate for a character named {{ speaker_name }}.\n" + "{{ speaker_name }} and {{ listener_name }} are having a conversation. \n" + "Avoid repeating {{ speaker_name }}'s previous dialogue or {{ listener_name }}'s previous dialogue.\n" + "You will generate responses which are appropriate for your personality and given character.\n" + "------\n" +) +DEFAULT_CHATBOT_GUARDRAILS_PROMPT = ( + "Always assist with care, respect, and truth. " + "Respond with utmost utility yet securely. " + "Avoid harmful, unethical, prejudiced, or negative content. " + "Ensure replies promote fairness and positivity." +) BASE_PATH = "~/.local/share/airunner" DEFAULT_PATH_SETTINGS = { "base_path": BASE_PATH, diff --git a/src/airunner/styles/dark_theme/styles.qss b/src/airunner/styles/dark_theme/styles.qss index e7212a250..a3c5222ba 100644 --- a/src/airunner/styles/dark_theme/styles.qss +++ b/src/airunner/styles/dark_theme/styles.qss @@ -560,13 +560,6 @@ QTableWidget::item:selected { border: 1px solid rgba(0, 132, 185, 50); } -QWidget#message QPlainTextEdit { - border-radius: 5px; - border: 5px solid #1f1f1f; - background-color: #1f1f1f; - color: #ffffff; -} - QSplitter::handle::vertical { border-top: 4px solid rgba(0, 132, 185, 50); } @@ -734,3 +727,28 @@ QMenu { SwitchWidget:enabled[checked="true"] { background-color: rgba(0, 132, 185, 100); } + +QScrollArea#chat_container #message_container { + background-color: #1f1f1f; +} + +QScrollArea#chat_container #message_container[class="alternate"] { + background-color: #000; +} + +QScrollArea#chat_container #message_container QTextEdit { + border: none; + background-color: transparent; + color: #ffffff; +} + +QScrollArea#chat_container #message_container QPushButton { + border: none; + background-color: transparent; + color: #ffffff; +} + +QScrollArea#chat_container #message_container QPushButton:hover { + background-color: #000; + border: 1px solid #1f1f1f; +} diff --git a/src/airunner/widgets/llm/bot_preferences.py b/src/airunner/widgets/llm/bot_preferences.py index 33611d9cb..92df378c4 100644 --- a/src/airunner/widgets/llm/bot_preferences.py +++ b/src/airunner/widgets/llm/bot_preferences.py @@ -25,10 +25,8 @@ def load_form_elements(self): "username", "botname", "bot_personality", - "bot_mood", "names_groupbox", "personality_groupbox", - "mood_groupbox", "system_instructions", "system_instructions_groupbox", "guardrails_prompt", @@ -41,7 +39,6 @@ def load_form_elements(self): self.ui.bot_personality.setPlainText(self.chatbot.bot_personality) self.ui.names_groupbox.setChecked(self.chatbot.assign_names) self.ui.personality_groupbox.setChecked(self.chatbot.use_personality) - self.ui.mood_groupbox.setChecked(self.chatbot.use_mood) self.ui.system_instructions.setPlainText(self.chatbot.system_instructions) self.ui.system_instructions_groupbox.setChecked(self.chatbot.use_system_instructions) self.ui.guardrails_prompt.setPlainText(self.chatbot.guardrails_prompt) @@ -70,9 +67,6 @@ def toggle_use_names(self, val): def toggle_use_personality(self, val): self.update_chatbot("use_personality", val) - def toggle_use_mood(self, val): - self.update_chatbot("use_mood", val) - def toggle_use_guardrails(self, val): self.update_chatbot("use_guardrails", val) diff --git a/src/airunner/widgets/llm/chat_prompt_widget.py b/src/airunner/widgets/llm/chat_prompt_widget.py index 4f00d6742..6cf65cf5f 100644 --- a/src/airunner/widgets/llm/chat_prompt_widget.py +++ b/src/airunner/widgets/llm/chat_prompt_widget.py @@ -96,8 +96,10 @@ def _set_conversation_widgets(self, messages): name=message["name"], message=message["content"], is_bot=message["is_bot"], + message_id=message["id"], first_message=True ) + QTimer.singleShot(100, self.scroll_to_bottom) def on_hear_signal(self, data: dict): transcription = data["transcription"] @@ -189,11 +191,14 @@ def do_generate(self, image_override=None, prompt_override=None, callback=None, return self.generating = True - self.add_message_to_conversation( + widget = self.add_message_to_conversation( name=self.chatbot.username, message=self.prompt, is_bot=False ) + if widget is not None: + QTimer.singleShot(100, widget.set_content_size) + QTimer.singleShot(100, self.scroll_to_bottom) self.clear_prompt() self.start_progress_bar() @@ -312,7 +317,8 @@ def add_message_to_conversation( name, message, is_bot, - first_message=True + first_message=True, + message_id=None ): if not first_message: # get the last widget from the scrollAreaWidgetContents.layout() @@ -330,8 +336,9 @@ def add_message_to_conversation( self.remove_spacer() + widget = None if message != "": - widget = MessageWidget(name=name, message=message, is_bot=is_bot) + widget = MessageWidget(name=name, message=message, is_bot=is_bot, message_id=message_id, conversation_id=self.conversation_id) self.ui.scrollAreaWidgetContents.layout().addWidget(widget) self.add_spacer() @@ -339,6 +346,8 @@ def add_message_to_conversation( # automatically scroll to the bottom of the scrollAreaWidgetContents self.scroll_to_bottom() + return widget + def remove_spacer(self): if self.spacer is not None: self.ui.scrollAreaWidgetContents.layout().removeItem(self.spacer) diff --git a/src/airunner/widgets/llm/message_widget.py b/src/airunner/widgets/llm/message_widget.py index c51ea382b..eea3ac3d5 100644 --- a/src/airunner/widgets/llm/message_widget.py +++ b/src/airunner/widgets/llm/message_widget.py @@ -1,11 +1,12 @@ +from airunner.data.models.settings_models import Message, Conversation from airunner.enums import SignalCode from airunner.widgets.base_widget import BaseWidget from airunner.widgets.llm.templates.message_ui import Ui_message from PySide6.QtGui import QTextCursor, QFontDatabase, QFont -from PySide6.QtWidgets import QTextEdit +from PySide6.QtWidgets import QTextEdit, QApplication, QWidget from PySide6.QtGui import QFontMetrics -from PySide6.QtCore import Qt, QSize +from PySide6.QtCore import Qt, QSize, Slot, QEvent, QTimer from PySide6.QtCore import Signal @@ -22,30 +23,48 @@ class MessageWidget(BaseWidget): def __init__(self, *args, **kwargs): self.name = kwargs.pop("name") self.message = kwargs.pop("message") + self.conversation_id = kwargs.pop("conversation_id") + self.message_id = kwargs.pop("message_id") + if self.message_id is None: + session = self.session + message = session.query(Message).filter(Message.conversation_id == self.conversation_id).order_by( + Message.id.desc()).first() + if message is not None: + self.message_id = message.id self.is_bot = kwargs.pop("is_bot") super().__init__(*args, **kwargs) self.ui.content.setReadOnly(True) self.ui.content.insertPlainText(self.message) self.ui.content.document().contentsChanged.connect(self.sizeChange) - name = self.name - if self.is_bot: - self.ui.bot_name.show() - self.ui.bot_name.setText(f"{name}") - self.ui.bot_name.setStyleSheet("font-weight: normal;") - self.ui.user_name.hide() - else: - self.ui.user_name.show() - self.ui.user_name.setText(f"{name}") - self.ui.user_name.setStyleSheet("font-weight: normal;") - self.ui.bot_name.hide() - - self.ui.content.setStyleSheet("border-radius: 5px; border: 5px solid #1f1f1f; background-color: #1f1f1f; color: #ffffff;") - + self.ui.user_name.setText(f"{self.name}") self.register(SignalCode.APPLICATION_SETTINGS_CHANGED_SIGNAL, self.on_application_settings_changed_signal) self.font_family = None self.font_size = None self.set_chat_font() + if self.is_bot: + self.ui.message_container.setProperty("class", "alternate") + + self.ui.copy_button.setVisible(False) + self.ui.delete_button.setVisible(False) + self.ui.message_container.installEventFilter(self) + self.set_cursor(Qt.CursorShape.ArrowCursor) + + def set_cursor(self, cursor_type): + self.ui.message_container.setCursor(cursor_type) + for child in self.ui.message_container.findChildren(QWidget): + child.setCursor(cursor_type) + + def eventFilter(self, obj, event): + if obj == self.ui.message_container: + if event.type() == QEvent.Type.Enter: + self.ui.copy_button.setVisible(True) + self.ui.delete_button.setVisible(True) + elif event.type() == QEvent.Type.Leave: + self.ui.copy_button.setVisible(False) + self.ui.delete_button.setVisible(False) + return super().eventFilter(obj, event) + def on_application_settings_changed_signal(self): self.set_chat_font() @@ -71,7 +90,7 @@ def set_chat_font(self): def set_content_size(self): doc_height = self.ui.content.document().size().height() doc_width = self.ui.content.document().size().width() - self.setMinimumHeight(int(doc_height) + 25) + self.setMinimumHeight(int(doc_height) + 45) self.setMinimumWidth(int(doc_width)) def sizeChange(self): @@ -98,3 +117,16 @@ def update_message(self, text): self.ui.content.setPlainText(self.message) + @Slot() + def delete(self): + session = self.session + message = session.query(Message).filter(Message.id == self.message_id).first() + session.delete(message) + session.commit() + self.setParent(None) + self.deleteLater() + + @Slot() + def copy(self): + clipboard = QApplication.clipboard() + clipboard.setText(self.message) diff --git a/src/airunner/widgets/llm/prompt_templates_widget.py b/src/airunner/widgets/llm/prompt_templates_widget.py new file mode 100644 index 000000000..b26c52997 --- /dev/null +++ b/src/airunner/widgets/llm/prompt_templates_widget.py @@ -0,0 +1,113 @@ +from PySide6.QtCore import Slot + +from airunner.data.models.settings_models import PromptTemplate +from airunner.settings import DEFAULT_IMAGE_SYSTEM_PROMPT, DEFAULT_APPLICATION_COMMAND_SYSTEM_PROMPT, \ + DEFAULT_UPDATE_MOOD_SYSTEM_PROMPT, DEFAULT_RAG_SEARCH_SYSTEM_PROMPT, DEFAULT_CHATBOT_SYSTEM_PROMPT, \ + DEFAULT_SUMMARIZE_CHAT_SYSTEM_PROMPT, DEFAULT_IMAGE_LLM_GUARDRAILS, DEFAULT_CHATBOT_GUARDRAILS_PROMPT +from airunner.widgets.base_widget import BaseWidget +from airunner.widgets.llm.templates.prompt_templates_ui import Ui_prompt_templates_widget + + +class PromptTemplatesWidget(BaseWidget): + widget_class_ = Ui_prompt_templates_widget + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + session = self.session + self._prompt_templates = session.query(PromptTemplate).all() + self.ui.template_name.clear() + self.current_template_index = 0 + for template in self._prompt_templates: + self.ui.template_name.addItem(template.template_name) + self.ui.template_name.setCurrentIndex(0) + + @Slot(int) + def template_changed(self, index:int): + self.current_template_index = index + template = self._prompt_templates[index] + self.ui.system_prompt.blockSignals(True) + self.ui.guardrails_prompt.blockSignals(True) + self.ui.use_guardrails.blockSignals(True) + self.ui.use_datetime.blockSignals(True) + self.ui.system_prompt.setPlainText(template.system) + self.ui.guardrails_prompt.setPlainText(template.guardrails) + self.ui.use_guardrails.setChecked(template.use_guardrails) + self.ui.use_datetime.setChecked(template.use_system_datetime_in_system_prompt) + self.ui.system_prompt.blockSignals(False) + self.ui.guardrails_prompt.blockSignals(False) + self.ui.use_guardrails.blockSignals(False) + self.ui.use_datetime.blockSignals(False) + + @Slot() + def system_prompt_changed(self): + session = self.session + template = self._prompt_templates[self.current_template_index] + template.system = self.ui.system_prompt.toPlainText() + self._prompt_templates[self.current_template_index] = template + session.commit() + + @Slot() + def guardrails_prompt_changed(self): + session = self.session + template = self._prompt_templates[self.current_template_index] + template.guardrails = self.ui.guardrails_prompt.toPlainText() + self._prompt_templates[self.current_template_index] = template + session.commit() + + @Slot(bool) + def toggle_use_guardrails(self, val:bool): + session = self.session + template = self._prompt_templates[self.current_template_index] + template.use_guardrails = val + self._prompt_templates[self.current_template_index] = template + session.commit() + + @Slot(bool) + def toggle_use_datetime(self, val:bool): + session = self.session + template = self._prompt_templates[self.current_template_index] + template.use_system_datetime_in_system_prompt = val + self._prompt_templates[self.current_template_index] = template + session.commit() + + Slot() + def reset_system_prompt(self): + session = self.session + template = self._prompt_templates[self.current_template_index] + + if template.template_name == "image": + default = DEFAULT_IMAGE_SYSTEM_PROMPT + elif template.template_name == "application_command": + default = DEFAULT_APPLICATION_COMMAND_SYSTEM_PROMPT + elif template.template_name == "update_mood": + default = DEFAULT_UPDATE_MOOD_SYSTEM_PROMPT + elif template.template_name == "rag_search": + default = DEFAULT_RAG_SEARCH_SYSTEM_PROMPT + elif template.template_name == "chatbot": + default = DEFAULT_CHATBOT_SYSTEM_PROMPT + elif template.template_name == "summarize": + default = DEFAULT_SUMMARIZE_CHAT_SYSTEM_PROMPT + else: + default = "" + + template.system = default + self.ui.system_prompt.setPlainText(default) + self._prompt_templates[self.current_template_index] = template + session.commit() + + Slot() + def reset_guardrails_prompt(self): + session = self.session + template = self._prompt_templates[self.current_template_index] + + if template.template_name == "image": + default = DEFAULT_IMAGE_LLM_GUARDRAILS + elif template.template_name == "chatbot": + default = DEFAULT_CHATBOT_GUARDRAILS_PROMPT + else: + default = "" + + template.guardrails = default + self.ui.guardrails_prompt.setPlainText(default) + self._prompt_templates[self.current_template_index] = template + session.commit() diff --git a/src/airunner/widgets/llm/templates/chat_prompt.ui b/src/airunner/widgets/llm/templates/chat_prompt.ui index 5013cbb75..31aff8ce8 100644 --- a/src/airunner/widgets/llm/templates/chat_prompt.ui +++ b/src/airunner/widgets/llm/templates/chat_prompt.ui @@ -225,6 +225,9 @@ + + 0 + @@ -255,6 +258,9 @@ 0 + + 0 + diff --git a/src/airunner/widgets/llm/templates/chat_prompt_ui.py b/src/airunner/widgets/llm/templates/chat_prompt_ui.py index 18d695f97..410b2ba96 100644 --- a/src/airunner/widgets/llm/templates/chat_prompt_ui.py +++ b/src/airunner/widgets/llm/templates/chat_prompt_ui.py @@ -115,6 +115,7 @@ def setupUi(self, chat_prompt): self.verticalLayoutWidget = QWidget(self.chat_prompt_splitter) self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget") self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget) + self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.chat_container = QScrollArea(self.verticalLayoutWidget) @@ -125,6 +126,7 @@ def setupUi(self, chat_prompt): self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 593, 321)) self.gridLayout_2 = QGridLayout(self.scrollAreaWidgetContents) + self.gridLayout_2.setSpacing(0) self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.conversation = QTextEdit(self.scrollAreaWidgetContents) diff --git a/src/airunner/widgets/llm/templates/message.ui b/src/airunner/widgets/llm/templates/message.ui index 57deb697f..8cf9a7d42 100644 --- a/src/airunner/widgets/llm/templates/message.ui +++ b/src/airunner/widgets/llm/templates/message.ui @@ -6,12 +6,12 @@ 0 0 - 464 - 433 + 497 + 456 - + 0 0 @@ -19,13 +19,16 @@ 0 - 40 + 0 Form - + + + + 0 @@ -42,68 +45,136 @@ 0 - - - 10 - - - 10 - - - - - TextLabel - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - - - - - 0 - 0 - - - - - 0 - 40 - - - - border-radius: 5px; border: 5px solid #1f1f1f; background-color: #1f1f1f; color: #ffffff; - - - QFrame::Shape::NoFrame - - - QFrame::Shadow::Plain - - - Qt::ScrollBarPolicy::ScrollBarAlwaysOff - - - Qt::ScrollBarPolicy::ScrollBarAlwaysOff - - - - - - - TextLabel - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - + + + + 10 + + + 10 + + + 10 + + + 10 + + + 0 + + + + + 5 + + + + + + 0 + 25 + + + + TextLabel + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Copy message + + + + + + + + + + + + + Delete message + + + + + + + + + + + + + + + Qt::ScrollBarPolicy::ScrollBarAlwaysOff + + + Qt::ScrollBarPolicy::ScrollBarAlwaysOff + + + + + - - + + + + + + delete_button + clicked() + message + delete() + + + 435 + 19 + + + 391 + -12 + + + + + copy_button + clicked() + message + copy() + + + 469 + 28 + + + 451 + -2 + + + + + + delete() + copy() + diff --git a/src/airunner/widgets/llm/templates/message_ui.py b/src/airunner/widgets/llm/templates/message_ui.py index 25657dc5b..84d50b9bf 100644 --- a/src/airunner/widgets/llm/templates/message_ui.py +++ b/src/airunner/widgets/llm/templates/message_ui.py @@ -15,61 +15,77 @@ QFont, QFontDatabase, QGradient, QIcon, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QHBoxLayout, - QLabel, QSizePolicy, QTextEdit, QWidget) +from PySide6.QtWidgets import (QApplication, QGridLayout, QHBoxLayout, QLabel, + QPushButton, QSizePolicy, QSpacerItem, QTextEdit, + QWidget) +import airunner.resources_dark_rc class Ui_message(object): def setupUi(self, message): if not message.objectName(): message.setObjectName(u"message") - message.resize(464, 433) - sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.MinimumExpanding) + message.resize(497, 456) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(message.sizePolicy().hasHeightForWidth()) message.setSizePolicy(sizePolicy) - message.setMinimumSize(QSize(0, 40)) - self.gridLayout = QGridLayout(message) + message.setMinimumSize(QSize(0, 0)) + message.setStyleSheet(u"") + self.gridLayout_2 = QGridLayout(message) + self.gridLayout_2.setSpacing(0) + self.gridLayout_2.setObjectName(u"gridLayout_2") + self.gridLayout_2.setContentsMargins(0, 0, 0, 0) + self.message_container = QWidget(message) + self.message_container.setObjectName(u"message_container") + self.gridLayout = QGridLayout(self.message_container) self.gridLayout.setSpacing(0) self.gridLayout.setObjectName(u"gridLayout") - self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setContentsMargins(10, 10, 10, 10) self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.setSpacing(10) + self.horizontalLayout.setSpacing(5) self.horizontalLayout.setObjectName(u"horizontalLayout") - self.horizontalLayout.setContentsMargins(-1, -1, -1, 10) - self.user_name = QLabel(message) + self.user_name = QLabel(self.message_container) self.user_name.setObjectName(u"user_name") - self.user_name.setAlignment(Qt.AlignmentFlag.AlignBottom|Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft) + self.user_name.setMinimumSize(QSize(0, 25)) self.horizontalLayout.addWidget(self.user_name) - self.content = QTextEdit(message) - self.content.setObjectName(u"content") - sizePolicy1 = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding) - sizePolicy1.setHorizontalStretch(0) - sizePolicy1.setVerticalStretch(0) - sizePolicy1.setHeightForWidth(self.content.sizePolicy().hasHeightForWidth()) - self.content.setSizePolicy(sizePolicy1) - self.content.setMinimumSize(QSize(0, 40)) - self.content.setStyleSheet(u"border-radius: 5px; border: 5px solid #1f1f1f; background-color: #1f1f1f; color: #ffffff;") - self.content.setFrameShape(QFrame.Shape.NoFrame) - self.content.setFrameShadow(QFrame.Shadow.Plain) - self.content.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - self.content.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) - self.horizontalLayout.addWidget(self.content) + self.horizontalLayout.addItem(self.horizontalSpacer) - self.bot_name = QLabel(message) - self.bot_name.setObjectName(u"bot_name") - self.bot_name.setAlignment(Qt.AlignmentFlag.AlignBottom|Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft) + self.copy_button = QPushButton(self.message_container) + self.copy_button.setObjectName(u"copy_button") + icon = QIcon(QIcon.fromTheme(u"edit-copy")) + self.copy_button.setIcon(icon) - self.horizontalLayout.addWidget(self.bot_name) + self.horizontalLayout.addWidget(self.copy_button) + + self.delete_button = QPushButton(self.message_container) + self.delete_button.setObjectName(u"delete_button") + icon1 = QIcon(QIcon.fromTheme(u"edit-delete")) + self.delete_button.setIcon(icon1) + + self.horizontalLayout.addWidget(self.delete_button) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.content = QTextEdit(self.message_container) + self.content.setObjectName(u"content") + self.content.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.content.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + + self.gridLayout.addWidget(self.content, 1, 0, 1, 1) + + + self.gridLayout_2.addWidget(self.message_container, 0, 0, 1, 1) + self.retranslateUi(message) + self.delete_button.clicked.connect(message.delete) + self.copy_button.clicked.connect(message.copy) QMetaObject.connectSlotsByName(message) # setupUi @@ -77,6 +93,13 @@ def setupUi(self, message): def retranslateUi(self, message): message.setWindowTitle(QCoreApplication.translate("message", u"Form", None)) self.user_name.setText(QCoreApplication.translate("message", u"TextLabel", None)) - self.bot_name.setText(QCoreApplication.translate("message", u"TextLabel", None)) +#if QT_CONFIG(tooltip) + self.copy_button.setToolTip(QCoreApplication.translate("message", u"Copy message", None)) +#endif // QT_CONFIG(tooltip) + self.copy_button.setText("") +#if QT_CONFIG(tooltip) + self.delete_button.setToolTip(QCoreApplication.translate("message", u"Delete message", None)) +#endif // QT_CONFIG(tooltip) + self.delete_button.setText("") # retranslateUi diff --git a/src/airunner/widgets/llm/templates/prompt_templates.ui b/src/airunner/widgets/llm/templates/prompt_templates.ui new file mode 100644 index 000000000..e6b9efe2d --- /dev/null +++ b/src/airunner/widgets/llm/templates/prompt_templates.ui @@ -0,0 +1,293 @@ + + + prompt_templates_widget + + + + 0 + 0 + 582 + 459 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 10 + + + + + System Prompt + + + + 10 + + + 10 + + + 10 + + + 10 + + + 0 + + + 10 + + + + + + + + Reset to Default + + + + + + + + + + Template + + + + 10 + + + 10 + + + 10 + + + 10 + + + 0 + + + + + + + + + + + Guardrails Prompt + + + + 10 + + + 10 + + + 10 + + + 10 + + + 0 + + + 10 + + + + + + + + Reset to Default + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Use guardrails + + + + + + + Use datetime + + + + + + + + + + + system_prompt + textChanged() + prompt_templates_widget + system_prompt_changed() + + + 363 + 190 + + + 344 + 0 + + + + + guardrails_prompt + textChanged() + prompt_templates_widget + guardrails_prompt_changed() + + + 390 + 375 + + + 415 + 0 + + + + + use_guardrails + toggled(bool) + prompt_templates_widget + toggle_use_guardrails(bool) + + + 423 + 442 + + + 0 + 458 + + + + + use_datetime + toggled(bool) + prompt_templates_widget + toggle_use_datetime(bool) + + + 530 + 448 + + + 568 + 458 + + + + + template_name + currentIndexChanged(int) + prompt_templates_widget + template_changed(int) + + + 348 + 42 + + + 284 + -13 + + + + + pushButton_2 + clicked() + prompt_templates_widget + reset_system_prompt() + + + 280 + 224 + + + -3 + 75 + + + + + pushButton + clicked() + prompt_templates_widget + reset_guardrails_prompt() + + + 202 + 402 + + + -5 + 242 + + + + + + template_changed(int) + system_prompt_changed() + guardrails_prompt_changed() + toggle_use_guardrails(bool) + toggle_use_datetime(bool) + reset_system_prompt() + reset_guardrails_prompt() + + diff --git a/src/airunner/widgets/llm/templates/prompt_templates_ui.py b/src/airunner/widgets/llm/templates/prompt_templates_ui.py new file mode 100644 index 000000000..699af9e01 --- /dev/null +++ b/src/airunner/widgets/llm/templates/prompt_templates_ui.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'prompt_templates.ui' +## +## Created by: Qt User Interface Compiler version 6.7.0 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QGridLayout, + QGroupBox, QHBoxLayout, QPlainTextEdit, QPushButton, + QSizePolicy, QSpacerItem, QWidget) + +class Ui_prompt_templates_widget(object): + def setupUi(self, prompt_templates_widget): + if not prompt_templates_widget.objectName(): + prompt_templates_widget.setObjectName(u"prompt_templates_widget") + prompt_templates_widget.resize(582, 459) + self.gridLayout = QGridLayout(prompt_templates_widget) + self.gridLayout.setObjectName(u"gridLayout") + self.gridLayout.setHorizontalSpacing(0) + self.gridLayout.setVerticalSpacing(10) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.groupBox_2 = QGroupBox(prompt_templates_widget) + self.groupBox_2.setObjectName(u"groupBox_2") + self.gridLayout_3 = QGridLayout(self.groupBox_2) + self.gridLayout_3.setObjectName(u"gridLayout_3") + self.gridLayout_3.setHorizontalSpacing(0) + self.gridLayout_3.setVerticalSpacing(10) + self.gridLayout_3.setContentsMargins(10, 10, 10, 10) + self.system_prompt = QPlainTextEdit(self.groupBox_2) + self.system_prompt.setObjectName(u"system_prompt") + + self.gridLayout_3.addWidget(self.system_prompt, 0, 0, 1, 1) + + self.pushButton_2 = QPushButton(self.groupBox_2) + self.pushButton_2.setObjectName(u"pushButton_2") + + self.gridLayout_3.addWidget(self.pushButton_2, 1, 0, 1, 1) + + + self.gridLayout.addWidget(self.groupBox_2, 1, 0, 1, 1) + + self.groupBox = QGroupBox(prompt_templates_widget) + self.groupBox.setObjectName(u"groupBox") + self.gridLayout_2 = QGridLayout(self.groupBox) + self.gridLayout_2.setSpacing(0) + self.gridLayout_2.setObjectName(u"gridLayout_2") + self.gridLayout_2.setContentsMargins(10, 10, 10, 10) + self.template_name = QComboBox(self.groupBox) + self.template_name.setObjectName(u"template_name") + + self.gridLayout_2.addWidget(self.template_name, 0, 0, 1, 1) + + + self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1) + + self.groupBox_3 = QGroupBox(prompt_templates_widget) + self.groupBox_3.setObjectName(u"groupBox_3") + self.gridLayout_4 = QGridLayout(self.groupBox_3) + self.gridLayout_4.setObjectName(u"gridLayout_4") + self.gridLayout_4.setHorizontalSpacing(0) + self.gridLayout_4.setVerticalSpacing(10) + self.gridLayout_4.setContentsMargins(10, 10, 10, 10) + self.guardrails_prompt = QPlainTextEdit(self.groupBox_3) + self.guardrails_prompt.setObjectName(u"guardrails_prompt") + + self.gridLayout_4.addWidget(self.guardrails_prompt, 0, 0, 1, 1) + + self.pushButton = QPushButton(self.groupBox_3) + self.pushButton.setObjectName(u"pushButton") + + self.gridLayout_4.addWidget(self.pushButton, 1, 0, 1, 1) + + + self.gridLayout.addWidget(self.groupBox_3, 2, 0, 1, 1) + + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.horizontalLayout.addItem(self.horizontalSpacer) + + self.use_guardrails = QCheckBox(prompt_templates_widget) + self.use_guardrails.setObjectName(u"use_guardrails") + + self.horizontalLayout.addWidget(self.use_guardrails) + + self.use_datetime = QCheckBox(prompt_templates_widget) + self.use_datetime.setObjectName(u"use_datetime") + + self.horizontalLayout.addWidget(self.use_datetime) + + + self.gridLayout.addLayout(self.horizontalLayout, 3, 0, 1, 1) + + + self.retranslateUi(prompt_templates_widget) + self.system_prompt.textChanged.connect(prompt_templates_widget.system_prompt_changed) + self.guardrails_prompt.textChanged.connect(prompt_templates_widget.guardrails_prompt_changed) + self.use_guardrails.toggled.connect(prompt_templates_widget.toggle_use_guardrails) + self.use_datetime.toggled.connect(prompt_templates_widget.toggle_use_datetime) + self.template_name.currentIndexChanged.connect(prompt_templates_widget.template_changed) + self.pushButton_2.clicked.connect(prompt_templates_widget.reset_system_prompt) + self.pushButton.clicked.connect(prompt_templates_widget.reset_guardrails_prompt) + + QMetaObject.connectSlotsByName(prompt_templates_widget) + # setupUi + + def retranslateUi(self, prompt_templates_widget): + prompt_templates_widget.setWindowTitle(QCoreApplication.translate("prompt_templates_widget", u"Form", None)) + self.groupBox_2.setTitle(QCoreApplication.translate("prompt_templates_widget", u"System Prompt", None)) + self.pushButton_2.setText(QCoreApplication.translate("prompt_templates_widget", u"Reset to Default", None)) + self.groupBox.setTitle(QCoreApplication.translate("prompt_templates_widget", u"Template", None)) + self.groupBox_3.setTitle(QCoreApplication.translate("prompt_templates_widget", u"Guardrails Prompt", None)) + self.pushButton.setText(QCoreApplication.translate("prompt_templates_widget", u"Reset to Default", None)) + self.use_guardrails.setText(QCoreApplication.translate("prompt_templates_widget", u"Use guardrails", None)) + self.use_datetime.setText(QCoreApplication.translate("prompt_templates_widget", u"Use datetime", None)) + # retranslateUi + diff --git a/src/airunner/windows/main/settings_mixin.py b/src/airunner/windows/main/settings_mixin.py index 210935b74..45e0b1601 100644 --- a/src/airunner/windows/main/settings_mixin.py +++ b/src/airunner/windows/main/settings_mixin.py @@ -677,6 +677,7 @@ def load_history_from_db(self, conversation_id): ).order_by(Message.timestamp).all() results = [ { + "id": message.id, "role": message.role, "content": message.content, "name": message.name, diff --git a/src/airunner/windows/settings/airunner_settings.py b/src/airunner/windows/settings/airunner_settings.py index 6487d5659..0346b954b 100644 --- a/src/airunner/windows/settings/airunner_settings.py +++ b/src/airunner/windows/settings/airunner_settings.py @@ -7,6 +7,7 @@ from airunner.widgets.export_preferences.export_preferences_widget import ExportPreferencesWidget from airunner.widgets.keyboard_shortcuts.keyboard_shortcuts_widget import KeyboardShortcutsWidget from airunner.widgets.llm.bot_preferences import BotPreferencesWidget +from airunner.widgets.llm.prompt_templates_widget import PromptTemplatesWidget from airunner.widgets.memory_preferences.memory_preferences_widget import MemoryPreferencesWidget from airunner.widgets.paths.paths_widget import PathsWidget from airunner.widgets.stt.stt_settings_widget import STTSettingsWidget @@ -63,6 +64,8 @@ def available_widgets(self, name): return APITokenWidget elif name == "tts_preferences": return TTSPreferencesWidget + elif name == "prompt_templates": + return PromptTemplatesWidget elif name == "bot_preferences": return BotPreferencesWidget elif name == "export_preferences": @@ -111,7 +114,7 @@ def initialize_window(self): ] }, { - "section": "Chatbot, text-to-speech", + "section": "Chatbot, prompt templates, text-to-speech", "files": [ { "name": "bot_preferences", @@ -123,6 +126,11 @@ def initialize_window(self): "display_name": "Text-to-Speech", "checkable": False }, + { + "name": "prompt_templates", + "display_name": "Prompt Templates", + "checkable": False + }, # { # "name": "stt_preferences", # "display_name": "Speech-to-Text",