From 7d58783eb31c7d1c23949fec395e1add7b26134a Mon Sep 17 00:00:00 2001
From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com>
Date: Thu, 24 Oct 2024 14:25:51 -0600
Subject: [PATCH 1/5] remove bot_mood from bot_preferences widget
---
setup.py | 2 +-
src/airunner/widgets/llm/bot_preferences.py | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
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/widgets/llm/bot_preferences.py b/src/airunner/widgets/llm/bot_preferences.py
index 33611d9cb..95160613a 100644
--- a/src/airunner/widgets/llm/bot_preferences.py
+++ b/src/airunner/widgets/llm/bot_preferences.py
@@ -25,7 +25,6 @@ def load_form_elements(self):
"username",
"botname",
"bot_personality",
- "bot_mood",
"names_groupbox",
"personality_groupbox",
"mood_groupbox",
From c23dcec020a13f6fe6c825349169597b0f172be0 Mon Sep 17 00:00:00 2001
From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com>
Date: Thu, 24 Oct 2024 14:35:52 -0600
Subject: [PATCH 2/5] remove more mood references
---
src/airunner/widgets/llm/bot_preferences.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/airunner/widgets/llm/bot_preferences.py b/src/airunner/widgets/llm/bot_preferences.py
index 95160613a..92df378c4 100644
--- a/src/airunner/widgets/llm/bot_preferences.py
+++ b/src/airunner/widgets/llm/bot_preferences.py
@@ -27,7 +27,6 @@ def load_form_elements(self):
"bot_personality",
"names_groupbox",
"personality_groupbox",
- "mood_groupbox",
"system_instructions",
"system_instructions_groupbox",
"guardrails_prompt",
@@ -40,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)
@@ -69,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)
From b1f7b6c0e093c78dd97910222459856715690747 Mon Sep 17 00:00:00 2001
From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com>
Date: Thu, 24 Oct 2024 14:36:10 -0600
Subject: [PATCH 3/5] use the correct originally requested action when
summarizing conversation
---
src/airunner/handlers/llm/agent/base_agent.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/airunner/handlers/llm/agent/base_agent.py b/src/airunner/handlers/llm/agent/base_agent.py
index bc4569fd2..d09e0296c 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
@@ -508,13 +509,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 +658,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 +749,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,
)
From 98ca98a879053ccc3a0d1ec178c65de1ac517a5a Mon Sep 17 00:00:00 2001
From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com>
Date: Fri, 25 Oct 2024 11:07:53 -0600
Subject: [PATCH 4/5] Adds a prompt template editor to settings
---
.../prompt_templates_bootstrap_data.py | 41 +--
src/airunner/data/models/settings_models.py | 27 +-
src/airunner/handlers/llm/agent/base_agent.py | 28 +-
src/airunner/resources_rc.py | 92 +++---
src/airunner/settings.py | 90 +++++-
.../widgets/llm/prompt_templates_widget.py | 113 +++++++
.../widgets/llm/templates/prompt_templates.ui | 293 ++++++++++++++++++
.../llm/templates/prompt_templates_ui.py | 128 ++++++++
.../windows/settings/airunner_settings.py | 10 +-
9 files changed, 678 insertions(+), 144 deletions(-)
create mode 100644 src/airunner/widgets/llm/prompt_templates_widget.py
create mode 100644 src/airunner/widgets/llm/templates/prompt_templates.ui
create mode 100644 src/airunner/widgets/llm/templates/prompt_templates_ui.py
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 d09e0296c..b902e73e3 100644
--- a/src/airunner/handlers/llm/agent/base_agent.py
+++ b/src/airunner/handlers/llm/agent/base_agent.py
@@ -352,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()
]
@@ -488,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,
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/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/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/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",
From a5c79b618360533144765ee0cc342bbeaa7b07b5 Mon Sep 17 00:00:00 2001
From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com>
Date: Fri, 25 Oct 2024 13:59:17 -0600
Subject: [PATCH 5/5] better handling of chat messages adds copy / delete
controls to each message
---
src/airunner/styles/dark_theme/styles.qss | 32 ++-
.../widgets/llm/chat_prompt_widget.py | 15 +-
src/airunner/widgets/llm/message_widget.py | 66 ++++--
.../widgets/llm/templates/chat_prompt.ui | 6 +
.../widgets/llm/templates/chat_prompt_ui.py | 2 +
src/airunner/widgets/llm/templates/message.ui | 203 ++++++++++++------
.../widgets/llm/templates/message_ui.py | 83 ++++---
src/airunner/windows/main/settings_mixin.py | 1 +
8 files changed, 285 insertions(+), 123 deletions(-)
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/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/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/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,