add per conversation settings
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
<file preprocess="xml-stripblanks" alias="ui/export_dialog.ui">views/export_dialog.ui</file>
|
||||
<file preprocess="xml-stripblanks" alias="ui/preferences_window.ui">views/preferences_window.ui</file>
|
||||
<file preprocess="xml-stripblanks" alias="ui/save_dialog.ui">views/save_dialog.ui</file>
|
||||
<file preprocess="xml-stripblanks" alias="ui/chat_settings_dialog.ui">views/chat_settings_dialog.ui</file>
|
||||
<file preprocess="xml-stripblanks" alias="ui/thread_item.ui">widgets/thread_item.ui</file>
|
||||
<file preprocess="xml-stripblanks" alias="ui/item.ui">widgets/item.ui</file>
|
||||
<file preprocess="xml-stripblanks" alias="ui/model_item.ui">widgets/model_item.ui</file>
|
||||
|
||||
+7
-27
@@ -60,35 +60,14 @@ class LLM:
|
||||
self.load_model()
|
||||
|
||||
messages = []
|
||||
for msg in chat.get("content", []):
|
||||
role = msg.get("role", "user")
|
||||
if role == self.app.user_name:
|
||||
role = "user"
|
||||
elif role == self.app.bot_name:
|
||||
role = "assistant"
|
||||
else:
|
||||
role = "user"
|
||||
content = msg.get("content", "")
|
||||
|
||||
system_prompt = chat.get("system_prompt", "")
|
||||
if system_prompt:
|
||||
messages.append({
|
||||
"role": role,
|
||||
"content": [{"type": "text", "text": content}]
|
||||
"role": "system",
|
||||
"content": [{"type": "text", "text": system_prompt}]
|
||||
})
|
||||
|
||||
with self.engine.create_conversation(messages=messages) as conv:
|
||||
response = conv.send_message(prompt)
|
||||
GLib.idle_add(callback, response["content"][0]["text"])
|
||||
except Exception as e:
|
||||
GLib.idle_add(error_callback, str(e))
|
||||
|
||||
t = threading.Thread(target=thread_run)
|
||||
t.start()
|
||||
|
||||
def ask_async(self, prompt, chat, callback, error_callback):
|
||||
def thread_run():
|
||||
try:
|
||||
self.load_model()
|
||||
|
||||
messages = []
|
||||
|
||||
for msg in chat.get("content", []):
|
||||
role = msg.get("role", "user")
|
||||
if role == self.app.user_name:
|
||||
@@ -109,6 +88,7 @@ class LLM:
|
||||
for item in chunk.get("content", []):
|
||||
if item.get("type") == "text":
|
||||
GLib.idle_add(callback, item["text"])
|
||||
GLib.idle_add(callback, None)
|
||||
except Exception as e:
|
||||
GLib.idle_add(error_callback, str(e))
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ class BavarderApplication(Adw.Application):
|
||||
"title": "New Chat " + str(chat_id),
|
||||
"starred": False,
|
||||
"content": [],
|
||||
"system_prompt": "",
|
||||
}
|
||||
|
||||
self.data["chats"].append(chat)
|
||||
|
||||
@@ -5,6 +5,7 @@ python = import('python')
|
||||
blueprints = custom_target('blueprints',
|
||||
input: files(
|
||||
'gtk/help-overlay.blp',
|
||||
'views/chat_settings_dialog.blp',
|
||||
'views/export_dialog.blp',
|
||||
'views/preferences_window.blp',
|
||||
'views/save_dialog.blp',
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $ChatSettingsDialog : Adw.MessageDialog {
|
||||
title: _("Chat Settings");
|
||||
close-response: "cancel";
|
||||
|
||||
Adw.EntryRow title_entry {
|
||||
title: _("Chat Title");
|
||||
show-apply-button: true;
|
||||
}
|
||||
|
||||
Gtk.Label {
|
||||
label: _("System Prompt");
|
||||
halign: start;
|
||||
margin-top: 12;
|
||||
}
|
||||
|
||||
Gtk.TextView system_prompt_view {
|
||||
height-request: 150;
|
||||
wrap-mode: word;
|
||||
margin-top: 6;
|
||||
}
|
||||
|
||||
Adw.ButtonContent {
|
||||
label: _("Cancel");
|
||||
use-underline: true;
|
||||
response: "cancel";
|
||||
}
|
||||
|
||||
Adw.ButtonContent {
|
||||
label: _("Save");
|
||||
use-underline: true;
|
||||
response: "save";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
from gi.repository import Gtk, Adw, GLib
|
||||
|
||||
from bavarder.constants import app_id, rootdir
|
||||
|
||||
|
||||
@Gtk.Template(resource_path=f"{rootdir}/ui/chat_settings_dialog.ui")
|
||||
class ChatSettingsDialog(Adw.MessageDialog):
|
||||
__gtype_name__ = "ChatSettingsDialog"
|
||||
|
||||
title_entry = Gtk.Template.Child()
|
||||
system_prompt_view = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, parent, chat, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.parent = parent
|
||||
self.chat = chat
|
||||
self.app = parent.app
|
||||
|
||||
self.setup()
|
||||
|
||||
def setup(self):
|
||||
self.title_entry.set_text(self.chat.get("title", ""))
|
||||
|
||||
system_prompt = self.chat.get("system_prompt", "")
|
||||
buffer = self.system_prompt_view.get_buffer()
|
||||
buffer.set_text(system_prompt, -1)
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_save_clicked(self, widget, *args):
|
||||
new_title = self.title_entry.get_text().strip()
|
||||
if new_title:
|
||||
self.chat["title"] = new_title
|
||||
self.parent.title.set_title(new_title)
|
||||
|
||||
buffer = self.system_prompt_view.get_buffer()
|
||||
system_prompt = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
|
||||
self.chat["system_prompt"] = system_prompt
|
||||
|
||||
self.parent.threads_row_activated_cb()
|
||||
self.close()
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_cancel_clicked(self, widget, *args):
|
||||
self.close()
|
||||
@@ -3,6 +3,7 @@ views_dir = join_paths(MODULE_DIR, 'views')
|
||||
views_sources = [
|
||||
'__init__.py',
|
||||
'about_window.py',
|
||||
'chat_settings_dialog.py',
|
||||
'export_dialog.py',
|
||||
'preferences_window.py',
|
||||
'save_dialog.py',
|
||||
|
||||
+45
-8
@@ -31,6 +31,7 @@ from bavarder.widgets.thread_item import ThreadItem
|
||||
from bavarder.widgets.item import Item
|
||||
from bavarder.threading import KillableThread
|
||||
from bavarder.views.export_dialog import ExportDialog
|
||||
from bavarder.views.chat_settings_dialog import ChatSettingsDialog
|
||||
|
||||
class CustomEntry(Gtk.TextView):
|
||||
def __init__(self, **kwargs):
|
||||
@@ -84,6 +85,7 @@ class BavarderWindow(Adw.ApplicationWindow):
|
||||
self.create_action("cancel", self.cancel, ["<primary>Escape"])
|
||||
self.create_action("clear_all", self.on_clear_all)
|
||||
self.create_action("export", self.on_export, ["<primary>e"])
|
||||
self.create_action("chat_settings", self.on_chat_settings, ["<primary>comma"])
|
||||
|
||||
self.settings.bind(
|
||||
"width", self, "default-width", Gio.SettingsBindFlags.DEFAULT
|
||||
@@ -252,6 +254,12 @@ class BavarderWindow(Adw.ApplicationWindow):
|
||||
toast.set_title(_("Nothing to export!"))
|
||||
self.toast_overlay.add_toast(toast)
|
||||
|
||||
def on_chat_settings(self, *args):
|
||||
if self.chat:
|
||||
dialog = ChatSettingsDialog(self, self.chat)
|
||||
dialog.set_transient_for(self)
|
||||
dialog.present()
|
||||
|
||||
# MODEL - OFFLINE
|
||||
def load_model_selector(self):
|
||||
provider_menu = Gio.Menu()
|
||||
@@ -333,12 +341,28 @@ class BavarderWindow(Adw.ApplicationWindow):
|
||||
|
||||
self.add_user_item(prompt)
|
||||
|
||||
def on_response(response):
|
||||
if not response:
|
||||
self.add_assistant_item(_("Sorry, I don't know what to say."))
|
||||
else:
|
||||
self.add_assistant_item(response)
|
||||
self.toast.dismiss()
|
||||
self.response_buffer = ""
|
||||
self.assistant_item_added = False
|
||||
|
||||
def on_token(token):
|
||||
if token is None:
|
||||
self.toast.dismiss()
|
||||
if not self.response_buffer:
|
||||
self.add_assistant_item(_("Sorry, I don't know what to say."))
|
||||
else:
|
||||
self.update_last_assistant_item(self.response_buffer.strip())
|
||||
return
|
||||
|
||||
self.response_buffer += token
|
||||
|
||||
min_content = 5
|
||||
if len(self.response_buffer) >= min_content:
|
||||
if not self.assistant_item_added:
|
||||
self.add_assistant_item(self.response_buffer.strip())
|
||||
self.assistant_item_added = True
|
||||
else:
|
||||
self.update_last_assistant_item(self.response_buffer.strip())
|
||||
self.scroll_down()
|
||||
|
||||
def on_error(error):
|
||||
self.toast.dismiss()
|
||||
@@ -349,7 +373,7 @@ class BavarderWindow(Adw.ApplicationWindow):
|
||||
self.toast.set_timeout(0)
|
||||
self.toast_overlay.add_toast(self.toast)
|
||||
|
||||
self.app.ask(prompt, self.chat, on_response, on_error)
|
||||
self.app.ask(prompt, self.chat, on_token, on_error)
|
||||
|
||||
# @Gtk.Template.Callback()
|
||||
# def on_emoji(self, *args):
|
||||
@@ -395,12 +419,19 @@ class BavarderWindow(Adw.ApplicationWindow):
|
||||
|
||||
self.scroll_down()
|
||||
|
||||
def get_model_name(self):
|
||||
model_path = self.app.data.get("models", {}).get("model_path", "")
|
||||
if model_path and os.path.exists(model_path):
|
||||
return os.path.basename(model_path)
|
||||
return "litert-lm"
|
||||
|
||||
def add_assistant_item(self, content):
|
||||
model_name = self.get_model_name()
|
||||
c = {
|
||||
"role": self.app.bot_name,
|
||||
"content": content,
|
||||
"time": self.get_time(),
|
||||
"model": "litert-lm",
|
||||
"model": model_name,
|
||||
}
|
||||
|
||||
self.content.append(c)
|
||||
@@ -408,3 +439,9 @@ class BavarderWindow(Adw.ApplicationWindow):
|
||||
self.threads_row_activated_cb()
|
||||
|
||||
self.scroll_down()
|
||||
|
||||
def update_last_assistant_item(self, content):
|
||||
if self.content:
|
||||
self.content[-1]["content"] = content
|
||||
self.threads_row_activated_cb()
|
||||
self.scroll_down()
|
||||
|
||||
Reference in New Issue
Block a user