From 92f1b62f8cbf45a55419b173748e0775e9ef7dd6 Mon Sep 17 00:00:00 2001 From: imbev Date: Wed, 17 Jul 2024 21:49:55 -0500 Subject: [PATCH] Add logging (#142) * Configure logger * Add logging at various points * Add logging to window verify_if_image_can_be_used --- src/local_instance.py | 13 ++++++++--- src/main.py | 13 ++++++++++- src/window.py | 50 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/local_instance.py b/src/local_instance.py index 54c7c51..aae9dbe 100644 --- a/src/local_instance.py +++ b/src/local_instance.py @@ -1,6 +1,10 @@ # local_instance.py import subprocess, os, threading from time import sleep +from logging import getLogger + + +logger = getLogger(__name__) instance = None port = 11435 @@ -13,19 +17,22 @@ def start(): params["OLLAMA_HOST"] = f"127.0.0.1:{port}" # You can't change this directly sorry :3 params["HOME"] = data_dir instance = subprocess.Popen(["/app/bin/ollama", "serve"], env={**os.environ, **params}, stderr=subprocess.PIPE, text=True) - print("Starting Alpaca's Ollama instance...") + logger.info("Starting Alpaca's Ollama instance...") + logger.debug(params) sleep(1) - print("Started Alpaca's Ollama instance") + logger.info("Started Alpaca's Ollama instance") def stop(): + logger.info("Stopping Alpaca's Ollama instance") global instance if instance: instance.terminate() instance.wait() instance = None - print("Stopped Alpaca's Ollama instance") + logger.info("Stopped Alpaca's Ollama instance") def reset(): + logger.info("Resetting Alpaca's Ollama instance") stop() sleep(1) start() diff --git a/src/main.py b/src/main.py index 2653bc9..14eb291 100644 --- a/src/main.py +++ b/src/main.py @@ -18,6 +18,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later import sys +import logging import gi gi.require_version('Gtk', '4.0') @@ -26,6 +27,10 @@ gi.require_version('Adw', '1') from gi.repository import Gtk, Gio, Adw, GLib from .window import AlpacaWindow + +logger = logging.getLogger(__name__) + + class AlpacaApplication(Adw.Application): """The main application singleton class.""" @@ -35,6 +40,7 @@ class AlpacaApplication(Adw.Application): self.create_action('quit', lambda *_: self.quit(), ['q']) self.create_action('preferences', lambda *_: AlpacaWindow.show_preferences_dialog(self.props.active_window), ['p']) self.create_action('about', self.on_about_action) + self.version = '0.9.6.1' def do_activate(self): win = self.props.active_window @@ -47,7 +53,7 @@ class AlpacaApplication(Adw.Application): application_name='Alpaca', application_icon='com.jeffser.Alpaca', developer_name='Jeffry Samuel Eduarte Rojas', - version='0.9.6.1', + version=self.version, developers=['Jeffser https://jeffser.com'], designers=['Jeffser https://jeffser.com', 'Tobias Bernard (App Icon) https://tobiasbernard.com/'], translator_credits='Alex K (Russian) https://github.com/alexkdeveloper\nJeffser (Spanish) https://jeffser.com\nDaimar Stein (Brazilian Portuguese) https://github.com/not-a-dev-stein\nLouis Chauvet-Villaret (French) https://github.com/loulou64490\nCounterFlow64 (Norwegian) https://github.com/CounterFlow64', @@ -64,5 +70,10 @@ class AlpacaApplication(Adw.Application): def main(version): + logging.basicConfig( + format="%(levelname)s\t[%(filename)s | %(funcName)s] %(message)s", + level=logging.INFO + ) app = AlpacaApplication() + logger.info(f"Alpaca version: {app.version}") return app.run(sys.argv) diff --git a/src/window.py b/src/window.py index a9f0801..0e56a41 100644 --- a/src/window.py +++ b/src/window.py @@ -29,6 +29,10 @@ from pypdf import PdfReader from datetime import datetime from . import dialogs, local_instance, connection_handler, available_models_descriptions + +logger = logging.getLogger(__name__) + + @Gtk.Template(resource_path='/com/jeffser/Alpaca/window.ui') class AlpacaWindow(Adw.ApplicationWindow): config_dir = os.getenv("XDG_CONFIG_HOME") @@ -45,10 +49,6 @@ class AlpacaWindow(Adw.ApplicationWindow): gettext.textdomain('com.jeffser.Alpaca') _ = gettext.gettext - logger = logging.getLogger(__name__) - logging.basicConfig(format="%(levelname)s\t[%(filename)s | %(funcName)s] %(message)s") - logger.setLevel(logging.ERROR) - #Variables editing_message = None available_models = None @@ -133,6 +133,7 @@ class AlpacaWindow(Adw.ApplicationWindow): @Gtk.Template.Callback() def verify_if_image_can_be_used(self, pspec=None, user_data=None): + logger.debug("Verifying if image can be used") if self.model_drop_down.get_selected_item() == None: return True selected = self.model_drop_down.get_selected_item().get_string().split(" (")[0].lower() if selected in [key for key, value in self.available_models.items() if value["image"]]: @@ -246,11 +247,13 @@ class AlpacaWindow(Adw.ApplicationWindow): @Gtk.Template.Callback() def manage_models_button_activate(self, button=None): + logger.debug(f"Managing models") self.update_list_local_models() self.manage_models_dialog.present(self) @Gtk.Template.Callback() def welcome_carousel_page_changed(self, carousel, index): + logger.debug("Showing welcome carousel") if index == 0: self.welcome_previous_button.set_sensitive(False) else: self.welcome_previous_button.set_sensitive(True) if index == carousel.get_n_pages()-1: @@ -274,6 +277,7 @@ class AlpacaWindow(Adw.ApplicationWindow): @Gtk.Template.Callback() def chat_changed(self, listbox, row): + logger.debug("Changing selected chat") if row and row.get_child().get_name() != self.chats["selected_chat"]: self.chats["selected_chat"] = row.get_child().get_name() self.load_history_into_chat() @@ -287,6 +291,7 @@ class AlpacaWindow(Adw.ApplicationWindow): @Gtk.Template.Callback() def change_remote_url(self, entry): self.remote_url = entry.get_text() + logger.debug(f"Changing remote url: {self.remote_url}") if self.run_remote: connection_handler.url = self.remote_url if self.verify_connection() == False: @@ -319,9 +324,9 @@ class AlpacaWindow(Adw.ApplicationWindow): @Gtk.Template.Callback() def closing_app(self, user_data): if self.get_hide_on_close(): - print("Hiding app...") + logger.info("Hiding app...") else: - print("Closing app...") + logger.info("Closing app...") local_instance.stop() @Gtk.Template.Callback() @@ -441,6 +446,7 @@ class AlpacaWindow(Adw.ApplicationWindow): def show_toast(self, message:str, overlay): + logger.info(message) toast = Adw.Toast( title=message, timeout=2 @@ -449,12 +455,14 @@ class AlpacaWindow(Adw.ApplicationWindow): def show_notification(self, title:str, body:str, icon:Gio.ThemedIcon=None): if not self.is_active(): + logger.info(f"{title}, {body}") notification = Gio.Notification.new(title) notification.set_body(body) if icon: notification.set_icon(icon) self.get_application().send_notification(None, notification) def delete_message(self, message_element): + logger.debug("Deleting message") id = message_element.get_name() del self.chats["chats"][self.chats["selected_chat"]]["messages"][id] self.chat_container.remove(message_element) @@ -463,12 +471,14 @@ class AlpacaWindow(Adw.ApplicationWindow): self.save_history() def copy_message(self, message_element): + logger.debug("Copying message") id = message_element.get_name() clipboard = Gdk.Display().get_default().get_clipboard() clipboard.set(self.chats["chats"][self.chats["selected_chat"]]["messages"][id]["content"]) self.show_toast(_("Message copied to the clipboard"), self.main_overlay) def edit_message(self, message_element, text_view, button_container): + logger.debug("Editing message") if self.editing_message: self.send_message() button_container.set_visible(False) @@ -489,6 +499,7 @@ class AlpacaWindow(Adw.ApplicationWindow): self.editing_message = {"text_view": text_view, "id": id, "button_container": button_container, "footer": footer} def preview_file(self, file_path, file_type, presend_name): + logger.debug(f"Previewing file: {file_path}") file_path = file_path.replace("{selected_chat}", self.chats["selected_chat"]) content = self.get_content_of_file(file_path, file_type) if presend_name: @@ -548,6 +559,7 @@ class AlpacaWindow(Adw.ApplicationWindow): return messages def generate_chat_title(self, message, label_element): + logger.debug("Generating chat title") prompt = f""" Generate a title following these rules: - The title should be based on the prompt at the end @@ -728,6 +740,7 @@ Generate a title following these rules: self.bot_message_button_container = button_container def update_list_local_models(self): + logger.debug("Updating list of local models") self.local_models = [] response = connection_handler.simple_get(f"{connection_handler.url}/api/tags") for i in range(self.model_string_list.get_n_items() -1, -1, -1): @@ -874,6 +887,7 @@ Generate a title following these rules: self.bot_message_box = None def on_theme_changed(self, manager, dark, buffer): + logger.debug("Theme changed") if manager.get_dark(): source_style = GtkSource.StyleSchemeManager.get_default().get_scheme('Adwaita-dark') else: @@ -881,6 +895,7 @@ Generate a title following these rules: buffer.set_style_scheme(source_style) def on_copy_code_clicked(self, btn, text_buffer): + logger.debug("Copying code") clipboard = Gdk.Display().get_default().get_clipboard() start = text_buffer.get_start_iter() end = text_buffer.get_end_iter() @@ -933,6 +948,7 @@ Generate a title following these rules: self.send_button.set_visible(not self.send_button.get_visible()) def run_message(self, messages, model, id): + logger.debug("Running message") self.bot_message_button_container.set_visible(False) response = connection_handler.stream_post(f"{connection_handler.url}/api/chat", data=json.dumps({"model": model, "messages": messages}), callback=lambda data, id=id: self.update_bot_message(data, id)) GLib.idle_add(self.add_code_blocks) @@ -980,6 +996,7 @@ Generate a title following these rules: GLib.idle_add(self.pulling_model_list_box.set_visible, False) def pull_model(self, model): + logger.info("Pulling model") if model in list(self.pulling_models.keys()) or model in self.local_models: return self.pulling_model_list_box.set_visible(True) @@ -1013,11 +1030,13 @@ Generate a title following these rules: 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.capitalize()) self.model_link_button.set_name(self.available_models[model_name]['url']) @@ -1037,6 +1056,7 @@ Generate a title following these rules: self.model_tag_list_box.append(tag_row) def update_list_available_models(self): + logger.debug("Updating list of available models") self.available_model_list_box.connect('row_selected', lambda list_box, row: self.list_available_model_tags(row.get_name()) if row else None) self.available_model_list_box.remove_all() for name, model_info in self.available_models.items(): @@ -1055,6 +1075,7 @@ Generate a title following these rules: self.available_model_list_box.append(model) def save_history(self): + logger.debug("Saving history") with open(os.path.join(self.data_dir, "chats", "chats.json"), "w+") as f: json.dump(self.chats, f, indent=4) @@ -1071,6 +1092,7 @@ Generate a title following these rules: self.bot_message = None def load_history(self): + logger.debug("Loading history") if os.path.exists(os.path.join(self.data_dir, "chats", "chats.json")): try: with open(os.path.join(self.data_dir, "chats", "chats.json"), "r") as f: @@ -1108,11 +1130,13 @@ Generate a title following these rules: return f"{datetime.today().strftime('%Y%m%d%H%M%S%f')}{uuid.uuid4().hex}" def clear_chat(self): + logger.info("Clearing chat") for widget in list(self.chat_container): self.chat_container.remove(widget) self.chats["chats"][self.chats["selected_chat"]]["messages"] = [] self.save_history() def delete_chat(self, chat_name): + logger.info("Deleting chat") del self.chats['chats'][chat_name] self.chats['order'].remove(chat_name) if os.path.exists(os.path.join(self.data_dir, "chats", chat_name)): @@ -1125,6 +1149,7 @@ Generate a title following these rules: self.chat_list_box.select_row(self.chat_list_box.get_row_at_index(0)) def rename_chat(self, old_chat_name, new_chat_name, label_element): + logger.info(f"Renaming chat \"{old_chat_name}\" -> \"{new_chat_name}\"") new_chat_name = self.generate_numbered_name(new_chat_name, self.chats["chats"].keys()) if self.chats["selected_chat"] == old_chat_name: self.chats["selected_chat"] = new_chat_name self.chats["chats"][new_chat_name] = self.chats["chats"][old_chat_name] @@ -1145,10 +1170,12 @@ Generate a title following these rules: self.new_chat_element(chat_name, True, False) def stop_pull_model(self, model_name): + logger.debug("Stopping model pull") 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: @@ -1205,15 +1232,18 @@ Generate a title following these rules: self.new_chat_element(name, self.chats["selected_chat"] == name, True) def show_preferences_dialog(self): + logger.debug("Showing preferences dialog") self.preferences_dialog.present(self) def connect_remote(self, url): + logger.debug(f"Connecting to remote: {url}") connection_handler.url = url self.remote_url = connection_handler.url self.remote_connection_entry.set_text(self.remote_url) if self.verify_connection() == False: self.connection_error() def connect_local(self): + logger.debug("Connecting to Alpaca's Ollama instance") self.run_remote = False connection_handler.bearer_token = None connection_handler.url = f"http://127.0.0.1:{local_instance.port}" @@ -1222,6 +1252,7 @@ Generate a title following these rules: else: self.remote_connection_switch.set_active(False) def connection_error(self): + logger.error("Connection error") if self.run_remote: dialogs.reconnect_remote(self, connection_handler.url) else: @@ -1229,6 +1260,7 @@ Generate a title following these rules: self.show_toast(_("There was an error with the local Ollama instance, so it has been reset"), self.main_overlay) def connection_switched(self): + logger.debug("Connection switched") new_value = self.remote_connection_switch.get_active() if new_value != self.run_remote: self.run_remote = new_value @@ -1277,6 +1309,7 @@ Generate a title following these rules: ) def export_chat(self, chat_name): + logger.info("Exporting chat") file_dialog = Gtk.FileDialog(initial_name=f"{chat_name}.tar") file_dialog.save(parent=self, cancellable=None, callback=lambda file_dialog, result, chat_name=chat_name: self.on_export_chat(file_dialog, result, chat_name)) @@ -1316,10 +1349,12 @@ Generate a title following these rules: self.show_toast(_("Chat imported successfully"), self.main_overlay) def import_chat(self): + logger.info("Importing chat") file_dialog = Gtk.FileDialog(default_filter=self.file_filter_tar) file_dialog.open(self, None, self.on_chat_imported) def switch_run_on_background(self): + logger.debug("Switching run on background") self.run_on_background = self.background_switch.get_active() self.set_hide_on_close(self.run_on_background) self.verify_connection() @@ -1357,12 +1392,14 @@ Generate a title following these rules: return text def remove_attached_file(self, name): + logger.debug("Removing attached file") button = self.attachments[name]['button'] button.get_parent().remove(button) del self.attachments[name] if len(self.attachments) == 0: self.attachment_box.set_visible(False) def attach_file(self, file_path, file_type): + logger.debug(f"Attaching file: {file_path}") file_name = self.generate_numbered_name(os.path.basename(file_path), self.attachments.keys()) content = self.get_content_of_file(file_path, file_type) if content: @@ -1446,6 +1483,7 @@ Generate a title following these rules: except Exception as e: 'huh' def on_clipboard_paste(self, textview): + logger.debug("Pasting from clipboard") clipboard = Gdk.Display.get_default().get_clipboard() clipboard.read_text_async(None, self.cb_text_received) clipboard.read_texture_async(None, self.cb_image_received)