New model manager (create model doesn't work yet)
This commit is contained in:
parent
bbf678cb75
commit
d0735de129
File diff suppressed because it is too large
Load Diff
@ -155,7 +155,7 @@ class chat(Gtk.ScrolledWindow):
|
||||
message_element.set_text(message_data['content'])
|
||||
message_element.add_footer(datetime.datetime.strptime(message_data['date'] + (":00" if message_data['date'].count(":") == 1 else ""), '%Y/%m/%d %H:%M:%S'))
|
||||
else:
|
||||
self.show_welcome_screen(len(window.model_selector.get_model_list()) > 0)
|
||||
self.show_welcome_screen(len(window.model_manager.get_model_list()) > 0)
|
||||
|
||||
def messages_to_dict(self) -> dict:
|
||||
messages_dict = {}
|
||||
@ -269,7 +269,7 @@ class chat_list(Gtk.ListBox):
|
||||
tab = chat_tab(chat_window)
|
||||
self.prepend(tab)
|
||||
self.tab_list.insert(0, tab)
|
||||
chat_window.show_welcome_screen(len(window.model_selector.get_model_list()) > 0)
|
||||
chat_window.show_welcome_screen(len(window.model_manager.get_model_list()) > 0)
|
||||
window.chat_stack.add_child(chat_window)
|
||||
window.chat_list_box.select_row(tab)
|
||||
return chat_window
|
||||
@ -399,5 +399,4 @@ class chat_list(Gtk.ListBox):
|
||||
window.switch_send_stop_button(not row.chat_window.busy)
|
||||
if len(row.chat_window.messages) > 0:
|
||||
last_model_used = row.chat_window.messages[list(row.chat_window.messages)[-1]].model
|
||||
window.model_selector.change_model(last_model_used)
|
||||
|
||||
window.model_manager.change_model(last_model_used)
|
||||
|
@ -7,8 +7,9 @@ import gi
|
||||
gi.require_version('Gtk', '4.0')
|
||||
gi.require_version('GtkSource', '5')
|
||||
from gi.repository import Gtk, GObject, Gio, Adw, GtkSource, GLib, Gdk
|
||||
import logging, os, datetime, re, shutil, threading
|
||||
import logging, os, datetime, re, shutil, threading, json, sys
|
||||
from ..internal import config_dir, data_dir, cache_dir, source_dir
|
||||
from .. import connection_handler, available_models_descriptions, dialogs
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -80,14 +81,9 @@ class model_selector_button(Gtk.MenuButton):
|
||||
self.get_child().set_label(window.convert_model_name(model_name, 0))
|
||||
self.set_tooltip_text(window.convert_model_name(model_name, 0))
|
||||
elif len(list(listbox)) == 0:
|
||||
self.get_child().set_label(_("Select a model"))
|
||||
self.get_child().set_label(_("Select a Model"))
|
||||
self.set_tooltip_text(_("Select a Model"))
|
||||
|
||||
def get_model(self) -> str:
|
||||
row = self.get_popover().model_list_box.get_selected_row()
|
||||
if row:
|
||||
return row.get_name()
|
||||
|
||||
def add_model(self, model_name:str):
|
||||
model_row = Gtk.ListBoxRow(
|
||||
child = Gtk.Label(
|
||||
@ -103,8 +99,409 @@ class model_selector_button(Gtk.MenuButton):
|
||||
self.get_popover().model_list_box.append(model_row)
|
||||
self.change_model(model_name)
|
||||
|
||||
def get_model_list(self) -> list:
|
||||
return [model.get_name() for model in list(self.get_popover().model_list_box)]
|
||||
def remove_model(self, model_name:str):
|
||||
self.get_popover().model_list_box.remove(next((model for model in list(self.get_popover().model_list_box) if model.get_name() == model_name), None))
|
||||
self.model_changed(self.get_popover().model_list_box)
|
||||
print(self.get_popover().model_list_box.get_selected_row())
|
||||
|
||||
def clear_list(self):
|
||||
self.get_popover().model_list_box.remove_all()
|
||||
|
||||
class pulling_model(Gtk.ListBoxRow):
|
||||
__gtype_name__ = 'AlpacaPullingModel'
|
||||
|
||||
def __init__(self, model_name:str):
|
||||
model_label = Gtk.Label(
|
||||
css_classes=["heading"],
|
||||
label=model_name.split(":")[0].replace("-", " ").title(),
|
||||
hexpand=True,
|
||||
halign=1
|
||||
)
|
||||
tag_label = Gtk.Label(
|
||||
css_classes=["subtitle"],
|
||||
label=model_name.split(":")[1]
|
||||
)
|
||||
self.prc_label = Gtk.Label(
|
||||
css_classes=["subtitle", "numeric"],
|
||||
label='50%',
|
||||
hexpand=True,
|
||||
halign=2
|
||||
)
|
||||
subtitle_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
spacing=5,
|
||||
orientation=0
|
||||
)
|
||||
subtitle_box.append(tag_label)
|
||||
subtitle_box.append(self.prc_label)
|
||||
self.progress_bar = Gtk.ProgressBar(
|
||||
valign=2,
|
||||
show_text=False,
|
||||
css_classes=["horizontal"],
|
||||
fraction=.5
|
||||
)
|
||||
description_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
vexpand=True,
|
||||
spacing=5,
|
||||
orientation=1
|
||||
)
|
||||
description_box.append(model_label)
|
||||
description_box.append(subtitle_box)
|
||||
description_box.append(self.progress_bar)
|
||||
|
||||
delete_button = Gtk.Button(
|
||||
icon_name = "media-playback-stop-symbolic",
|
||||
vexpand = False,
|
||||
valign = 3,
|
||||
css_classes = ["destructive-action", "circular"],
|
||||
tooltip_text = _("Stop Pulling '{}'").format(window.convert_model_name(model_name, 0))
|
||||
)
|
||||
delete_button.connect('clicked', lambda *_: dialogs.stop_pull_model(window, self))
|
||||
|
||||
container_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
vexpand=True,
|
||||
spacing=10,
|
||||
orientation=0,
|
||||
margin_top=10,
|
||||
margin_bottom=10,
|
||||
margin_start=10,
|
||||
margin_end=10
|
||||
)
|
||||
|
||||
container_box.append(description_box)
|
||||
container_box.append(delete_button)
|
||||
|
||||
super().__init__(
|
||||
child=container_box,
|
||||
name=model_name
|
||||
)
|
||||
self.error = None
|
||||
|
||||
def update(self, data):
|
||||
if not self.get_parent():
|
||||
sys.exit()
|
||||
if 'error' in data:
|
||||
self.error = data['error']
|
||||
if 'total' in data and 'completed' in data:
|
||||
fraction = round(data['completed'] / data['total'], 4)
|
||||
GLib.idle_add(self.prc_label.set_label, f"{fraction:05.2%}")
|
||||
GLib.idle_add(self.progress_bar.set_fraction, fraction)
|
||||
else:
|
||||
GLib.idle_add(self.prc_label.set_label, data['status'])
|
||||
GLib.idle_add(self.progress_bar.pulse)
|
||||
|
||||
class pulling_model_list(Gtk.ListBox):
|
||||
__gtype_name__ = 'AlpacaPullingModelList'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
selection_mode=0,
|
||||
css_classes=["boxed-list"],
|
||||
visible=False
|
||||
)
|
||||
|
||||
class local_model(Gtk.ListBoxRow):
|
||||
__gtype_name__ = 'AlpacaLocalModel'
|
||||
|
||||
def __init__(self, model_name:str):
|
||||
model_title = window.convert_model_name(model_name, 0)
|
||||
|
||||
model_label = Gtk.Label(
|
||||
css_classes=["heading"],
|
||||
label=model_title.split(" (")[0],
|
||||
hexpand=True,
|
||||
halign=1
|
||||
)
|
||||
tag_label = Gtk.Label(
|
||||
css_classes=["subtitle"],
|
||||
label=model_title.split(" (")[1][:-1],
|
||||
hexpand=True,
|
||||
halign=1
|
||||
)
|
||||
description_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
vexpand=True,
|
||||
spacing=5,
|
||||
orientation=1
|
||||
)
|
||||
description_box.append(model_label)
|
||||
description_box.append(tag_label)
|
||||
|
||||
delete_button = Gtk.Button(
|
||||
icon_name = "user-trash-symbolic",
|
||||
vexpand = False,
|
||||
valign = 3,
|
||||
css_classes = ["destructive-action", "circular"],
|
||||
tooltip_text = _("Remove '{}'").format(window.convert_model_name(model_name, 0))
|
||||
)
|
||||
delete_button.connect('clicked', lambda *_, model_name=model_name: dialogs.delete_model(window, model_name))
|
||||
|
||||
container_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
vexpand=True,
|
||||
spacing=10,
|
||||
orientation=0,
|
||||
margin_top=10,
|
||||
margin_bottom=10,
|
||||
margin_start=10,
|
||||
margin_end=10
|
||||
)
|
||||
container_box.append(description_box)
|
||||
container_box.append(delete_button)
|
||||
|
||||
super().__init__(
|
||||
child=container_box,
|
||||
name=model_name
|
||||
)
|
||||
|
||||
class local_model_list(Gtk.ListBox):
|
||||
__gtype_name__ = 'AlpacaLocalModelList'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
selection_mode=0,
|
||||
css_classes=["boxed-list"],
|
||||
visible=False
|
||||
)
|
||||
|
||||
def add_model(self, model_name:str):
|
||||
model = local_model(model_name)
|
||||
self.append(model)
|
||||
if not self.get_visible():
|
||||
self.set_visible(True)
|
||||
|
||||
def remove_model(self, model_name:str):
|
||||
self.remove(next((model for model in list(self) if model.get_name() == model_name), None))
|
||||
|
||||
class available_model(Gtk.ListBoxRow):
|
||||
__gtype_name__ = 'AlpacaAvailableModel'
|
||||
|
||||
def __init__(self, model_name:str, model_author:str, model_description:str, image_recognition:bool):
|
||||
self.model_description = model_description
|
||||
self.model_title = model_name.replace("-", " ").title()
|
||||
self.model_author = model_author
|
||||
self.image_recognition = image_recognition
|
||||
model_label = Gtk.Label(
|
||||
css_classes=["heading"],
|
||||
label="<b>{}</b> <small>by {}</small>".format(self.model_title, self.model_author),
|
||||
hexpand=True,
|
||||
halign=1,
|
||||
use_markup=True
|
||||
)
|
||||
description_label = Gtk.Label(
|
||||
css_classes=["subtitle"],
|
||||
label=self.model_description,
|
||||
hexpand=True,
|
||||
halign=1,
|
||||
wrap=True,
|
||||
wrap_mode=0,
|
||||
)
|
||||
image_recognition_indicator = Gtk.Button(
|
||||
css_classes=["success", "pill", "image_recognition_indicator"],
|
||||
child=Gtk.Label(
|
||||
label=_("Image Recognition"),
|
||||
css_classes=["subtitle"]
|
||||
),
|
||||
halign=1
|
||||
)
|
||||
description_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
vexpand=True,
|
||||
spacing=5,
|
||||
orientation=1
|
||||
)
|
||||
description_box.append(model_label)
|
||||
description_box.append(description_label)
|
||||
if self.image_recognition: description_box.append(image_recognition_indicator)
|
||||
|
||||
container_box = Gtk.Box(
|
||||
hexpand=True,
|
||||
vexpand=True,
|
||||
spacing=10,
|
||||
orientation=0,
|
||||
margin_top=10,
|
||||
margin_bottom=10,
|
||||
margin_start=10,
|
||||
margin_end=10
|
||||
)
|
||||
next_icon = Gtk.Image.new_from_icon_name("go-next")
|
||||
next_icon.update_property([4], [_("Enter download menu for {}").format(self.model_title)])
|
||||
|
||||
container_box.append(description_box)
|
||||
container_box.append(next_icon)
|
||||
|
||||
super().__init__(
|
||||
child=container_box,
|
||||
name=model_name
|
||||
)
|
||||
|
||||
gesture_click = Gtk.GestureClick.new()
|
||||
gesture_click.connect("pressed", lambda *_: self.show_pull_menu())
|
||||
|
||||
event_controller_key = Gtk.EventControllerKey.new()
|
||||
event_controller_key.connect("key-pressed", lambda controller, key, *_: self.show_pull_menu() if key in (Gdk.KEY_space, Gdk.KEY_Return) else None)
|
||||
|
||||
self.add_controller(gesture_click)
|
||||
self.add_controller(event_controller_key)
|
||||
|
||||
def confirm_pull_model(self, model_name):
|
||||
##TODO I really need that instance manager
|
||||
threading.Thread(target=window.model_manager.pull_model, args=('http://0.0.0.0:11435', model_name)).start()
|
||||
window.navigation_view_manage_models.pop()
|
||||
|
||||
def show_pull_menu(self):
|
||||
with open(os.path.join(source_dir, 'available_models.json'), 'r', encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
window.navigation_view_manage_models.push_by_tag('model_tags_page')
|
||||
window.navigation_view_manage_models.find_page('model_tags_page').set_title(self.get_name().replace("-", " ").title())
|
||||
window.model_link_button.set_name(data[self.get_name()]['url'])
|
||||
window.model_link_button.set_tooltip_text(data[self.get_name()]['url'])
|
||||
window.model_tag_list_box.remove_all()
|
||||
tags = data[self.get_name()]['tags']
|
||||
|
||||
for tag_data in tags:
|
||||
if f"{self.get_name()}:{tag_data[0]}" not in window.model_manager.get_model_list():
|
||||
tag_row = Adw.ActionRow(
|
||||
title = tag_data[0],
|
||||
subtitle = tag_data[1],
|
||||
name = f"{self.get_name()}:{tag_data[0]}"
|
||||
)
|
||||
download_icon = Gtk.Image.new_from_icon_name("folder-download-symbolic")
|
||||
tag_row.add_suffix(download_icon)
|
||||
download_icon.update_property([4], [_("Download {}:{}").format(self.get_name(), tag_data[0])])
|
||||
|
||||
gesture_click = Gtk.GestureClick.new()
|
||||
gesture_click.connect("pressed", lambda *_, name=f"{self.get_name()}:{tag_data[0]}" : self.confirm_pull_model(name))
|
||||
|
||||
event_controller_key = Gtk.EventControllerKey.new()
|
||||
event_controller_key.connect("key-pressed", lambda controller, key, *_, name=f"{self.get_name()}:{tag_data[0]}" : self.confirm_pull_model(name) if key in (Gdk.KEY_space, Gdk.KEY_Return) else None)
|
||||
|
||||
tag_row.add_controller(gesture_click)
|
||||
tag_row.add_controller(event_controller_key)
|
||||
|
||||
window.model_tag_list_box.append(tag_row)
|
||||
|
||||
class available_model_list(Gtk.ListBox):
|
||||
__gtype_name__ = 'AlpacaAvailableModelList'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
selection_mode=0,
|
||||
css_classes=["boxed-list"],
|
||||
visible=False
|
||||
)
|
||||
|
||||
def add_model(self, model_name:str, model_author:str, model_description:str, image_recognition:bool):
|
||||
model = available_model(model_name, model_author, model_description, image_recognition)
|
||||
self.append(model)
|
||||
if not self.get_visible():
|
||||
self.set_visible(True)
|
||||
|
||||
class model_manager_container(Gtk.Box):
|
||||
__gtype_name__ = 'AlpacaModelManagerContainer'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
margin_top=12,
|
||||
margin_bottom=12,
|
||||
margin_start=12,
|
||||
margin_end=12,
|
||||
spacing=12,
|
||||
orientation=1
|
||||
)
|
||||
self.pulling_list = pulling_model_list()
|
||||
self.append(self.pulling_list)
|
||||
self.local_list = local_model_list()
|
||||
self.append(self.local_list)
|
||||
self.available_list = available_model_list()
|
||||
self.append(self.available_list)
|
||||
self.model_selector = model_selector_button()
|
||||
window.header_bar.set_title_widget(self.model_selector)
|
||||
|
||||
def add_local_model(self, model_name:str):
|
||||
self.local_list.add_model(model_name)
|
||||
if not self.local_list.get_visible():
|
||||
self.local_list.set_visible(True)
|
||||
self.model_selector.add_model(model_name)
|
||||
|
||||
def remove_local_model(self, model_name:str):
|
||||
logger.debug("Deleting model")
|
||||
response = connection_handler.simple_delete(f"{connection_handler.URL}/api/delete", data={"name": model_name})
|
||||
|
||||
if response.status_code == 200:
|
||||
self.local_list.remove_model(model_name)
|
||||
if len(list(self.local_list)) == 0:
|
||||
self.local_list.set_visible(False)
|
||||
self.model_selector.remove_model(model_name)
|
||||
window.show_toast(_("Model deleted successfully"), window.manage_models_overlay)
|
||||
else:
|
||||
window.manage_models_dialog.close()
|
||||
window.connection_error()
|
||||
|
||||
def get_selected_model(self) -> str:
|
||||
row = self.model_selector.get_popover().model_list_box.get_selected_row()
|
||||
if row:
|
||||
return row.get_name()
|
||||
|
||||
def get_model_list(self) -> list:
|
||||
return [model.get_name() for model in list(self.model_selector.get_popover().model_list_box)]
|
||||
|
||||
#Should only be called when the app starts
|
||||
def update_local_list(self):
|
||||
response = connection_handler.simple_get(f"{connection_handler.URL}/api/tags")
|
||||
if response.status_code == 200:
|
||||
self.local_list.remove_all()
|
||||
data = json.loads(response.text)
|
||||
if len(data['models']) == 0:
|
||||
self.local_list.set_visible(False)
|
||||
else:
|
||||
self.local_list.set_visible(True)
|
||||
for model in data['models']:
|
||||
self.add_local_model(model['name'])
|
||||
else:
|
||||
window.connection_error()
|
||||
|
||||
#Should only be called when the app starts
|
||||
def update_available_list(self):
|
||||
with open(os.path.join(source_dir, 'available_models.json'), 'r', encoding="utf-8") as f:
|
||||
for name, model_info in json.load(f).items():
|
||||
self.available_list.add_model(name, model_info['author'], available_models_descriptions.descriptions[name], model_info['image'])
|
||||
|
||||
def change_model(self, model_name:str):
|
||||
self.model_selector.change_model(model_name)
|
||||
|
||||
#threading.Thread(target=self.pulling_list.pull_model, args=(url, model_name, modelfile)).start()
|
||||
#Important: Call this using a thread, if not the app crashes
|
||||
def pull_model(self, url:str, model_name:str, modelfile:str=None): ##TODO, once you make an instance manager remove the url from this
|
||||
if ':' in model_name and model_name not in [model.get_name() for model in list(self.pulling_list)]:
|
||||
logger.info("Pulling model: {}".format(model_name))
|
||||
model = pulling_model(model_name)
|
||||
self.pulling_list.append(model)
|
||||
if not self.pulling_list.get_visible():
|
||||
GLib.idle_add(self.pulling_list.set_visible, True)
|
||||
|
||||
if modelfile:
|
||||
response = connection_handler.stream_post("{}/api/create".format(url), data=json.dumps({"name": model_name, "modelfile": modelfile}), callback=lambda data: model.update(data))
|
||||
else:
|
||||
response = connection_handler.stream_post("{}/api/pull".format(url), data=json.dumps({"name": model_name}), callback=lambda data: model.update(data))
|
||||
|
||||
if response.status_code == 200 and not model.error:
|
||||
GLib.idle_add(window.show_notification, _("Task Complete"), _("Model '{}' pulled successfully.").format(model_name), Gio.ThemedIcon.new("emblem-ok-symbolic"))
|
||||
GLib.idle_add(window.show_toast, _("Model '{}' pulled successfully.").format(model), window.manage_models_overlay)
|
||||
self.add_local_model(model_name)
|
||||
elif response.status_code == 200:
|
||||
GLib.idle_add(window.show_notification, _("Pull Model Error"), _("Failed to pull model '{}': {}").format(model_name, model.error), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
||||
GLib.idle_add(window.show_toast, _("Error pulling '{}': {}").format(model, model.error), window.manage_models_overlay)
|
||||
else:
|
||||
GLib.idle_add(window.show_notification, _("Pull Model Error"), _("Failed to pull model '{}' due to network error.").format(model_name), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
||||
GLib.idle_add(window.show_toast, _("Error pulling '{}'").format(model), window.manage_models_overlay)
|
||||
GLib.idle_add(window.manage_models_dialog.close)
|
||||
GLib.idle_add(window.connection_error)
|
||||
|
||||
self.pulling_list.remove(model)
|
||||
|
||||
if len(list(self.pulling_list)) == 0:
|
||||
GLib.idle_add(self.pulling_list.set_visible, False)
|
||||
|
||||
|
@ -116,15 +116,16 @@ def new_chat(self):
|
||||
|
||||
# STOP PULL MODEL | WORKS
|
||||
|
||||
def stop_pull_model_response(self, dialog, task, model_name):
|
||||
def stop_pull_model_response(self, dialog, task, pulling_model):
|
||||
if dialog.choose_finish(task) == "stop":
|
||||
self.stop_pull_model(model_name)
|
||||
if len(list(pulling_model.get_parent())) == 1:
|
||||
pulling_model.get_parent().set_visible(False)
|
||||
pulling_model.get_parent().remove(pulling_model)
|
||||
|
||||
def stop_pull_model(self, model_name):
|
||||
#self.pulling_model_list_box.unselect_all()
|
||||
def stop_pull_model(self, pulling_model):
|
||||
dialog = Adw.AlertDialog(
|
||||
heading=_("Stop Download?"),
|
||||
body=_("Are you sure you want to stop pulling '{} ({})'?").format(model_name.split(":")[0].capitalize(), model_name.split(":")[1]),
|
||||
body=_("Are you sure you want to stop pulling '{}'?").format(self.convert_model_name(pulling_model.get_name(), 0)),
|
||||
close_response="cancel"
|
||||
)
|
||||
dialog.add_response("cancel", _("Cancel"))
|
||||
@ -134,19 +135,19 @@ def stop_pull_model(self, model_name):
|
||||
dialog.choose(
|
||||
parent = self.manage_models_dialog,
|
||||
cancellable = None,
|
||||
callback = lambda dialog, task, model_name = model_name: stop_pull_model_response(self, dialog, task, model_name)
|
||||
callback = lambda dialog, task, model=pulling_model: stop_pull_model_response(self, dialog, task, model)
|
||||
)
|
||||
|
||||
# DELETE MODEL | WORKS
|
||||
|
||||
def delete_model_response(self, dialog, task, model_name):
|
||||
if dialog.choose_finish(task) == "delete":
|
||||
self.delete_model(model_name)
|
||||
self.model_manager.remove_local_model(model_name)
|
||||
|
||||
def delete_model(self, model_name):
|
||||
dialog = Adw.AlertDialog(
|
||||
heading=_("Delete Model?"),
|
||||
body=_("Are you sure you want to delete '{}'?").format(model_name),
|
||||
body=_("Are you sure you want to delete '{}'?").format(self.convert_model_name(model_name, 0)),
|
||||
close_response="cancel"
|
||||
)
|
||||
dialog.add_response("cancel", _("Cancel"))
|
||||
|
@ -31,3 +31,6 @@
|
||||
stacksidebar {
|
||||
border: none;
|
||||
}
|
||||
.image_recognition_indicator {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
223
src/window.py
223
src/window.py
@ -116,9 +116,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
||||
model_link_button = Gtk.Template.Child()
|
||||
|
||||
manage_models_dialog = Gtk.Template.Child()
|
||||
pulling_model_list_box = Gtk.Template.Child()
|
||||
local_model_list_box = Gtk.Template.Child()
|
||||
available_model_list_box = Gtk.Template.Child()
|
||||
model_scroller = Gtk.Template.Child()
|
||||
|
||||
chat_list_container = Gtk.Template.Child()
|
||||
chat_list_box = None
|
||||
@ -284,7 +282,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
||||
for line in modelfile_raw.split('\n'):
|
||||
if not line.startswith('SYSTEM') and not line.startswith('FROM'):
|
||||
modelfile.append(line)
|
||||
self.pulling_model_list_box.set_visible(True)
|
||||
#self.pulling_model_list_box.set_visible(True)
|
||||
model_row = Adw.ActionRow(
|
||||
title = name
|
||||
)
|
||||
@ -309,7 +307,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
||||
self.pulling_models[name] = {"row": model_row, "progress_bar": progress_bar, "overlay": overlay}
|
||||
overlay.set_child(model_row)
|
||||
overlay.add_overlay(progress_bar)
|
||||
self.pulling_model_list_box.append(overlay)
|
||||
#self.pulling_model_list_box.append(overlay)
|
||||
self.navigation_view_manage_models.pop()
|
||||
thread.start()
|
||||
|
||||
@ -334,22 +332,21 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
||||
@Gtk.Template.Callback()
|
||||
def model_search_toggle(self, button):
|
||||
self.model_searchbar.set_search_mode(button.get_active())
|
||||
self.pulling_model_list_box.set_visible(not button.get_active() and len(self.pulling_models) > 0)
|
||||
self.local_model_list_box.set_visible(not button.get_active())
|
||||
self.model_manager.pulling_list.set_visible(not button.get_active() and len(list(self.model_manager.pulling_list)) > 0)
|
||||
self.model_manager.local_list.set_visible(not button.get_active() and len(list(self.model_manager.local_list)) > 0)
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def model_search_changed(self, entry):
|
||||
results = 0
|
||||
for i, key in enumerate(self.available_models.keys()):
|
||||
row = self.available_model_list_box.get_row_at_index(i)
|
||||
row.set_visible(re.search(entry.get_text(), '{} {} {}'.format(row.get_title(), (_("image") if self.available_models[key]['image'] else " "), row.get_subtitle()), re.IGNORECASE))
|
||||
if row.get_visible():
|
||||
for model in list(self.model_manager.available_list):
|
||||
model.set_visible(re.search(entry.get_text(), '{} {} {} {} {}'.format(model.get_name(), model.model_title, model.model_author, model.model_description, (_('image') if model.image_recognition else '')), re.IGNORECASE))
|
||||
if model.get_visible():
|
||||
results += 1
|
||||
if entry.get_text() and results == 0:
|
||||
self.available_model_list_box.set_visible(False)
|
||||
self.no_results_page.set_visible(True)
|
||||
self.model_scroller.set_visible(False)
|
||||
else:
|
||||
self.available_model_list_box.set_visible(True)
|
||||
self.model_scroller.set_visible(True)
|
||||
self.no_results_page.set_visible(False)
|
||||
|
||||
def verify_if_image_can_be_used(self):
|
||||
@ -514,38 +511,6 @@ Generate a title following these rules:
|
||||
new_chat_name = new_chat_name[:50] + (new_chat_name[50:] and '...')
|
||||
self.chat_list_box.rename_chat(old_chat_name, new_chat_name)
|
||||
|
||||
def update_list_local_models(self):
|
||||
logger.debug("Updating list of local models")
|
||||
response = connection_handler.simple_get(f"{connection_handler.URL}/api/tags")
|
||||
self.model_selector.clear_list()
|
||||
|
||||
if response.status_code == 200:
|
||||
self.local_model_list_box.remove_all()
|
||||
if len(json.loads(response.text)['models']) == 0:
|
||||
self.local_model_list_box.set_visible(False)
|
||||
else:
|
||||
self.local_model_list_box.set_visible(True)
|
||||
for model in json.loads(response.text)['models']:
|
||||
model_name = self.convert_model_name(model["name"], 0)
|
||||
model_row = Adw.ActionRow(
|
||||
title = "<b>{}</b>".format(model_name.split(" (")[0]),
|
||||
subtitle = model_name.split(" (")[1][:-1]
|
||||
)
|
||||
button = Gtk.Button(
|
||||
icon_name = "user-trash-symbolic",
|
||||
vexpand = False,
|
||||
valign = 3,
|
||||
css_classes = ["error", "circular"],
|
||||
tooltip_text = _("Remove '{}'").format(model_name)
|
||||
)
|
||||
button.connect("clicked", lambda button=button, model_name=model["name"]: dialogs.delete_model(self, model_name))
|
||||
model_row.add_suffix(button)
|
||||
self.local_model_list_box.append(model_row)
|
||||
|
||||
self.model_selector.add_model(model["name"])
|
||||
else:
|
||||
self.connection_error()
|
||||
|
||||
def save_server_config(self):
|
||||
with open(os.path.join(self.config_dir, "server.json"), "w+", encoding="utf-8") as f:
|
||||
json.dump({'remote_url': self.remote_url, 'remote_bearer_token': self.remote_bearer_token, 'run_remote': self.run_remote, 'local_port': local_instance.port, 'run_on_background': self.run_on_background, 'model_tweaks': self.model_tweaks, 'ollama_overrides': local_instance.overrides}, f, indent=6)
|
||||
@ -555,7 +520,7 @@ Generate a title following these rules:
|
||||
response = connection_handler.simple_get(f"{connection_handler.URL}/api/tags")
|
||||
if response.status_code == 200:
|
||||
self.save_server_config()
|
||||
self.update_list_local_models()
|
||||
#self.update_list_local_models()
|
||||
return response.status_code == 200
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
@ -613,144 +578,6 @@ Generate a title following these rules:
|
||||
GLib.idle_add(chat.show_regenerate_button, message_element)
|
||||
GLib.idle_add(self.connection_error)
|
||||
|
||||
def pull_model_update(self, data, model_name):
|
||||
if 'error' in data:
|
||||
self.pulling_models[model_name]['error'] = data['error']
|
||||
return
|
||||
if model_name in self.pulling_models.keys():
|
||||
if 'completed' in data and 'total' in data:
|
||||
GLib.idle_add(self.pulling_models[model_name]['row'].set_subtitle, '<tt>{}%</tt>'.format(round(data['completed'] / data['total'] * 100, 2)))
|
||||
GLib.idle_add(self.pulling_models[model_name]['progress_bar'].set_fraction, (data['completed'] / data['total']))
|
||||
else:
|
||||
GLib.idle_add(self.pulling_models[model_name]['row'].set_subtitle, '{}'.format(data['status'].capitalize()))
|
||||
GLib.idle_add(self.pulling_models[model_name]['progress_bar'].pulse)
|
||||
else:
|
||||
if len(list(self.pulling_models.keys())) == 0:
|
||||
GLib.idle_add(self.pulling_model_list_box.set_visible, False)
|
||||
|
||||
def pull_model_process(self, model, modelfile):
|
||||
if modelfile:
|
||||
data = {"name": model, "modelfile": modelfile}
|
||||
response = connection_handler.stream_post(f"{connection_handler.URL}/api/create", data=json.dumps(data), callback=lambda data, model_name=model: self.pull_model_update(data, model_name))
|
||||
else:
|
||||
data = {"name": model}
|
||||
response = connection_handler.stream_post(f"{connection_handler.URL}/api/pull", data=json.dumps(data), callback=lambda data, model_name=model: self.pull_model_update(data, model_name))
|
||||
GLib.idle_add(self.update_list_local_models)
|
||||
|
||||
if response.status_code == 200 and 'error' not in self.pulling_models[model]:
|
||||
GLib.idle_add(self.show_notification, _("Task Complete"), _("Model '{}' pulled successfully.").format(model), Gio.ThemedIcon.new("emblem-ok-symbolic"))
|
||||
GLib.idle_add(self.show_toast, _("Model '{}' pulled successfully.").format(model), self.manage_models_overlay)
|
||||
elif response.status_code == 200 and self.pulling_models[model]['error']:
|
||||
GLib.idle_add(self.show_notification, _("Pull Model Error"), _("Failed to pull model '{}': {}").format(model, self.pulling_models[model]['error']), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
||||
GLib.idle_add(self.show_toast, _("Error pulling '{}': {}").format(model, self.pulling_models[model]['error']), self.manage_models_overlay)
|
||||
else:
|
||||
GLib.idle_add(self.show_notification, _("Pull Model Error"), _("Failed to pull model '{}' due to network error.").format(model), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
||||
GLib.idle_add(self.show_toast, _("Error pulling '{}'").format(model), self.manage_models_overlay)
|
||||
GLib.idle_add(self.manage_models_dialog.close)
|
||||
GLib.idle_add(self.connection_error)
|
||||
|
||||
GLib.idle_add(self.pulling_models[model]['overlay'].get_parent().get_parent().remove, self.pulling_models[model]['overlay'].get_parent())
|
||||
del self.pulling_models[model]
|
||||
if len(list(self.pulling_models.keys())) == 0:
|
||||
GLib.idle_add(self.pulling_model_list_box.set_visible, False)
|
||||
|
||||
def pull_model(self, model):
|
||||
if model in self.pulling_models.keys() or model in self.model_selector.get_model_list() or ":" not in model:
|
||||
return
|
||||
logger.info("Pulling model")
|
||||
self.pulling_model_list_box.set_visible(True)
|
||||
#self.pulling_model_list_box.connect('row_selected', lambda list_box, row: dialogs.stop_pull_model(self, row.get_name()) if row else None) #It isn't working for some reason
|
||||
model_name = self.convert_model_name(model, 0)
|
||||
model_row = Adw.ActionRow(
|
||||
title = "<b>{}</b> <small>{}</small>".format(model_name.split(" (")[0], model_name.split(" (")[1][:-1]),
|
||||
name = model
|
||||
)
|
||||
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": model, "modelfile": None})
|
||||
overlay = Gtk.Overlay()
|
||||
progress_bar = Gtk.ProgressBar(
|
||||
valign = 2,
|
||||
show_text = False,
|
||||
margin_start = 10,
|
||||
margin_end = 10,
|
||||
css_classes = ["osd", "horizontal", "bottom"]
|
||||
)
|
||||
button = Gtk.Button(
|
||||
icon_name = "media-playback-stop-symbolic",
|
||||
vexpand = False,
|
||||
valign = 3,
|
||||
css_classes = ["error", "circular"],
|
||||
tooltip_text = _("Stop Pulling '{}'").format(model_name)
|
||||
)
|
||||
button.connect("clicked", lambda button, model_name=model : dialogs.stop_pull_model(self, model_name))
|
||||
model_row.add_suffix(button)
|
||||
self.pulling_models[model] = {"row": model_row, "progress_bar": progress_bar, "overlay": overlay}
|
||||
overlay.set_child(model_row)
|
||||
overlay.add_overlay(progress_bar)
|
||||
self.pulling_model_list_box.append(overlay)
|
||||
thread.start()
|
||||
|
||||
def confirm_pull_model(self, model_name):
|
||||
logger.debug("Confirming pull model")
|
||||
self.navigation_view_manage_models.pop()
|
||||
self.model_tag_list_box.unselect_all()
|
||||
self.pull_model(model_name)
|
||||
|
||||
def list_available_model_tags(self, model_name):
|
||||
logger.debug("Listing available model tags")
|
||||
self.navigation_view_manage_models.push_by_tag('model_tags_page')
|
||||
self.navigation_view_manage_models.find_page('model_tags_page').set_title(model_name.replace("-", " ").title())
|
||||
self.model_link_button.set_name(self.available_models[model_name]['url'])
|
||||
self.model_link_button.set_tooltip_text(self.available_models[model_name]['url'])
|
||||
self.available_model_list_box.unselect_all()
|
||||
self.model_tag_list_box.remove_all()
|
||||
tags = self.available_models[model_name]['tags']
|
||||
|
||||
for tag_data in tags:
|
||||
if f"{model_name}:{tag_data[0]}" not in self.model_selector.get_model_list():
|
||||
tag_row = Adw.ActionRow(
|
||||
title = tag_data[0],
|
||||
subtitle = tag_data[1],
|
||||
name = f"{model_name}:{tag_data[0]}"
|
||||
)
|
||||
download_icon = Gtk.Image.new_from_icon_name("folder-download-symbolic")
|
||||
tag_row.add_suffix(download_icon)
|
||||
download_icon.update_property([4], [_("Download {}:{}").format(model_name, tag_data[0])])
|
||||
|
||||
gesture_click = Gtk.GestureClick.new()
|
||||
gesture_click.connect("pressed", lambda *_, name=f"{model_name}:{tag_data[0]}" : self.confirm_pull_model(name))
|
||||
|
||||
event_controller_key = Gtk.EventControllerKey.new()
|
||||
event_controller_key.connect("key-pressed", lambda controller, key, *_, name=f"{model_name}:{tag_data[0]}" : self.confirm_pull_model(name) if key in (Gdk.KEY_space, Gdk.KEY_Return) else None)
|
||||
|
||||
tag_row.add_controller(gesture_click)
|
||||
tag_row.add_controller(event_controller_key)
|
||||
|
||||
self.model_tag_list_box.append(tag_row)
|
||||
return True
|
||||
|
||||
def update_list_available_models(self):
|
||||
logger.debug("Updating list of available models")
|
||||
self.available_model_list_box.remove_all()
|
||||
for name, model_info in self.available_models.items():
|
||||
model = Adw.ActionRow(
|
||||
title = "<b>{}</b> <small>by {}</small>".format(name.replace("-", " ").title(), model_info['author']),
|
||||
subtitle = available_models_descriptions.descriptions[name] + ("\n\n<b>{}</b>".format(_("Image Recognition")) if model_info['image'] else ""),
|
||||
name = name
|
||||
)
|
||||
next_icon = Gtk.Image.new_from_icon_name("go-next")
|
||||
next_icon.set_margin_start(5)
|
||||
next_icon.update_property([4], [_("Enter download menu for {}").format(name.replace("-", ""))])
|
||||
model.add_suffix(next_icon)
|
||||
|
||||
gesture_click = Gtk.GestureClick.new()
|
||||
gesture_click.connect("pressed", lambda *_, name=name : self.list_available_model_tags(name))
|
||||
|
||||
event_controller_key = Gtk.EventControllerKey.new()
|
||||
event_controller_key.connect("key-pressed", lambda controller, key, *_, name=name : self.list_available_model_tags(name) if key in (Gdk.KEY_space, Gdk.KEY_Return) else None)
|
||||
|
||||
model.add_controller(gesture_click)
|
||||
model.add_controller(event_controller_key)
|
||||
self.available_model_list_box.append(model)
|
||||
|
||||
def save_history(self, chat:chat_widget.chat=None):
|
||||
logger.debug("Saving history")
|
||||
@ -793,8 +620,7 @@ Generate a title following these rules:
|
||||
selected_chat = list(data['chats'])[0]
|
||||
if len(data['chats'][selected_chat]['messages'].keys()) > 0:
|
||||
last_model_used = data['chats'][selected_chat]['messages'][list(data["chats"][selected_chat]["messages"])[-1]]["model"]
|
||||
self.model_selector.change_model(last_model_used)
|
||||
|
||||
self.model_manager.change_model(last_model_used)
|
||||
for chat_name in data['chats']:
|
||||
self.chat_list_box.append_chat(chat_name)
|
||||
chat_container = self.chat_list_box.get_chat_by_name(chat_name)
|
||||
@ -830,16 +656,6 @@ Generate a title following these rules:
|
||||
self.pulling_models[model_name]['overlay'].get_parent().get_parent().remove(self.pulling_models[model_name]['overlay'].get_parent())
|
||||
del self.pulling_models[model_name]
|
||||
|
||||
def delete_model(self, model_name):
|
||||
logger.debug("Deleting model")
|
||||
response = connection_handler.simple_delete(f"{connection_handler.URL}/api/delete", data={"name": model_name})
|
||||
self.update_list_local_models()
|
||||
if response.status_code == 200:
|
||||
self.show_toast(_("Model deleted successfully"), self.manage_models_overlay)
|
||||
else:
|
||||
self.manage_models_dialog.close()
|
||||
self.connection_error()
|
||||
|
||||
def chat_click_handler(self, gesture, n_press, x, y):
|
||||
chat_row = gesture.get_widget()
|
||||
popover = Gtk.PopoverMenu(
|
||||
@ -1056,8 +872,6 @@ Generate a title following these rules:
|
||||
message_widget.window = self
|
||||
chat_widget.window = self
|
||||
model_widget.window = self
|
||||
self.model_selector = model_widget.model_selector_button()
|
||||
self.header_bar.set_title_widget(self.model_selector)
|
||||
|
||||
self.chat_list_box = chat_widget.chat_list()
|
||||
self.chat_list_container.set_child(self.chat_list_box)
|
||||
@ -1145,5 +959,16 @@ Generate a title following these rules:
|
||||
self.welcome_dialog.present(self)
|
||||
if self.verify_connection() is False:
|
||||
self.connection_error()
|
||||
self.update_list_available_models()
|
||||
self.model_manager = model_widget.model_manager_container()
|
||||
self.model_scroller.set_child(self.model_manager)
|
||||
self.model_manager.update_local_list()
|
||||
self.model_manager.update_available_list()
|
||||
|
||||
"""
|
||||
response = connection_handler.simple_get(f"{connection_handler.URL}/api/tags")
|
||||
self.model_selector.clear_list()
|
||||
|
||||
to update local models
|
||||
"""
|
||||
|
||||
self.load_history()
|
||||
|
@ -440,65 +440,38 @@
|
||||
</object>
|
||||
</child>
|
||||
<property name="content">
|
||||
<object class="GtkScrolledWindow">
|
||||
<object class="GtkBox">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<property name="orientation">1</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="pulling_model_list_box">
|
||||
<property name="visible">false</property>
|
||||
<property name="selection-mode">none</property>
|
||||
<style>
|
||||
<class name="boxed-list"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBox" id="local_model_list_box">
|
||||
<property name="selection-mode">none</property>
|
||||
<style>
|
||||
<class name="boxed-list"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBox" id="available_model_list_box">
|
||||
<property name="selection-mode">none</property>
|
||||
<style>
|
||||
<class name="boxed-list"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwStatusPage" id="no_results_page">
|
||||
<property name="visible">false</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="icon-name">edit-find-symbolic</property>
|
||||
<property name="title" translatable="yes">No Models Found</property>
|
||||
<property name="description" translatable="yes">Try a different search or pull an unlisted model from it's name</property>
|
||||
<object class="GtkScrolledWindow" id="model_scroller">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwStatusPage" id="no_results_page">
|
||||
<property name="visible">false</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="icon-name">edit-find-symbolic</property>
|
||||
<property name="title" translatable="yes">No Models Found</property>
|
||||
<property name="description" translatable="yes">Try a different search or pull an unlisted model from it's name</property>
|
||||
<property name="child">
|
||||
<object class="GtkButton">
|
||||
<property name="tooltip-text">Pull Model From Name</property>
|
||||
<property name="action-name">app.create_model_from_name</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="child">
|
||||
<object class="GtkButton">
|
||||
<property name="tooltip-text">Pull Model From Name</property>
|
||||
<property name="action-name">app.create_model_from_name</property>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Pull Model From Name</property>
|
||||
</object>
|
||||
</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Pull Model From Name</property>
|
||||
</object>
|
||||
</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@ -542,6 +515,9 @@
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="label" translatable="yes">By downloading this model you accept the license agreement available on the model's website.</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
Loading…
x
Reference in New Issue
Block a user