From 4b84c61b488387e17052f81a06c9556582679a92 Mon Sep 17 00:00:00 2001 From: jeffser Date: Fri, 17 Jan 2025 23:47:50 -0600 Subject: [PATCH] Removed model manager 1.0 from codebase --- src/custom_widgets/model_widget.py | 846 ----------------------------- src/meson.build | 1 - src/window.py | 3 +- 3 files changed, 1 insertion(+), 849 deletions(-) delete mode 100644 src/custom_widgets/model_widget.py diff --git a/src/custom_widgets/model_widget.py b/src/custom_widgets/model_widget.py deleted file mode 100644 index 9de0d40..0000000 --- a/src/custom_widgets/model_widget.py +++ /dev/null @@ -1,846 +0,0 @@ -#model_widget.py -""" -Handles the model widget (testing) -""" - -import gi -gi.require_version('Gtk', '4.0') -gi.require_version('GtkSource', '5') -from gi.repository import Gtk, GObject, Gio, Adw, GtkSource, GLib, Gdk, GdkPixbuf -import logging, os, datetime, re, shutil, threading, json, sys, glob, icu, base64 -from ..internal import config_dir, data_dir, cache_dir, source_dir -from .. import available_models_descriptions -from . import dialog_widget - -logger = logging.getLogger(__name__) - -window = None - -available_models = None - -class model_selector_popup(Gtk.Popover): - __gtype_name__ = 'AlpacaModelSelectorPopup' - - def __init__(self): - manage_models_button = Gtk.Button( - tooltip_text=_('Manage Models'), - child=Gtk.Label(label=_('Manage Models'), halign=1), - hexpand=True, - css_classes=['manage_models_button', 'flat'] - ) - manage_models_button.set_action_name("app.manage_models") - manage_models_button.connect("clicked", lambda *_: self.hide()) - self.model_list_box = Gtk.ListBox( - css_classes=['navigation-sidebar', 'model_list_box'], - height_request=0, - selection_mode=1 - ) - container = Gtk.Box( - orientation=1, - spacing=5 - ) - container.append(self.model_list_box) - container.append(Gtk.Separator()) - container.append(manage_models_button) - - scroller = Gtk.ScrolledWindow( - max_content_height=300, - propagate_natural_width=True, - propagate_natural_height=True, - child=container - ) - - super().__init__( - css_classes=['model_popover'], - has_arrow=False, - child=scroller - ) - -class model_selector_row(Gtk.ListBoxRow): - __gtype_name__ = 'AlpacaModelSelectorRow' - - def __init__(self, model_name:str, data:dict): - super().__init__( - child = Gtk.Label( - label=window.convert_model_name(model_name, 0), - halign=1, - hexpand=True - ), - halign=0, - hexpand=True, - name=model_name, - tooltip_text=window.convert_model_name(model_name, 0) - ) - self.data = data - self.image_recognition = 'projector_info' in self.data - self.profile_picture_data = None - picture = window.sql_instance.get_model_picture(self.get_name()) - if picture: - self.profile_picture_data = picture - -class model_selector_button(Gtk.MenuButton): - __gtype_name__ = 'AlpacaModelSelectorButton' - - def __init__(self): - self.popover = model_selector_popup() - self.popover.model_list_box.connect('selected-rows-changed', self.model_changed) - self.popover.model_list_box.connect('row-activated', lambda *_: self.get_popover().hide()) - container = Gtk.Box( - orientation=0, - spacing=5 - ) - self.label = Gtk.Label() - container.append(self.label) - container.append(Gtk.Image.new_from_icon_name("down-symbolic")) - super().__init__( - child=container, - popover=self.popover, - halign=3 - ) - - def change_model(self, model_name:str): - for model_row in list(self.get_popover().model_list_box): - if model_name == model_row.get_name(): - self.get_popover().model_list_box.select_row(model_row) - break - - def model_changed(self, listbox:Gtk.ListBox): - row = listbox.get_selected_row() - if row: - model_name = row.get_name() - self.label.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: - #window.title_stack.set_visible_child_name('no_models') - window.model_manager.verify_if_image_can_be_used() - - def add_model(self, model_name:str, data:dict): - model_row = model_selector_row(model_name, data) - GLib.idle_add(self.get_popover().model_list_box.append, model_row) - GLib.idle_add(self.change_model, model_name) - #GLib.idle_add(window.title_stack.set_visible_child_name, 'model_selector') - - 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) - #window.title_stack.set_visible_child_name('model_selector' if len(window.model_manager.get_model_list()) > 0 else 'no_models') - window.sql_instance.delete_model_picture(self.get_name()) - window.chat_list_box.update_profile_pictures() - - def get_model_by_name(self, model_name:str) -> object: - return next((model for model in list(self.get_popover().model_list_box) if model.get_name() == model_name), None) - -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) - - stop_button = Gtk.Button( - icon_name = "media-playback-stop-symbolic", - vexpand = False, - valign = 3, - css_classes = ["error", "circular"], - tooltip_text = _("Stop Pulling '{}'").format(window.convert_model_name(model_name, 0)) - ) - stop_button.connect('clicked', lambda *i: dialog_widget.simple( - _('Stop Download?'), - _("Are you sure you want to stop pulling '{}'?").format(window.convert_model_name(self.get_name(), 0)), - self.stop, - _('Stop'), - 'destructive' - )) - - 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(stop_button) - - super().__init__( - child=container_box, - name=model_name - ) - self.error = None - self.digests = [] - - def stop(self): - if len(list(self.get_parent())) == 1: - self.get_parent().set_visible(False) - self.get_parent().remove(self) - - def update(self, data): - if 'digest' in data and data['digest'] not in self.digests: - self.digests.append(data['digest'].replace(':', '-')) - if not self.get_parent(): - logger.info("Pulling of '{}' was canceled".format(self.get_name())) - directory = os.path.join(window.ollama_instance.model_directory, 'blobs') - for digest in self.digests: - files_to_delete = glob.glob(os.path.join(directory, digest + '*')) - for file in files_to_delete: - logger.info("Deleting '{}'".format(file)) - try: - os.remove(file) - except Exception as e: - logger.error(f"Can't delete file {file}: {e}") - sys.exit() - if 'error' in data: - self.error = data['error'] - else: - 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 information_bow(Gtk.Box): - __gtype_name__ = 'AlpacaModelInformationBow' - - def __init__(self, title:str, subtitle:str): - self.title = title - self.subtitle = subtitle - title_label = Gtk.Label( - label=self.title, - css_classes=['subtitle', 'caption', 'dim-label'], - hexpand=True, - margin_top=10, - margin_start=0, - margin_end=0 - ) - subtitle_label = Gtk.Label( - label=self.subtitle if self.subtitle else '(none)', - css_classes=['heading'], - hexpand=True, - margin_bottom=10, - margin_start=0, - margin_end=0 - ) - super().__init__( - spacing=5, - orientation=1, - css_classes=['card'] - ) - self.append(title_label) - self.append(subtitle_label) - -class category_pill(Gtk.Button): - __gtype_name__ = 'AlpacaCategoryPill' - - metadata = { - 'multilingual': {'name': _('Multilingual'), 'css': ['accent'], 'icon': 'language-symbolic'}, - 'code': {'name': _('Code'), 'css': ['accent'], 'icon': 'code-symbolic'}, - 'math': {'name': _('Math'), 'css': ['accent'], 'icon': 'accessories-calculator-symbolic'}, - 'vision': {'name': _('Vision'), 'css': ['accent'], 'icon': 'eye-open-negative-filled-symbolic'}, - 'embedding': {'name': _('Embedding'), 'css': ['error'], 'icon': 'brain-augemnted-symbolic'}, - 'small': {'name': _('Small'), 'css': ['success'], 'icon': 'leaf-symbolic'}, - 'medium': {'name': _('Medium'), 'css': ['success'], 'icon': 'sprout-symbolic'}, - 'big': {'name': _('Big'), 'css': ['warning'], 'icon': 'tree-circle-symbolic'}, - 'huge': {'name': _('Huge'), 'css': ['error'], 'icon': 'weight-symbolic'}, - 'language': {'css': [], 'icon': 'language-symbolic'} - } - - def __init__(self, name_id:str, show_label:bool): - if 'language:' in name_id: - self.metadata['language']['name'] = name_id.split(':')[1] - name_id = 'language' - button_content = Adw.ButtonContent( - icon_name=self.metadata[name_id]['icon'] - ) - if show_label: - button_content.set_label(self.metadata[name_id]['name']) - super().__init__( - css_classes=['subtitle', 'category_pill'] + self.metadata[name_id]['css'] + (['pill'] if show_label else ['circular']), - tooltip_text=self.metadata[name_id]['name'], - child=button_content, - halign=1 - ) - - -class local_model(Gtk.ListBoxRow): - __gtype_name__ = 'AlpacaLocalModel' - - def __init__(self, model_name:str, categories:list): - model_title = window.convert_model_name(model_name, 0) - self.categories = categories - 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) - - info_button = Gtk.Button( - icon_name = "info-outline-symbolic", - vexpand = False, - valign = 3, - css_classes = ["circular"], - tooltip_text = _("Details") - ) - - info_button.connect('clicked', self.show_information) - - delete_button = Gtk.Button( - icon_name = "user-trash-symbolic", - vexpand = False, - valign = 3, - css_classes = ["error", "circular"], - tooltip_text = _("Remove '{}'").format(window.convert_model_name(model_name, 0)) - ) - - delete_button.connect('clicked', lambda *i: dialog_widget.simple( - _('Delete Model?'), - _("Are you sure you want to delete '{}'?").format(model_title), - lambda model_name=model_name: window.model_manager.remove_local_model(model_name), - _('Delete'), - 'destructive' - )) - - 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(info_button) - container_box.append(delete_button) - - super().__init__( - child=container_box, - name=model_name - ) - - def change_pfp(self, file_dialog, result, button, model): - file = file_dialog.open_finish(result) - if file: - model.profile_picture_data = window.get_content_of_file(file.get_path(), 'image') - image_data = base64.b64decode(model.profile_picture_data) - loader = GdkPixbuf.PixbufLoader.new() - loader.write(image_data) - loader.close() - pixbuf = loader.get_pixbuf() - texture = Gdk.Texture.new_for_pixbuf(pixbuf) - image = Gtk.Image.new_from_paintable(texture) - image.set_size_request(64, 64) - button.set_overflow(1) - button.set_child(image) - window.sql_instance.insert_or_update_model_picture(self.get_name(), model.profile_picture_data) - window.chat_list_box.update_profile_pictures() - - def remove_pfp(self, button, model): - window.sql_instance.delete_model_picture(self.get_name()) - #button.remove(button.get_child()) - button.set_icon_name('image-x-generic-symbolic') - model.profile_picture_data = None - window.chat_list_box.update_profile_pictures() - - def pfp_button_pressed(self, button, model): - file_filter = Gtk.FileFilter() - file_filter.add_suffix('png') - file_filter.add_suffix('jpg') - file_filter.add_suffix('jpeg') - file_filter.add_suffix('webp') - if model.profile_picture_data: - options = { - _('Cancel'): {'callback': None}, - _('Remove'): {'callback': lambda button=button, model=model: self.remove_pfp(button, model), 'appearance': 'destructive'}, - _('Change'): {'callback': lambda button=button, model=model: Gtk.FileDialog(default_filter=file_filter).open(window, None, lambda file_dialog, result, button=button, model=model: self.change_pfp(file_dialog, result, button, model)), 'appearance': 'suggested'}, - } - dialog_widget.Options(_("Model Profile Picture"), _("What do you want to do with the model's profile picture?"), list(options.keys())[0], options) - else: - Gtk.FileDialog(default_filter=file_filter).open(window, None, lambda file_dialog, result, button=button, model=model: self.change_pfp(file_dialog, result, button, model)) - - def show_information(self, button): - model = next((element for element in list(window.model_manager.model_selector.get_popover().model_list_box) if element.get_name() == self.get_name()), None) - model_name = model.get_child().get_label() - window.model_detail_create_button.set_name(model_name) - window.model_detail_create_button.set_tooltip_text(_("Create Model Based on '{}'").format(model_name)) - - actionrow = Adw.ActionRow( - title="{}".format(' ('.join(model_name.split(' (')[:-1])), - subtitle=' ('.join(model_name.split(' (')[-1:])[:-1], - css_classes=["card"] - ) - pfp_button = Gtk.Button( - css_classes=['circular'], - valign=3, - icon_name='image-x-generic-symbolic', - width_request=64, - height_request=64, - margin_top=10, - margin_bottom=10, - tooltip_text=_("Change Model Picture") - ) - if model.profile_picture_data: - image_data = base64.b64decode(model.profile_picture_data) - loader = GdkPixbuf.PixbufLoader.new() - loader.write(image_data) - loader.close() - pixbuf = loader.get_pixbuf() - texture = Gdk.Texture.new_for_pixbuf(pixbuf) - image = Gtk.Image.new_from_paintable(texture) - image.set_size_request(64, 64) - pfp_button.set_overflow(1) - pfp_button.set_child(image) - - pfp_button.connect('clicked', lambda button, model=model: self.pfp_button_pressed(button, model)) - - actionrow.add_prefix(pfp_button) - window.model_detail_header.set_child(actionrow) - - translation_strings={ - 'modified_at': _('Modified At'), - 'parent_model': _('Parent Model'), - 'format': _('Format'), - 'family': _('Family'), - 'parameter_size': _('Parameter Size'), - 'quantization_level': _('Quantization Level') - } - window.model_detail_system.set_label(model.data['system'] if 'system' in model.data else '') - window.model_detail_information.remove_all() - if 'modified_at' in model.data and model.data['modified_at']: - window.model_detail_information.append(information_bow( - title=translation_strings['modified_at'], - subtitle=datetime.datetime.strptime(':'.join(model.data['modified_at'].split(':')[:2]), '%Y-%m-%dT%H:%M').strftime('%Y-%m-%d %H:%M') - )) - - for name, value in model.data['details'].items(): - if isinstance(value, str): - window.model_detail_information.append(information_bow( - title=translation_strings[name] if name in translation_strings else name.replace('_', ' ').title(), - subtitle=value - )) - window.model_detail_categories.remove_all() - languages = ['en'] - if model.get_name().split(':')[0] in available_models: - languages = available_models[model.get_name().split(':')[0]]['languages'] - - for category in self.categories + ['language:' + icu.Locale(lan).getDisplayLanguage(icu.Locale(lan)).title() for lan in languages]: - window.model_detail_categories.append(category_pill(category, True)) - - if 'multilingual' in self.categories and len(languages) == 1: - window.model_tag_flow_box.append(category_pill('language:Others...', True)) - - window.navigation_view_manage_models.push_by_tag('model_information') - -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, categories:list): - model = local_model(model_name, categories) - GLib.idle_add(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, categories:list): - self.model_description = model_description - self.model_title = model_name.replace("-", " ").title() - self.model_author = model_author - self.categories = categories - self.image_recognition = 'vision' in categories - model_label = Gtk.Label( - css_classes=["heading"], - label="{} by {}".format(self.model_title, self.model_author), - hexpand=True, - halign=1, - use_markup=True, - wrap=True, - wrap_mode=0 - ) - description_label = Gtk.Label( - css_classes=["subtitle"], - label=self.model_description, - hexpand=True, - halign=1, - wrap=True, - wrap_mode=0, - ) - categories_box = Gtk.FlowBox( - hexpand=True, - vexpand=True, - orientation=0, - selection_mode=0 - ) - description_box = Gtk.Box( - hexpand=True, - vexpand=True, - spacing=5, - orientation=1 - ) - description_box.append(model_label) - description_box.append(description_label) - description_box.append(categories_box) - - for category in self.categories: - categories_box.append(category_pill(category, False)) - - #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): - threading.Thread(target=window.model_manager.pull_model, args=(model_name,)).start() - window.navigation_view_manage_models.pop() - - def pull_model(self, model_name): - global available_models - rematch = re.search(r':(\d*\.?\d*)([bBmM])', model_name) - parameter_size = 0 - if rematch: - number = float(rematch.group(1)) - suffix = rematch.group(2).lower() - parameter_size = number * 1e9 if suffix == 'b' else number * 1e6 - parameter_size *= 2 - if model_name.split(':')[0] in available_models and 'embedding' in available_models[model_name.split(':')[0]]['categories']: - dialog_widget.simple( - _('Embedding Model'), - _("This model is meant to be used in the training of other models and won't work directly with Alpaca. Are you sure you want to download it anyway?"), - lambda name=model_name: self.confirm_pull_model(name), - _('Download'), - 'destructive' - ) - elif parameter_size > 50000000000: - dialog_widget.simple( - _('Large Model'), - _("This model might be too large to run optimally. Are you sure you want to download it anyway?"), - lambda name=model_name: self.confirm_pull_model(name), - _('Download'), - 'destructive' - ) - else: - self.confirm_pull_model(model_name) - - def show_pull_menu(self): - global available_models - 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(available_models[self.get_name()]['url']) - window.model_link_button.set_tooltip_text(available_models[self.get_name()]['url']) - window.model_tag_list_box.remove_all() - tags = available_models[self.get_name()]['tags'] - window.model_tag_flow_box.remove_all() - languages = ['en'] - if self.get_name() in available_models: - languages = available_models[self.get_name()]['languages'] - - for category in self.categories + ['language:' + icu.Locale(lan).getDisplayLanguage(icu.Locale(lan)).title() for lan in languages]: - window.model_tag_flow_box.append(category_pill(category, True)) - - if 'multilingual' in self.categories and len(languages) == 1: - window.model_tag_flow_box.append(category_pill('language:' + _('Others...'), True)) - - for tag_data in tags: - tag_row = Adw.ActionRow( - title = tag_data[0], - subtitle = tag_data[1], - name = f"{self.get_name()}:{tag_data[0]}", - sensitive = f"{self.get_name()}:{tag_data[0]}" not in window.model_manager.get_model_list() - ) - download_icon = Gtk.Image.new_from_icon_name("folder-download-symbolic" if f"{self.get_name()}:{tag_data[0]}" not in window.model_manager.get_model_list() else "check-plain-symbolic") - tag_row.add_suffix(download_icon) - download_icon.update_property([4], [_("Download {}:{}").format(self.get_name(), tag_data[0])]) - - if f"{self.get_name()}:{tag_data[0]}" not in window.model_manager.get_model_list(): - gesture_click = Gtk.GestureClick.new() - gesture_click.connect("pressed", lambda *_, name=f"{self.get_name()}:{tag_data[0]}" : self.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, categories:list): - model = available_model(model_name, model_author, model_description, categories) - 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.title_stack.add_named(self.model_selector, 'model_selector') - global available_models - try: - with open(os.path.join(source_dir, 'available_models.json'), 'r', encoding="utf-8") as f: - available_models = json.load(f) - except Exception as e: - available_models = {} - - def add_local_model(self, model_name:str): - data = None - categories = [] - try: - response = window.ollama_instance.request("POST", "api/show", json.dumps({"name": model_name})) - data = json.loads(response.text) - except Exception as e: - data = None - - if model_name.split(':')[0] in available_models: # Same name in available models, extract categories - categories = available_models[model_name.split(':')[0]]['categories'] - elif data and data['details']['parent_model'].split(':')[0] in available_models: - categories = available_models[data['details']['parent_model'].split(':')[0]]['categories'] - - self.local_list.add_model(model_name, [cat for cat in categories if cat not in ['small', 'medium', 'big', 'huge']]) - if not self.local_list.get_visible(): - self.local_list.set_visible(True) - self.model_selector.add_model(model_name, data) - GLib.idle_add(window.default_model_list.append, window.convert_model_name(model_name, 0)) - - def remove_local_model(self, model_name:str): - logger.debug("Deleting model") - response = window.ollama_instance.request("DELETE", "api/delete", json.dumps({"name": model_name})) - - if response.status_code == 200: - self.local_list.remove_model(model_name) - self.model_selector.remove_model(model_name) - try: - window.default_model_list.remove(window.default_model_list.find(model_name)) - except: - pass - if len(self.get_model_list()) == 0: - self.local_list.set_visible(False) - window.chat_list_box.update_welcome_screens() - 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): - try: - response = window.ollama_instance.request("GET", "api/tags") - if response.status_code == 200: - threads = [] - GLib.idle_add(self.model_selector.popover.model_list_box.remove_all) - GLib.idle_add(self.local_list.remove_all) - window.default_model_list.splice(0, len(list(window.default_model_list)), None) - data = json.loads(response.text) - GLib.idle_add(window.chat_list_box.update_welcome_screens) - if len(data['models']) == 0: - GLib.idle_add(self.local_list.set_visible, False) - else: - GLib.idle_add(self.local_list.set_visible, True) - for model in data['models']: - thread = threading.Thread(target=self.add_local_model, args=(model['name'], )) - thread.start() - threads.append(thread) - for thread in threads: - thread.join() - else: - window.connection_error() - except Exception as e: - logger.error(e) - window.connection_error() - #window.title_stack.set_visible_child_name('model_selector' if len(window.model_manager.get_model_list()) > 0 else 'no_models') - GLib.idle_add(window.chat_list_box.update_profile_pictures) - - #Should only be called when the app starts - def update_available_list(self): - global available_models - for name, model_info in available_models.items(): - if 'small' in model_info['categories'] or 'medium' in model_info['categories'] or 'big' in model_info['categories'] or os.getenv('ALPACA_SHOW_HUGE_MODELS', '0') == '1': - if 'embedding' not in model_info['categories'] or os.getenv('ALPACA_SHOW_EMBEDDING_MODELS', '0') == '1': - self.available_list.add_model(name, model_info['author'], available_models_descriptions.descriptions[name], model_info['categories']) - - def change_model(self, model_name:str): - self.model_selector.change_model(model_name) - - def verify_if_image_can_be_used(self): - logger.debug("Verifying if image can be used") - selected = self.model_selector.get_popover().model_list_box.get_selected_row() - if selected and selected.image_recognition: - for name, content in window.attachments.items(): - if content['type'] == 'image': - content['button'].set_css_classes(["flat"]) - return True - elif selected: - for name, content in window.attachments.items(): - if content['type'] == 'image': - content['button'].set_css_classes(["flat", "error"]) - - def pull_model(self, model_name:str, modelfile:str=None): - if ':' not in model_name: - model_name += ':latest' - if model_name not in [model.get_name() for model in list(self.pulling_list)] and model_name not in [model.get_name() for model in list(self.local_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 = window.ollama_instance.request("POST", "api/create", json.dumps({"name": model_name, "modelfile": modelfile}), lambda data: model.update(data)) - else: - response = window.ollama_instance.request("POST", "api/pull", json.dumps({"name": model_name}), 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_name), 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_name, 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_name), window.manage_models_overlay) - GLib.idle_add(window.manage_models_dialog.close) - GLib.idle_add(window.connection_error) - - self.pulling_list.remove(model) - GLib.idle_add(window.chat_list_box.update_welcome_screens) - if len(list(self.pulling_list)) == 0: - GLib.idle_add(self.pulling_list.set_visible, False) diff --git a/src/meson.build b/src/meson.build index a36d92f..68c132e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -51,7 +51,6 @@ custom_widgets = [ 'custom_widgets/table_widget.py', 'custom_widgets/message_widget.py', 'custom_widgets/chat_widget.py', - 'custom_widgets/model_widget.py', 'custom_widgets/terminal_widget.py', 'custom_widgets/dialog_widget.py', 'custom_widgets/model_manager_widget.py' diff --git a/src/window.py b/src/window.py index f8b5db1..c948507 100644 --- a/src/window.py +++ b/src/window.py @@ -35,7 +35,7 @@ from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf, Spelling, GObject from . import connection_handler, generic_actions, sql_manager -from .custom_widgets import message_widget, chat_widget, model_widget, terminal_widget, dialog_widget, model_manager_widget +from .custom_widgets import message_widget, chat_widget, terminal_widget, dialog_widget, model_manager_widget from .internal import config_dir, data_dir, cache_dir, source_dir logger = logging.getLogger(__name__) @@ -1207,7 +1207,6 @@ def __init__(self, **kwargs): self.message_searchbar.connect('notify::search-mode-enabled', lambda *_: self.message_search_button.set_active(self.message_searchbar.get_search_mode())) message_widget.window = self chat_widget.window = self - model_widget.window = self dialog_widget.window = self terminal_widget.window = self generic_actions.window = self