2024-05-12 18:18:25 -06:00
# window.py
#
2024-06-04 12:07:15 -06:00
# Copyright 2024 Jeffser
2024-05-12 18:18:25 -06:00
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later
import gi
2024-05-16 20:13:18 -06:00
gi . require_version ( ' GtkSource ' , ' 5 ' )
2024-05-17 00:35:34 -06:00
gi . require_version ( ' GdkPixbuf ' , ' 2.0 ' )
from gi . repository import Adw , Gtk , Gdk , GLib , GtkSource , Gio , GdkPixbuf
2024-06-30 16:04:29 -06:00
import json , requests , threading , os , re , base64 , sys , gettext , locale , subprocess , uuid , shutil , tarfile , tempfile
2024-05-21 15:36:24 -06:00
from time import sleep
2024-05-17 00:35:34 -06:00
from io import BytesIO
from PIL import Image
2024-06-21 17:16:59 -06:00
from pypdf import PdfReader
2024-05-12 18:18:25 -06:00
from datetime import datetime
2024-07-07 17:41:01 -06:00
from . import dialogs , local_instance , connection_handler , update_history , available_models_descriptions
2024-05-12 18:18:25 -06:00
@Gtk.Template ( resource_path = ' /com/jeffser/Alpaca/window.ui ' )
class AlpacaWindow ( Adw . ApplicationWindow ) :
2024-05-20 15:25:33 -06:00
config_dir = os . getenv ( " XDG_CONFIG_HOME " )
2024-06-04 12:07:15 -06:00
data_dir = os . getenv ( " XDG_DATA_HOME " )
2024-05-22 18:01:16 -06:00
app_dir = os . getenv ( " FLATPAK_DEST " )
2024-06-01 00:07:34 -06:00
cache_dir = os . getenv ( " XDG_CACHE_HOME " )
2024-05-22 18:01:16 -06:00
2024-05-12 18:18:25 -06:00
__gtype_name__ = ' AlpacaWindow '
2024-05-19 19:06:11 -06:00
localedir = os . path . join ( os . path . abspath ( os . path . dirname ( __file__ ) ) , ' locale ' )
locale . setlocale ( locale . LC_ALL , ' ' )
gettext . bindtextdomain ( ' com.jeffser.Alpaca ' , localedir )
gettext . textdomain ( ' com.jeffser.Alpaca ' )
_ = gettext . gettext
2024-05-12 18:18:25 -06:00
#Variables
2024-06-30 20:45:22 -06:00
editing_message = None
2024-06-25 00:36:02 -06:00
available_models = None
2024-05-21 23:15:32 -06:00
run_on_background = False
remote_url = " "
2024-06-28 21:29:36 -06:00
remote_bearer_token = " "
2024-05-21 17:58:51 -06:00
run_remote = False
2024-05-29 14:24:30 -06:00
model_tweaks = { " temperature " : 0.7 , " seed " : 0 , " keep_alive " : 5 }
2024-05-12 18:18:25 -06:00
local_models = [ ]
2024-05-19 17:09:58 -06:00
pulling_models = { }
2024-06-30 17:11:40 -06:00
chats = { " chats " : { _ ( " New Chat " ) : { " messages " : { } } } , " selected_chat " : " New Chat " , " order " : [ ] }
2024-06-04 12:07:15 -06:00
attachments = { }
2024-05-12 18:18:25 -06:00
2024-06-03 17:01:27 -06:00
#Override elements
override_HSA_OVERRIDE_GFX_VERSION = Gtk . Template . Child ( )
2024-06-04 13:34:48 -06:00
override_CUDA_VISIBLE_DEVICES = Gtk . Template . Child ( )
override_HIP_VISIBLE_DEVICES = Gtk . Template . Child ( )
2024-06-03 17:01:27 -06:00
2024-05-12 18:18:25 -06:00
#Elements
2024-06-01 00:07:34 -06:00
create_model_base = Gtk . Template . Child ( )
create_model_name = Gtk . Template . Child ( )
create_model_system = Gtk . Template . Child ( )
create_model_template = Gtk . Template . Child ( )
create_model_dialog = Gtk . Template . Child ( )
2024-05-29 14:24:30 -06:00
temperature_spin = Gtk . Template . Child ( )
seed_spin = Gtk . Template . Child ( )
keep_alive_spin = Gtk . Template . Child ( )
2024-05-21 17:58:51 -06:00
preferences_dialog = Gtk . Template . Child ( )
2024-05-19 12:10:14 -06:00
shortcut_window : Gtk . ShortcutsWindow = Gtk . Template . Child ( )
2024-05-12 18:18:25 -06:00
bot_message : Gtk . TextBuffer = None
2024-05-16 20:13:18 -06:00
bot_message_box : Gtk . Box = None
bot_message_view : Gtk . TextView = None
2024-06-26 15:35:59 -06:00
bot_message_button_container : Gtk . TextView = None
2024-06-04 12:07:15 -06:00
file_preview_dialog = Gtk . Template . Child ( )
file_preview_text_view = Gtk . Template . Child ( )
2024-05-21 15:36:24 -06:00
welcome_dialog = Gtk . Template . Child ( )
2024-05-24 12:43:25 -06:00
welcome_carousel = Gtk . Template . Child ( )
welcome_previous_button = Gtk . Template . Child ( )
welcome_next_button = Gtk . Template . Child ( )
2024-05-14 00:27:02 -06:00
main_overlay = Gtk . Template . Child ( )
manage_models_overlay = Gtk . Template . Child ( )
2024-05-12 18:18:25 -06:00
chat_container = Gtk . Template . Child ( )
2024-05-13 13:25:46 -06:00
chat_window = Gtk . Template . Child ( )
2024-05-17 00:35:34 -06:00
message_text_view = Gtk . Template . Child ( )
2024-05-12 18:18:25 -06:00
send_button = Gtk . Template . Child ( )
2024-05-29 14:32:57 -06:00
stop_button = Gtk . Template . Child ( )
2024-06-04 12:07:15 -06:00
attachment_container = Gtk . Template . Child ( )
attachment_box = Gtk . Template . Child ( )
file_filter_tar = Gtk . Template . Child ( )
2024-06-01 00:07:34 -06:00
file_filter_gguf = Gtk . Template . Child ( )
2024-06-21 17:16:59 -06:00
file_filter_attachments = Gtk . Template . Child ( )
attachment_button = Gtk . Template . Child ( )
2024-05-12 18:18:25 -06:00
model_drop_down = Gtk . Template . Child ( )
model_string_list = Gtk . Template . Child ( )
2024-06-24 23:13:17 -06:00
chat_right_click_menu = Gtk . Template . Child ( )
2024-06-25 23:32:09 -06:00
model_tag_list_box = Gtk . Template . Child ( )
2024-06-27 22:48:02 -06:00
navigation_view_manage_models = Gtk . Template . Child ( )
2024-06-26 16:03:44 -06:00
file_preview_open_button = Gtk . Template . Child ( )
2024-06-28 16:42:58 -06:00
secondary_menu_button = Gtk . Template . Child ( )
2024-06-29 11:10:31 -06:00
model_searchbar = Gtk . Template . Child ( )
2024-06-30 18:31:10 -06:00
no_results_page = Gtk . Template . Child ( )
2024-06-30 13:28:08 -06:00
model_link_button = Gtk . Template . Child ( )
2024-05-12 18:18:25 -06:00
manage_models_dialog = Gtk . Template . Child ( )
2024-05-19 17:09:58 -06:00
pulling_model_list_box = Gtk . Template . Child ( )
2024-05-18 15:52:50 -06:00
local_model_list_box = Gtk . Template . Child ( )
2024-05-19 17:09:58 -06:00
available_model_list_box = Gtk . Template . Child ( )
2024-05-12 18:18:25 -06:00
2024-05-19 00:17:00 -06:00
chat_list_box = Gtk . Template . Child ( )
add_chat_button = Gtk . Template . Child ( )
2024-05-18 16:46:20 -06:00
loading_spinner = None
2024-05-21 23:15:32 -06:00
background_switch = Gtk . Template . Child ( )
2024-05-21 17:58:51 -06:00
remote_connection_switch = Gtk . Template . Child ( )
remote_connection_entry = Gtk . Template . Child ( )
2024-06-28 21:29:36 -06:00
remote_bearer_token_entry = Gtk . Template . Child ( )
2024-05-21 17:58:51 -06:00
2024-05-25 23:07:51 +02:00
style_manager = Adw . StyleManager ( )
2024-05-25 23:13:25 -06:00
@Gtk.Template.Callback ( )
def verify_if_image_can_be_used ( self , pspec = None , user_data = None ) :
if self . model_drop_down . get_selected_item ( ) == None : return True
2024-06-30 14:43:12 -06:00
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 " ] ] :
2024-06-04 12:07:15 -06:00
for name , content in self . attachments . items ( ) :
if content [ ' type ' ] == ' image ' :
content [ ' button ' ] . set_css_classes ( [ " flat " ] )
2024-05-25 23:13:25 -06:00
return True
else :
2024-06-04 12:07:15 -06:00
for name , content in self . attachments . items ( ) :
if content [ ' type ' ] == ' image ' :
content [ ' button ' ] . set_css_classes ( [ " flat " , " error " ] )
2024-05-25 23:13:25 -06:00
return False
@Gtk.Template.Callback ( )
2024-05-29 14:32:57 -06:00
def stop_message ( self , button = None ) :
if self . loading_spinner : self . chat_container . remove ( self . loading_spinner )
self . toggle_ui_sensitive ( True )
self . switch_send_stop_button ( )
self . bot_message = None
self . bot_message_box = None
self . bot_message_view = None
2024-06-26 15:35:59 -06:00
self . bot_message_button_container = None
2024-05-25 23:13:25 -06:00
2024-05-29 14:32:57 -06:00
@Gtk.Template.Callback ( )
def send_message ( self , button = None ) :
2024-06-30 20:45:22 -06:00
if self . editing_message :
self . editing_message [ " button_container " ] . set_visible ( True )
self . editing_message [ " text_view " ] . set_css_classes ( [ " flat " ] )
2024-07-05 13:22:25 -06:00
self . editing_message [ " text_view " ] . set_cursor_visible ( False )
2024-06-30 20:45:22 -06:00
self . editing_message [ " text_view " ] . set_editable ( False )
buffer = self . editing_message [ " text_view " ] . get_buffer ( )
text = buffer . get_text ( buffer . get_start_iter ( ) , buffer . get_end_iter ( ) , False ) . rstrip ( ' \n ' )
footer = " <small> " + self . editing_message [ " footer " ] + " </small> "
buffer . insert_markup ( buffer . get_end_iter ( ) , footer , len ( footer ) )
self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ self . editing_message [ " id " ] ] [ " content " ] = text
self . editing_message = None
self . save_history ( )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Message edited successfully " ) , self . main_overlay )
2024-06-30 20:45:22 -06:00
2024-06-30 23:24:29 -06:00
if self . bot_message or self . get_focus ( ) not in ( self . message_text_view , self . send_button ) : return
2024-05-29 14:32:57 -06:00
if not self . message_text_view . get_buffer ( ) . get_text ( self . message_text_view . get_buffer ( ) . get_start_iter ( ) , self . message_text_view . get_buffer ( ) . get_end_iter ( ) , False ) : return
2024-06-30 21:52:52 -06:00
current_chat_row = self . chat_list_box . get_selected_row ( )
self . chat_list_box . unselect_all ( )
self . chat_list_box . remove ( current_chat_row )
self . chat_list_box . prepend ( current_chat_row )
self . chat_list_box . select_row ( self . chat_list_box . get_row_at_index ( 0 ) )
self . chats [ ' order ' ] . remove ( self . chats [ ' selected_chat ' ] )
self . chats [ ' order ' ] . insert ( 0 , self . chats [ ' selected_chat ' ] )
2024-07-03 17:43:56 -06:00
self . save_history ( )
2024-06-23 17:34:41 -06:00
current_model = self . model_drop_down . get_selected_item ( ) . get_string ( )
2024-06-28 20:46:31 -06:00
current_model = current_model . replace ( ' ( ' , ' : ' ) [ : - 1 ] . lower ( )
2024-05-29 14:32:57 -06:00
if current_model is None :
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Please select a model before chatting " ) , self . main_overlay )
2024-05-29 14:32:57 -06:00
return
2024-06-04 12:07:15 -06:00
id = self . generate_uuid ( )
attached_images = [ ]
attached_files = { }
can_use_images = self . verify_if_image_can_be_used ( )
for name , content in self . attachments . items ( ) :
if content [ " type " ] == ' image ' and can_use_images : attached_images . append ( name )
2024-06-24 00:18:55 -06:00
else :
2024-06-26 14:26:41 -06:00
attached_files [ name ] = content [ ' type ' ]
2024-06-04 12:07:15 -06:00
if not os . path . exists ( os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id ) ) :
os . makedirs ( os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id ) )
2024-06-26 14:26:41 -06:00
shutil . copy ( content [ ' path ' ] , os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id , name ) )
2024-06-04 12:07:15 -06:00
content [ " button " ] . get_parent ( ) . remove ( content [ " button " ] )
self . attachments = { }
2024-06-04 14:14:09 -06:00
self . attachment_box . set_visible ( False )
2024-06-04 12:07:15 -06:00
#{"path": file_path, "type": file_type, "content": content}
2024-05-29 14:32:57 -06:00
formated_datetime = datetime . now ( ) . strftime ( " % Y/ % m/ %d % H: % M " )
2024-06-04 12:07:15 -06:00
self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ] = {
2024-05-29 14:32:57 -06:00
" role " : " user " ,
" model " : " User " ,
" date " : formated_datetime ,
" content " : self . message_text_view . get_buffer ( ) . get_text ( self . message_text_view . get_buffer ( ) . get_start_iter ( ) , self . message_text_view . get_buffer ( ) . get_end_iter ( ) , False )
2024-06-04 12:07:15 -06:00
}
if len ( attached_images ) > 0 :
self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ] [ ' images ' ] = attached_images
if len ( attached_files . keys ( ) ) > 0 :
self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ] [ ' files ' ] = attached_files
2024-05-29 14:32:57 -06:00
data = {
2024-06-23 17:34:41 -06:00
" model " : current_model ,
2024-06-04 12:07:15 -06:00
" messages " : self . convert_history_to_ollama ( ) ,
2024-05-29 14:32:57 -06:00
" options " : { " temperature " : self . model_tweaks [ " temperature " ] , " seed " : self . model_tweaks [ " seed " ] } ,
" keep_alive " : f " { self . model_tweaks [ ' keep_alive ' ] } m "
}
self . switch_send_stop_button ( )
self . toggle_ui_sensitive ( False )
2024-06-04 12:07:15 -06:00
#self.attachments[name] = {"path": file_path, "type": file_type, "content": content}
2024-07-01 20:15:33 -06:00
raw_message = self . message_text_view . get_buffer ( ) . get_text ( self . message_text_view . get_buffer ( ) . get_start_iter ( ) , self . message_text_view . get_buffer ( ) . get_end_iter ( ) , False )
self . show_message ( raw_message , False , f " \n \n <small> { formated_datetime } </small> " , attached_images , attached_files , id = id )
2024-05-29 14:32:57 -06:00
self . message_text_view . get_buffer ( ) . set_text ( " " , 0 )
self . loading_spinner = Gtk . Spinner ( spinning = True , margin_top = 12 , margin_bottom = 12 , hexpand = True )
self . chat_container . append ( self . loading_spinner )
2024-06-04 12:07:15 -06:00
bot_id = self . generate_uuid ( )
self . show_message ( " " , True , id = bot_id )
2024-05-29 14:32:57 -06:00
2024-06-04 12:07:15 -06:00
thread = threading . Thread ( target = self . run_message , args = ( data [ ' messages ' ] , data [ ' model ' ] , bot_id ) )
2024-05-29 14:32:57 -06:00
thread . start ( )
2024-06-28 16:30:47 -06:00
if len ( data [ ' messages ' ] ) == 1 :
2024-07-01 20:15:33 -06:00
message_data = data [ " messages " ] [ 0 ] . copy ( )
message_data [ ' content ' ] = raw_message
generate_title_thread = threading . Thread ( target = self . generate_chat_title , args = ( message_data , self . chat_list_box . get_selected_row ( ) . get_child ( ) ) )
2024-06-28 16:30:47 -06:00
generate_title_thread . start ( )
2024-05-25 23:13:25 -06:00
@Gtk.Template.Callback ( )
def manage_models_button_activate ( self , button = None ) :
self . update_list_local_models ( )
self . manage_models_dialog . present ( self )
@Gtk.Template.Callback ( )
def welcome_carousel_page_changed ( self , carousel , index ) :
if index == 0 : self . welcome_previous_button . set_sensitive ( False )
else : self . welcome_previous_button . set_sensitive ( True )
2024-05-26 14:10:20 -06:00
if index == carousel . get_n_pages ( ) - 1 : self . welcome_next_button . set_label ( _ ( " Close " ) )
else : self . welcome_next_button . set_label ( _ ( " Next " ) )
2024-05-25 23:13:25 -06:00
@Gtk.Template.Callback ( )
def welcome_previous_button_activate ( self , button ) :
self . welcome_carousel . scroll_to ( self . welcome_carousel . get_nth_page ( self . welcome_carousel . get_position ( ) - 1 ) , True )
@Gtk.Template.Callback ( )
def welcome_next_button_activate ( self , button ) :
if button . get_label ( ) == " Next " : self . welcome_carousel . scroll_to ( self . welcome_carousel . get_nth_page ( self . welcome_carousel . get_position ( ) + 1 ) , True )
else :
self . welcome_dialog . force_close ( )
if not self . verify_connection ( ) :
self . connection_error ( )
@Gtk.Template.Callback ( )
def chat_changed ( self , listbox , row ) :
2024-06-30 21:52:52 -06:00
if row and row . get_child ( ) . get_name ( ) != self . chats [ " selected_chat " ] :
self . chats [ " selected_chat " ] = row . get_child ( ) . get_name ( )
2024-05-25 23:13:25 -06:00
self . load_history_into_chat ( )
2024-06-04 12:07:15 -06:00
if len ( self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] . keys ( ) ) > 0 :
2024-05-25 23:13:25 -06:00
for i in range ( self . model_string_list . get_n_items ( ) ) :
2024-06-04 12:07:15 -06:00
if self . model_string_list . get_string ( i ) == self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ list ( self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] . keys ( ) ) [ - 1 ] ] [ " model " ] :
2024-05-25 23:13:25 -06:00
self . model_drop_down . set_selected ( i )
break
2024-07-03 17:43:56 -06:00
self . save_history ( )
2024-05-25 23:13:25 -06:00
@Gtk.Template.Callback ( )
def change_remote_url ( self , entry ) :
self . remote_url = entry . get_text ( )
if self . run_remote :
connection_handler . url = self . remote_url
if self . verify_connection ( ) == False :
entry . set_css_classes ( [ " error " ] )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Failed to connect to server " ) , self . preferences_dialog )
2024-05-25 23:13:25 -06:00
2024-06-28 21:29:36 -06:00
@Gtk.Template.Callback ( )
def change_remote_bearer_token ( self , entry ) :
self . remote_bearer_token = entry . get_text ( )
self . save_server_config ( )
return
if self . remote_url and self . run_remote :
connection_handler . url = self . remote_url
if self . verify_connection ( ) == False :
entry . set_css_classes ( [ " error " ] )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Failed to connect to server " ) , self . preferences_dialog )
2024-06-28 21:29:36 -06:00
2024-05-26 14:10:20 -06:00
@Gtk.Template.Callback ( )
def pull_featured_model ( self , button ) :
action_row = button . get_parent ( ) . get_parent ( ) . get_parent ( )
button . get_parent ( ) . remove ( button )
model = f " { action_row . get_title ( ) . lower ( ) } :latest "
action_row . set_subtitle ( _ ( " Pulling in the background... " ) )
spinner = Gtk . Spinner ( )
spinner . set_spinning ( True )
action_row . add_suffix ( spinner )
action_row . set_sensitive ( False )
self . pull_model ( model )
2024-05-28 11:24:50 -06:00
@Gtk.Template.Callback ( )
def closing_app ( self , user_data ) :
if self . get_hide_on_close ( ) :
print ( " Hiding app... " )
else :
print ( " Closing app... " )
local_instance . stop ( )
2024-05-26 14:10:20 -06:00
2024-05-29 14:24:30 -06:00
@Gtk.Template.Callback ( )
def model_spin_changed ( self , spin ) :
value = spin . get_value ( )
if spin . get_name ( ) != " temperature " : value = round ( value )
else : value = round ( value , 1 )
if self . model_tweaks [ spin . get_name ( ) ] is not None and self . model_tweaks [ spin . get_name ( ) ] != value :
self . model_tweaks [ spin . get_name ( ) ] = value
self . save_server_config ( )
2024-06-01 00:07:34 -06:00
@Gtk.Template.Callback ( )
def create_model_start ( self , button ) :
base = self . create_model_base . get_subtitle ( )
name = self . create_model_name . get_text ( )
system = self . create_model_system . get_text ( )
template = self . create_model_template . get_text ( )
if " / " in base :
modelfile = f " FROM { base } \n SYSTEM { system } \n TEMPLATE { template } "
else :
modelfile = f " FROM { base } \n SYSTEM { system } "
self . pulling_model_list_box . set_visible ( True )
model_row = Adw . ActionRow (
title = name
)
thread = threading . Thread ( target = self . pull_model_process , kwargs = { " model " : name , " modelfile " : modelfile } )
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 ,
2024-06-23 18:00:54 -06:00
css_classes = [ " error " ] ,
2024-06-26 00:38:09 -06:00
tooltip_text = _ ( " Stop Creating ' {} ' " ) . format ( name )
2024-06-01 00:07:34 -06:00
)
button . connect ( " clicked " , lambda button , model_name = name : dialogs . stop_pull_model ( self , model_name ) )
model_row . add_suffix ( button )
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 . create_model_dialog . close ( )
self . manage_models_dialog . present ( self )
thread . start ( )
2024-06-03 16:31:03 -06:00
@Gtk.Template.Callback ( )
2024-06-04 12:55:31 -06:00
def override_changed ( self , entry ) :
name = entry . get_name ( )
value = entry . get_text ( )
2024-06-03 16:31:03 -06:00
if ( not value and name not in local_instance . overrides ) or ( value and value in local_instance . overrides and local_instance . overrides [ name ] == value ) : return
if not value : del local_instance . overrides [ name ]
2024-06-04 12:55:31 -06:00
else : local_instance . overrides [ name ] = value
2024-06-03 16:31:03 -06:00
self . save_server_config ( )
if not self . run_remote : local_instance . reset ( )
2024-06-01 00:07:34 -06:00
2024-06-03 17:01:27 -06:00
@Gtk.Template.Callback ( )
def link_button_handler ( self , button ) :
2024-06-30 22:10:02 -06:00
os . system ( f ' xdg-open " { button . get_name ( ) } " ' . replace ( " {selected_chat} " , self . chats [ " selected_chat " ] ) )
2024-06-03 17:01:27 -06:00
2024-06-29 11:10:31 -06:00
@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 ( ) )
@Gtk.Template.Callback ( )
def model_search_changed ( self , entry ) :
2024-06-30 18:31:10 -06:00
results = 0
2024-06-30 18:16:17 -06:00
for i , key in enumerate ( self . available_models . keys ( ) ) :
2024-06-29 11:10:31 -06:00
row = self . available_model_list_box . get_row_at_index ( i )
2024-06-30 18:16:17 -06:00
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 ) )
2024-06-30 18:31:10 -06:00
if row . 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 )
else :
self . available_model_list_box . set_visible ( True )
self . no_results_page . set_visible ( False )
2024-06-29 11:10:31 -06:00
2024-06-01 00:07:34 -06:00
def check_alphanumeric ( self , editable , text , length , position ) :
new_text = ' ' . join ( [ char for char in text if char . isalnum ( ) or char in [ ' - ' , ' _ ' ] ] )
if new_text != text : editable . stop_emission_by_name ( " insert-text " )
def create_model ( self , model : str , file : bool ) :
name = " "
system = " "
template = " "
if not file :
response = connection_handler . simple_post ( f " { connection_handler . url } /api/show " , json . dumps ( { " name " : model } ) )
if ' text ' in response :
data = json . loads ( response [ ' text ' ] )
for line in data [ ' modelfile ' ] . split ( ' \n ' ) :
if line . startswith ( ' SYSTEM ' ) :
system = line [ len ( ' SYSTEM ' ) : ] . strip ( )
elif line . startswith ( ' TEMPLATE ' ) :
template = line [ len ( ' TEMPLATE ' ) : ] . strip ( )
self . create_model_template . set_sensitive ( False )
name = model . split ( ' : ' ) [ 0 ]
else :
self . create_model_template . set_sensitive ( True )
template = ' " " " {{ if .System }}<|start_header_id|>system<|end_header_id|> \n \n {{ .System }}<|eot_id|> {{ end }} {{ if .Prompt }}<|start_header_id|>user<|end_header_id|> \n \n {{ .Prompt }}<|eot_id|> {{ end }}<|start_header_id|>assistant<|end_header_id|> \n {{ .Response }}<|eot_id|> " " " '
name = model . split ( " / " ) [ - 1 ] . split ( " . " ) [ 0 ]
self . create_model_base . set_subtitle ( model )
self . create_model_name . set_text ( name )
self . create_model_system . set_text ( system )
self . create_model_template . set_text ( template )
self . manage_models_dialog . close ( )
self . create_model_dialog . present ( self )
2024-07-07 20:24:29 -06:00
def show_toast ( self , message : str , overlay ) :
2024-05-12 18:18:25 -06:00
toast = Adw . Toast (
2024-07-07 20:24:29 -06:00
title = message ,
2024-05-12 18:18:25 -06:00
timeout = 2
)
2024-05-14 00:27:02 -06:00
overlay . add_toast ( toast )
2024-05-12 18:18:25 -06:00
2024-06-30 16:56:28 -06:00
def show_notification ( self , title : str , body : str , icon : Gio . ThemedIcon = None ) :
if not self . is_active ( ) :
2024-05-18 17:28:10 -06:00
notification = Gio . Notification . new ( title )
notification . set_body ( body )
if icon : notification . set_icon ( icon )
self . get_application ( ) . send_notification ( None , notification )
2024-05-18 16:46:20 -06:00
2024-05-23 19:06:50 -06:00
def delete_message ( self , message_element ) :
2024-06-04 12:07:15 -06:00
id = message_element . get_name ( )
del self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ]
self . chat_container . remove ( message_element )
if os . path . exists ( os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id ) ) :
shutil . rmtree ( os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id ) )
self . save_history ( )
2024-05-23 19:06:50 -06:00
2024-05-29 21:59:47 -06:00
def copy_message ( self , message_element ) :
2024-06-04 12:07:15 -06:00
id = message_element . get_name ( )
2024-05-29 21:59:47 -06:00
clipboard = Gdk . Display ( ) . get_default ( ) . get_clipboard ( )
2024-06-04 12:07:15 -06:00
clipboard . set ( self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ] [ " content " ] )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Message copied to the clipboard " ) , self . main_overlay )
2024-05-29 21:59:47 -06:00
2024-06-30 20:45:22 -06:00
def edit_message ( self , message_element , text_view , button_container ) :
if self . editing_message : self . send_message ( )
button_container . set_visible ( False )
id = message_element . get_name ( )
text_buffer = text_view . get_buffer ( )
end_iter = text_buffer . get_end_iter ( )
start_iter = end_iter . copy ( )
start_iter . backward_line ( )
start_iter . backward_char ( )
footer = text_buffer . get_text ( start_iter , end_iter , False )
text_buffer . delete ( start_iter , end_iter )
text_view . set_editable ( True )
2024-07-05 13:19:51 -06:00
text_view . set_css_classes ( [ " view " , " editing_message_textview " ] )
2024-07-05 13:22:25 -06:00
text_view . set_cursor_visible ( True )
2024-06-30 20:45:22 -06:00
self . editing_message = { " text_view " : text_view , " id " : id , " button_container " : button_container , " footer " : footer }
2024-06-04 12:07:15 -06:00
def preview_file ( self , file_path , file_type ) :
2024-06-30 22:10:02 -06:00
file_path = file_path . replace ( " {selected_chat} " , self . chats [ " selected_chat " ] )
2024-06-04 12:07:15 -06:00
content = self . get_content_of_file ( file_path , file_type )
2024-06-04 18:53:41 -06:00
if content :
buffer = self . file_preview_text_view . get_buffer ( )
buffer . delete ( buffer . get_start_iter ( ) , buffer . get_end_iter ( ) )
buffer . insert ( buffer . get_start_iter ( ) , content , len ( content ) )
2024-06-24 00:18:55 -06:00
if file_type == ' youtube ' :
2024-06-26 14:26:41 -06:00
self . file_preview_dialog . set_title ( content . split ( ' \n ' ) [ 0 ] )
2024-06-26 16:03:44 -06:00
self . file_preview_open_button . set_name ( content . split ( ' \n ' ) [ 2 ] )
2024-06-30 23:24:29 -06:00
elif file_type == ' website ' :
self . file_preview_open_button . set_name ( content . split ( ' \n ' ) [ 0 ] )
2024-06-24 00:18:55 -06:00
else :
self . file_preview_dialog . set_title ( os . path . basename ( file_path ) )
2024-06-26 16:03:44 -06:00
self . file_preview_open_button . set_name ( file_path )
2024-06-04 18:53:41 -06:00
self . file_preview_dialog . present ( self )
2024-06-04 12:07:15 -06:00
def convert_history_to_ollama ( self ) :
messages = [ ]
for id , message in self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] . items ( ) :
new_message = message . copy ( )
if ' files ' in message and len ( message [ ' files ' ] ) > 0 :
del new_message [ ' files ' ]
new_message [ ' content ' ] = ' '
for name , file_type in message [ ' files ' ] . items ( ) :
2024-06-26 14:26:41 -06:00
file_path = os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id , name )
2024-06-04 18:53:41 -06:00
file_data = self . get_content_of_file ( file_path , file_type )
if file_data : new_message [ ' content ' ] + = f " ```[ { name } ] \n { file_data } \n ``` "
2024-06-04 12:07:15 -06:00
new_message [ ' content ' ] + = message [ ' content ' ]
if ' images ' in message and len ( message [ ' images ' ] ) > 0 :
new_message [ ' images ' ] = [ ]
for name in message [ ' images ' ] :
file_path = os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id , name )
2024-06-04 18:53:41 -06:00
image_data = self . get_content_of_file ( file_path , ' image ' )
if image_data : new_message [ ' images ' ] . append ( image_data )
2024-06-04 12:07:15 -06:00
messages . append ( new_message )
return messages
2024-06-30 16:13:36 -06:00
def generate_chat_title ( self , message , label_element ) :
2024-06-28 16:30:47 -06:00
prompt = f """
Generate a title following these rules :
- The title should be based on the prompt at the end
- Keep it in the same language as the prompt
2024-06-30 23:24:29 -06:00
- The title needs to be less than 30 characters
2024-06-28 16:36:04 -06:00
- Use only alphanumeric characters and spaces
2024-06-30 17:11:40 -06:00
- Just write the title , NOTHING ELSE
2024-06-28 16:30:47 -06:00
` ` ` PROMPT
2024-06-30 16:13:36 -06:00
{ message [ ' content ' ] }
2024-06-28 16:30:47 -06:00
` ` ` """
current_model = self . model_drop_down . get_selected_item ( ) . get_string ( )
2024-06-30 14:43:12 -06:00
current_model = current_model . replace ( ' ( ' , ' : ' ) [ : - 1 ] . lower ( )
2024-06-30 15:06:02 -06:00
data = { " model " : current_model , " prompt " : prompt , " stream " : False }
2024-06-30 16:13:36 -06:00
if ' images ' in message : data [ " images " ] = message [ ' images ' ]
2024-06-30 15:06:02 -06:00
response = connection_handler . simple_post ( f " { connection_handler . url } /api/generate " , data = json . dumps ( data ) )
2024-07-03 20:46:48 -06:00
new_chat_name = json . loads ( response [ ' text ' ] ) [ " response " ] . strip ( ) . removeprefix ( " Title: " ) . removeprefix ( " title: " ) . strip ( ' \' " ' ) . title ( )
2024-07-04 11:43:54 -06:00
new_chat_name = new_chat_name [ : 50 ] + ( new_chat_name [ 50 : ] and ' ... ' )
2024-06-30 21:52:52 -06:00
self . rename_chat ( label_element . get_name ( ) , new_chat_name , label_element )
2024-06-04 12:07:15 -06:00
def show_message ( self , msg : str , bot : bool , footer : str = None , images : list = None , files : dict = None , id : str = None ) :
2024-05-12 18:18:25 -06:00
message_text = Gtk . TextView (
editable = False ,
2024-05-19 12:10:14 -06:00
focusable = True ,
2024-05-12 18:18:25 -06:00
wrap_mode = Gtk . WrapMode . WORD ,
margin_top = 12 ,
margin_bottom = 12 ,
margin_start = 12 ,
margin_end = 12 ,
2024-05-16 20:13:18 -06:00
hexpand = True ,
2024-07-05 13:22:25 -06:00
cursor_visible = False ,
2024-05-22 18:01:16 -06:00
css_classes = [ " flat " ] ,
2024-05-12 18:18:25 -06:00
)
message_buffer = message_text . get_buffer ( )
message_buffer . insert ( message_buffer . get_end_iter ( ) , msg )
2024-05-13 13:25:46 -06:00
if footer is not None : message_buffer . insert_markup ( message_buffer . get_end_iter ( ) , footer , len ( footer ) )
2024-05-22 19:43:33 -06:00
delete_button = Gtk . Button (
icon_name = " user-trash-symbolic " ,
2024-06-24 23:13:17 -06:00
css_classes = [ " flat " , " circular " ] ,
2024-06-26 00:38:09 -06:00
tooltip_text = _ ( " Remove Message " )
2024-05-29 21:59:47 -06:00
)
copy_button = Gtk . Button (
icon_name = " edit-copy-symbolic " ,
2024-06-24 23:13:17 -06:00
css_classes = [ " flat " , " circular " ] ,
2024-06-26 00:38:09 -06:00
tooltip_text = _ ( " Copy Message " )
2024-05-29 21:59:47 -06:00
)
2024-06-30 20:45:22 -06:00
edit_button = Gtk . Button (
icon_name = " edit-symbolic " ,
css_classes = [ " flat " , " circular " ] ,
tooltip_text = _ ( " Edit Message " )
)
2024-05-29 21:59:47 -06:00
button_container = Gtk . Box (
orientation = 0 ,
spacing = 6 ,
margin_end = 6 ,
2024-05-22 19:43:33 -06:00
margin_bottom = 6 ,
2024-05-29 21:59:47 -06:00
valign = " end " ,
halign = " end "
2024-05-22 19:43:33 -06:00
)
2024-05-16 20:13:18 -06:00
message_box = Gtk . Box (
orientation = 1 ,
2024-05-22 18:01:16 -06:00
halign = ' fill ' ,
2024-06-24 23:13:17 -06:00
css_classes = [ None if bot else " card " ]
2024-05-12 18:18:25 -06:00
)
message_text . set_valign ( Gtk . Align . CENTER )
2024-05-22 13:22:46 -06:00
2024-06-04 12:07:15 -06:00
if images and len ( images ) > 0 :
image_container = Gtk . Box (
orientation = 0 ,
2024-07-03 18:01:51 -06:00
spacing = 12
2024-06-04 12:07:15 -06:00
)
image_scroller = Gtk . ScrolledWindow (
margin_top = 10 ,
margin_start = 10 ,
margin_end = 10 ,
hexpand = True ,
height_request = 240 ,
child = image_container
)
for image in images :
2024-06-30 18:58:45 -06:00
path = os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] , id , image )
raw_data = self . get_content_of_file ( path , " image " )
2024-06-04 18:53:41 -06:00
if raw_data :
2024-07-03 18:01:51 -06:00
#image_container.set_visible(True)
2024-06-04 18:53:41 -06:00
image_data = base64 . b64decode ( raw_data )
2024-06-04 18:45:50 -06:00
loader = GdkPixbuf . PixbufLoader . new ( )
loader . write ( image_data )
loader . close ( )
pixbuf = loader . get_pixbuf ( )
texture = Gdk . Texture . new_for_pixbuf ( pixbuf )
2024-06-30 18:58:45 -06:00
image_texture = Gtk . Image . new_from_paintable ( texture )
image_texture . set_size_request ( 240 , 240 )
button = Gtk . Button (
child = image_texture ,
css_classes = [ " flat " , " chat_image_button " ] ,
2024-06-30 22:10:02 -06:00
name = os . path . join ( self . data_dir , " chats " , " {selected_chat} " , id , image ) ,
2024-06-30 18:58:45 -06:00
tooltip_text = os . path . basename ( path )
)
button . connect ( ' clicked ' , self . link_button_handler )
2024-07-03 18:01:51 -06:00
else :
image_texture = Gtk . Image . new_from_icon_name ( " image-missing-symbolic " )
image_texture . set_icon_size ( 2 )
image_texture . set_vexpand ( True )
image_texture . set_pixel_size ( 120 )
image_label = Gtk . Label (
label = _ ( " Missing Image " ) ,
)
image_box = Gtk . Box (
spacing = 10 ,
orientation = 1 ,
margin_top = 10 ,
margin_bottom = 10 ,
margin_start = 10 ,
margin_end = 10
)
image_box . append ( image_texture )
image_box . append ( image_label )
image_box . set_size_request ( 220 , 220 )
button = Gtk . Button (
child = image_box ,
css_classes = [ " flat " , " chat_image_button " ] ,
tooltip_text = _ ( " Missing image " )
)
image_container . append ( button )
2024-06-04 12:07:15 -06:00
message_box . append ( image_scroller )
if files and len ( files ) > 0 :
file_container = Gtk . Box (
orientation = 0 ,
spacing = 12
)
file_scroller = Gtk . ScrolledWindow (
margin_top = 10 ,
margin_start = 10 ,
margin_end = 10 ,
hexpand = True ,
child = file_container
)
for name , file_type in files . items ( ) :
button_content = Adw . ButtonContent (
2024-06-30 19:23:26 -06:00
label = name ,
2024-06-30 23:24:29 -06:00
icon_name = {
" plain_text " : " document-text-symbolic " ,
" pdf " : " document-text-symbolic " ,
" youtube " : " play-symbolic " ,
" website " : " globe-symbolic "
} [ file_type ]
2024-06-04 12:07:15 -06:00
)
button = Gtk . Button (
vexpand = False ,
valign = 3 ,
name = name ,
css_classes = [ " flat " ] ,
2024-06-26 14:26:41 -06:00
tooltip_text = name ,
2024-06-04 12:07:15 -06:00
child = button_content
)
2024-06-30 22:10:02 -06:00
file_path = os . path . join ( self . data_dir , " chats " , " {selected_chat} " , id , name )
2024-06-24 00:18:55 -06:00
button . connect ( " clicked " , lambda button , file_path = file_path , file_type = file_type : self . preview_file ( file_path , file_type ) )
2024-06-04 12:07:15 -06:00
file_container . append ( button )
message_box . append ( file_scroller )
2024-05-17 00:35:34 -06:00
message_box . append ( message_text )
2024-05-29 21:59:47 -06:00
overlay = Gtk . Overlay ( css_classes = [ " message " ] , name = id )
2024-05-29 15:58:09 -06:00
overlay . set_child ( message_box )
2024-05-23 19:06:50 -06:00
2024-05-29 15:58:09 -06:00
delete_button . connect ( " clicked " , lambda button , element = overlay : self . delete_message ( element ) )
2024-05-29 21:59:47 -06:00
copy_button . connect ( " clicked " , lambda button , element = overlay : self . copy_message ( element ) )
2024-06-30 20:45:22 -06:00
edit_button . connect ( " clicked " , lambda button , element = overlay , textview = message_text , button_container = button_container : self . edit_message ( element , textview , button_container ) )
2024-05-29 21:59:47 -06:00
button_container . append ( delete_button )
button_container . append ( copy_button )
2024-06-30 20:45:22 -06:00
if not bot : button_container . append ( edit_button )
2024-05-29 21:59:47 -06:00
overlay . add_overlay ( button_container )
2024-05-29 15:58:09 -06:00
self . chat_container . append ( overlay )
2024-05-17 00:35:34 -06:00
2024-05-16 20:13:18 -06:00
if bot :
self . bot_message = message_buffer
self . bot_message_view = message_text
self . bot_message_box = message_box
2024-06-26 15:35:59 -06:00
self . bot_message_button_container = button_container
2024-05-12 18:18:25 -06:00
def update_list_local_models ( self ) :
self . local_models = [ ]
2024-06-01 00:07:34 -06:00
response = connection_handler . simple_get ( f " { connection_handler . url } /api/tags " )
2024-05-14 00:27:02 -06:00
for i in range ( self . model_string_list . get_n_items ( ) - 1 , - 1 , - 1 ) :
self . model_string_list . remove ( i )
2024-05-12 18:18:25 -06:00
if response [ ' status ' ] == ' ok ' :
2024-05-19 17:09:58 -06:00
self . local_model_list_box . remove_all ( )
2024-05-20 17:36:22 -06:00
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 )
2024-05-12 18:18:25 -06:00
for model in json . loads ( response [ ' text ' ] ) [ ' models ' ] :
2024-05-19 17:09:58 -06:00
model_row = Adw . ActionRow (
2024-07-04 11:56:45 -06:00
title = " <b> {} </b> " . format ( model [ " name " ] . split ( " : " ) [ 0 ] . replace ( " - " , " " ) . title ( ) ) ,
2024-05-19 17:09:58 -06:00
subtitle = model [ " name " ] . split ( " : " ) [ 1 ]
)
button = Gtk . Button (
icon_name = " user-trash-symbolic " ,
vexpand = False ,
valign = 3 ,
2024-06-28 20:46:31 -06:00
css_classes = [ " error " , " circular " ] ,
2024-07-04 11:56:45 -06:00
tooltip_text = _ ( " Remove ' {} ( {} ) ' " ) . format ( model [ " name " ] . split ( " : " ) [ 0 ] . replace ( ' - ' , ' ' ) . title ( ) , model [ " name " ] . split ( " : " ) [ 1 ] )
2024-05-19 17:09:58 -06:00
)
2024-05-25 23:03:26 -06:00
button . connect ( " clicked " , lambda button = button , model_name = model [ " name " ] : dialogs . delete_model ( self , model_name ) )
2024-05-19 17:09:58 -06:00
model_row . add_suffix ( button )
self . local_model_list_box . append ( model_row )
2024-07-04 11:56:45 -06:00
self . model_string_list . append ( f " { model [ ' name ' ] . split ( ' : ' ) [ 0 ] . replace ( ' - ' , ' ' ) . title ( ) } ( { model [ ' name ' ] . split ( ' : ' ) [ 1 ] } ) " )
2024-05-12 18:18:25 -06:00
self . local_models . append ( model [ " name " ] )
self . model_drop_down . set_selected ( 0 )
2024-05-17 00:35:34 -06:00
self . verify_if_image_can_be_used ( )
2024-05-12 18:18:25 -06:00
return
2024-05-13 13:25:46 -06:00
else :
2024-05-21 18:52:56 -06:00
self . connection_error ( )
2024-05-13 13:25:46 -06:00
2024-05-29 14:24:30 -06:00
def save_server_config ( self ) :
with open ( os . path . join ( self . config_dir , " server.json " ) , " w+ " ) as f :
2024-06-28 21:29:36 -06:00
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 )
2024-05-29 14:24:30 -06:00
2024-05-13 13:25:46 -06:00
def verify_connection ( self ) :
2024-05-25 23:03:26 -06:00
response = connection_handler . simple_get ( connection_handler . url )
2024-05-13 13:25:46 -06:00
if response [ ' status ' ] == ' ok ' :
if " Ollama is running " in response [ ' text ' ] :
2024-05-29 14:24:30 -06:00
self . save_server_config ( )
2024-05-13 13:25:46 -06:00
self . update_list_local_models ( )
return True
return False
2024-05-12 18:18:25 -06:00
2024-05-16 20:13:18 -06:00
def add_code_blocks ( self ) :
text = self . bot_message . get_text ( self . bot_message . get_start_iter ( ) , self . bot_message . get_end_iter ( ) , True )
GLib . idle_add ( self . bot_message_view . get_parent ( ) . remove , self . bot_message_view )
# Define a regular expression pattern to match code blocks
code_block_pattern = re . compile ( r ' ```( \ w+) \ n(.*?) \ n``` ' , re . DOTALL )
parts = [ ]
pos = 0
for match in code_block_pattern . finditer ( text ) :
start , end = match . span ( )
if pos < start :
normal_text = text [ pos : start ]
parts . append ( { " type " : " normal " , " text " : normal_text . strip ( ) } )
language = match . group ( 1 )
code_text = match . group ( 2 )
parts . append ( { " type " : " code " , " text " : code_text , " language " : language } )
pos = end
# Extract any remaining normal text after the last code block
if pos < len ( text ) :
normal_text = text [ pos : ]
if normal_text . strip ( ) :
parts . append ( { " type " : " normal " , " text " : normal_text . strip ( ) } )
2024-05-18 19:33:22 -06:00
bold_pattern = re . compile ( r ' \ * \ *(.*?) \ * \ * ' ) #"**text**"
code_pattern = re . compile ( r ' `(.*?)` ' ) #"`text`"
h1_pattern = re . compile ( r ' ^# \ s(.*)$ ' ) #"# text"
h2_pattern = re . compile ( r ' ^## \ s(.*)$ ' ) #"## text"
markup_pattern = re . compile ( r ' <(b|u|tt|span.*)>(.*?)< \ /(b|u|tt|span)> ' ) #heh butt span, I'm so funny
2024-05-16 20:13:18 -06:00
for part in parts :
if part [ ' type ' ] == ' normal ' :
message_text = Gtk . TextView (
editable = False ,
2024-05-19 12:10:14 -06:00
focusable = True ,
2024-05-16 20:13:18 -06:00
wrap_mode = Gtk . WrapMode . WORD ,
margin_top = 12 ,
margin_bottom = 12 ,
hexpand = True ,
css_classes = [ " flat " ]
)
message_buffer = message_text . get_buffer ( )
2024-05-18 18:10:01 -06:00
footer = None
2024-05-17 01:00:32 -06:00
if part [ ' text ' ] . split ( " \n " ) [ - 1 ] == parts [ - 1 ] [ ' text ' ] . split ( " \n " ) [ - 1 ] :
2024-05-28 22:26:36 -06:00
footer = " \n <small> " + part [ ' text ' ] . split ( ' \n ' ) [ - 1 ] + " </small> "
2024-05-17 01:00:32 -06:00
part [ ' text ' ] = ' \n ' . join ( part [ ' text ' ] . split ( " \n " ) [ : - 1 ] )
2024-05-18 18:10:01 -06:00
2024-05-18 19:33:22 -06:00
part [ ' text ' ] = part [ ' text ' ] . replace ( " \n * " , " \n • " )
2024-05-19 00:30:05 -06:00
#part['text'] = GLib.markup_escape_text(part['text'])
2024-05-18 18:10:01 -06:00
part [ ' text ' ] = code_pattern . sub ( r ' <tt> \ 1</tt> ' , part [ ' text ' ] )
part [ ' text ' ] = bold_pattern . sub ( r ' <b> \ 1</b> ' , part [ ' text ' ] )
2024-05-18 19:33:22 -06:00
part [ ' text ' ] = h1_pattern . sub ( r ' <span size= " x-large " > \ 1</span> ' , part [ ' text ' ] )
part [ ' text ' ] = h2_pattern . sub ( r ' <span size= " large " > \ 1</span> ' , part [ ' text ' ] )
position = 0
for match in markup_pattern . finditer ( part [ ' text ' ] ) :
start , end = match . span ( )
if position < start :
message_buffer . insert ( message_buffer . get_end_iter ( ) , part [ ' text ' ] [ position : start ] )
message_buffer . insert_markup ( message_buffer . get_end_iter ( ) , match . group ( 0 ) , len ( match . group ( 0 ) ) )
position = end
if position < len ( part [ ' text ' ] ) :
message_buffer . insert ( message_buffer . get_end_iter ( ) , part [ ' text ' ] [ position : ] )
2024-05-18 18:10:01 -06:00
if footer : message_buffer . insert_markup ( message_buffer . get_end_iter ( ) , footer , len ( footer ) )
2024-05-16 20:13:18 -06:00
self . bot_message_box . append ( message_text )
else :
language = GtkSource . LanguageManager . get_default ( ) . get_language ( part [ ' language ' ] )
2024-05-25 23:07:51 +02:00
if language :
buffer = GtkSource . Buffer . new_with_language ( language )
else :
buffer = GtkSource . Buffer ( )
2024-05-16 20:13:18 -06:00
buffer . set_text ( part [ ' text ' ] )
2024-05-25 23:07:51 +02:00
if self . style_manager . get_dark ( ) :
source_style = GtkSource . StyleSchemeManager . get_default ( ) . get_scheme ( ' Adwaita-dark ' )
else :
source_style = GtkSource . StyleSchemeManager . get_default ( ) . get_scheme ( ' Adwaita ' )
buffer . set_style_scheme ( source_style )
2024-05-16 20:13:18 -06:00
source_view = GtkSource . View (
2024-05-25 23:07:51 +02:00
auto_indent = True , indent_width = 4 , buffer = buffer , show_line_numbers = True ,
top_margin = 6 , bottom_margin = 6 , left_margin = 12 , right_margin = 12
2024-05-16 20:13:18 -06:00
)
2024-05-18 19:48:43 -06:00
source_view . set_editable ( False )
2024-05-25 23:07:51 +02:00
code_block_box = Gtk . Box ( css_classes = [ " card " ] , orientation = 1 , overflow = 1 )
title_box = Gtk . Box ( margin_start = 12 , margin_top = 3 , margin_bottom = 3 , margin_end = 3 )
title_box . append ( Gtk . Label ( label = language . get_name ( ) if language else part [ ' language ' ] , hexpand = True , xalign = 0 ) )
2024-06-26 00:38:09 -06:00
copy_button = Gtk . Button ( icon_name = " edit-copy-symbolic " , css_classes = [ " flat " , " circular " ] , tooltip_text = _ ( " Copy Message " ) )
2024-05-25 23:07:51 +02:00
copy_button . connect ( " clicked " , self . on_copy_code_clicked , buffer )
title_box . append ( copy_button )
code_block_box . append ( title_box )
code_block_box . append ( Gtk . Separator ( ) )
code_block_box . append ( source_view )
self . bot_message_box . append ( code_block_box )
self . style_manager . connect ( " notify::dark " , self . on_theme_changed , buffer )
2024-05-26 12:23:13 -06:00
vadjustment = self . chat_window . get_vadjustment ( )
vadjustment . set_value ( vadjustment . get_upper ( ) )
2024-05-16 20:13:18 -06:00
self . bot_message = None
self . bot_message_box = None
2024-05-25 23:07:51 +02:00
def on_theme_changed ( self , manager , dark , buffer ) :
if manager . get_dark ( ) :
source_style = GtkSource . StyleSchemeManager . get_default ( ) . get_scheme ( ' Adwaita-dark ' )
else :
source_style = GtkSource . StyleSchemeManager . get_default ( ) . get_scheme ( ' Adwaita ' )
buffer . set_style_scheme ( source_style )
def on_copy_code_clicked ( self , btn , text_buffer ) :
clipboard = Gdk . Display ( ) . get_default ( ) . get_clipboard ( )
start = text_buffer . get_start_iter ( )
end = text_buffer . get_end_iter ( )
text = text_buffer . get_text ( start , end , False )
clipboard . set ( text )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Code copied to the clipboard " ) , self . main_overlay )
2024-05-25 23:07:51 +02:00
2024-06-04 12:07:15 -06:00
def update_bot_message ( self , data , id ) :
2024-05-19 18:29:20 -06:00
if self . bot_message is None :
self . save_history ( )
sys . exit ( )
2024-05-18 11:44:16 -06:00
vadjustment = self . chat_window . get_vadjustment ( )
2024-07-03 20:53:25 -06:00
if id not in self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] or vadjustment . get_value ( ) + 50 > = vadjustment . get_upper ( ) - vadjustment . get_page_size ( ) :
2024-05-18 11:46:27 -06:00
GLib . idle_add ( vadjustment . set_value , vadjustment . get_upper ( ) )
2024-05-12 18:18:25 -06:00
if data [ ' done ' ] :
2024-05-13 13:25:46 -06:00
formated_datetime = datetime . now ( ) . strftime ( " % Y/ % m/ %d % H: % M " )
2024-06-24 00:23:32 -06:00
text = f " \n <small> { data [ ' model ' ] } \t { formated_datetime } </small> "
2024-05-13 13:25:46 -06:00
GLib . idle_add ( self . bot_message . insert_markup , self . bot_message . get_end_iter ( ) , text , len ( text ) )
self . save_history ( )
2024-06-26 15:35:59 -06:00
GLib . idle_add ( self . bot_message_button_container . set_visible , True )
2024-07-04 11:42:35 -06:00
#Notification
first_paragraph = self . bot_message . get_text ( self . bot_message . get_start_iter ( ) , self . bot_message . get_end_iter ( ) , False ) . split ( " \n " ) [ 0 ]
GLib . idle_add ( self . show_notification , self . chats [ " selected_chat " ] , first_paragraph [ : 100 ] + ( first_paragraph [ 100 : ] and ' ... ' ) , Gio . ThemedIcon . new ( " chat-message-new-symbolic " ) )
2024-05-12 18:18:25 -06:00
else :
2024-06-04 12:07:15 -06:00
if id not in self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] :
2024-05-18 16:46:20 -06:00
GLib . idle_add ( self . chat_container . remove , self . loading_spinner )
self . loading_spinner = None
2024-06-04 12:07:15 -06:00
self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ] = {
2024-05-12 18:18:25 -06:00
" role " : " assistant " ,
2024-05-13 13:25:46 -06:00
" model " : data [ ' model ' ] ,
" date " : datetime . now ( ) . strftime ( " % Y/ % m/ %d % H: % M " ) ,
2024-05-16 20:13:18 -06:00
" content " : ' '
2024-06-04 12:07:15 -06:00
}
2024-05-16 20:13:18 -06:00
GLib . idle_add ( self . bot_message . insert , self . bot_message . get_end_iter ( ) , data [ ' message ' ] [ ' content ' ] )
2024-06-04 12:07:15 -06:00
self . chats [ " chats " ] [ self . chats [ " selected_chat " ] ] [ " messages " ] [ id ] [ ' content ' ] + = data [ ' message ' ] [ ' content ' ]
2024-05-16 20:13:18 -06:00
2024-05-25 23:10:16 -06:00
def toggle_ui_sensitive ( self , status ) :
2024-06-28 16:42:58 -06:00
for element in [ self . chat_list_box , self . add_chat_button , self . secondary_menu_button ] :
2024-05-25 23:10:16 -06:00
element . set_sensitive ( status )
2024-05-29 14:32:57 -06:00
def switch_send_stop_button ( self ) :
self . stop_button . set_visible ( self . send_button . get_visible ( ) )
self . send_button . set_visible ( not self . send_button . get_visible ( ) )
2024-06-04 12:07:15 -06:00
def run_message ( self , messages , model , id ) :
2024-06-26 15:35:59 -06:00
self . bot_message_button_container . set_visible ( False )
2024-06-04 12:07:15 -06:00
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 ) )
2024-05-16 20:13:18 -06:00
GLib . idle_add ( self . add_code_blocks )
2024-05-29 14:32:57 -06:00
GLib . idle_add ( self . switch_send_stop_button )
2024-05-25 23:10:16 -06:00
GLib . idle_add ( self . toggle_ui_sensitive , True )
2024-06-01 00:07:34 -06:00
if self . loading_spinner :
GLib . idle_add ( self . chat_container . remove , self . loading_spinner )
self . loading_spinner = None
2024-05-16 20:13:18 -06:00
if response [ ' status ' ] == ' error ' :
2024-05-21 18:52:56 -06:00
GLib . idle_add ( self . connection_error )
2024-05-12 18:18:25 -06:00
2024-05-19 17:09:58 -06:00
def pull_model_update ( self , data , model_name ) :
if model_name in list ( self . pulling_models . keys ( ) ) :
2024-06-30 18:09:06 -06:00
if ' completed ' in data and ' total ' in data :
GLib . idle_add ( self . pulling_models [ model_name ] [ ' row ' ] . set_subtitle , ' <tt> {} % </tt> \t {} ' . format ( round ( data [ ' completed ' ] / data [ ' total ' ] * 100 , 2 ) , data [ ' status ' ] . capitalize ( ) ) )
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 )
2024-05-19 17:09:58 -06:00
else :
2024-05-20 17:36:22 -06:00
if len ( list ( self . pulling_models . keys ( ) ) ) == 0 :
GLib . idle_add ( self . pulling_model_list_box . set_visible , False )
2024-05-12 18:18:25 -06:00
2024-06-01 00:07:34 -06:00
def pull_model_process ( self , model , modelfile ) :
response = { }
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 ) )
2024-05-19 17:09:58 -06:00
GLib . idle_add ( self . update_list_local_models )
2024-05-20 17:36:22 -06:00
2024-05-19 17:09:58 -06:00
if response [ ' status ' ] == ' ok ' :
2024-06-30 16:56:28 -06:00
GLib . idle_add ( self . show_notification , _ ( " Task Complete " ) , _ ( " Model ' {} ' pulled successfully. " ) . format ( model ) , Gio . ThemedIcon . new ( " emblem-ok-symbolic " ) )
2024-05-19 17:09:58 -06:00
GLib . idle_add ( self . show_toast , " good " , 1 , self . manage_models_overlay )
2024-05-27 20:47:59 -06:00
GLib . idle_add ( self . pulling_models [ model ] [ ' overlay ' ] . get_parent ( ) . get_parent ( ) . remove , self . pulling_models [ model ] [ ' overlay ' ] . get_parent ( ) )
2024-05-25 23:03:26 -06:00
del self . pulling_models [ model ]
2024-05-19 17:09:58 -06:00
else :
2024-06-30 16:56:28 -06:00
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 " ) )
2024-05-27 20:47:59 -06:00
GLib . idle_add ( self . pulling_models [ model ] [ ' overlay ' ] . get_parent ( ) . get_parent ( ) . remove , self . pulling_models [ model ] [ ' overlay ' ] . get_parent ( ) )
2024-05-25 23:03:26 -06:00
del self . pulling_models [ model ]
2024-05-19 17:09:58 -06:00
GLib . idle_add ( self . manage_models_dialog . close )
2024-05-21 18:52:56 -06:00
GLib . idle_add ( self . connection_error )
2024-05-20 17:36:22 -06:00
if len ( list ( self . pulling_models . keys ( ) ) ) == 0 :
GLib . idle_add ( self . pulling_model_list_box . set_visible , False )
2024-05-12 18:18:25 -06:00
2024-05-25 23:03:26 -06:00
def pull_model ( self , model ) :
2024-07-07 20:24:29 -06:00
if model in list ( self . pulling_models . keys ( ) ) or model in self . local_models :
2024-05-25 23:03:26 -06:00
return
self . pulling_model_list_box . set_visible ( True )
2024-06-26 17:14:23 -06:00
#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
2024-05-25 23:03:26 -06:00
model_row = Adw . ActionRow (
2024-07-04 11:56:45 -06:00
title = " <b> {} </b> <small> {} </small> " . format ( model . split ( " : " ) [ 0 ] . replace ( " - " , " " ) . title ( ) , model . split ( " : " ) [ 1 ] ) ,
2024-06-26 17:14:23 -06:00
name = model
2024-05-18 15:52:50 -06:00
)
2024-06-01 00:07:34 -06:00
thread = threading . Thread ( target = self . pull_model_process , kwargs = { " model " : model , " modelfile " : None } )
2024-05-27 20:47:59 -06:00
overlay = Gtk . Overlay ( )
2024-05-27 17:27:05 -06:00
progress_bar = Gtk . ProgressBar (
2024-05-27 20:47:59 -06:00
valign = 2 ,
show_text = False ,
margin_start = 10 ,
margin_end = 10 ,
css_classes = [ " osd " , " horizontal " , " bottom " ]
2024-05-27 17:27:05 -06:00
)
2024-05-25 23:03:26 -06:00
button = Gtk . Button (
icon_name = " media-playback-stop-symbolic " ,
vexpand = False ,
valign = 3 ,
2024-06-28 20:46:31 -06:00
css_classes = [ " error " , " circular " ] ,
2024-07-04 11:56:45 -06:00
tooltip_text = _ ( " Stop Pulling ' {} ( {} ) ' " ) . format ( model . split ( ' : ' ) [ 0 ] . replace ( ' - ' , ' ' ) . title ( ) , model . split ( ' : ' ) [ 1 ] )
2024-05-18 15:52:50 -06:00
)
2024-05-25 23:03:26 -06:00
button . connect ( " clicked " , lambda button , model_name = model : dialogs . stop_pull_model ( self , model_name ) )
model_row . add_suffix ( button )
2024-05-27 20:47:59 -06:00
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 )
2024-05-25 23:03:26 -06:00
thread . start ( )
2024-05-18 15:52:50 -06:00
2024-06-25 23:32:09 -06:00
def confirm_pull_model ( self , model_name ) :
2024-06-27 22:48:02 -06:00
self . navigation_view_manage_models . pop ( )
2024-06-26 16:38:00 -06:00
self . model_tag_list_box . unselect_all ( )
2024-06-25 23:32:09 -06:00
self . pull_model ( model_name )
def list_available_model_tags ( self , model_name ) :
2024-06-27 22:48:02 -06:00
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 ( ) )
2024-06-30 13:28:08 -06:00
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 ' ] )
2024-06-26 16:38:00 -06:00
self . available_model_list_box . unselect_all ( )
self . model_tag_list_box . connect ( ' row_selected ' , lambda list_box , row : self . confirm_pull_model ( row . get_name ( ) ) if row else None )
2024-06-25 23:32:09 -06:00
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 . local_models :
tag_row = Adw . ActionRow (
title = tag_data [ 0 ] ,
2024-06-26 16:38:00 -06:00
subtitle = tag_data [ 1 ] ,
name = f " { model_name } : { tag_data [ 0 ] } "
2024-06-25 23:32:09 -06:00
)
2024-06-28 14:40:32 -06:00
tag_row . add_suffix ( Gtk . Image . new_from_icon_name ( " folder-download-symbolic " ) )
2024-06-25 23:32:09 -06:00
self . model_tag_list_box . append ( tag_row )
2024-05-12 18:18:25 -06:00
def update_list_available_models ( self ) :
2024-06-26 16:38:00 -06:00
self . available_model_list_box . connect ( ' row_selected ' , lambda list_box , row : self . list_available_model_tags ( row . get_name ( ) ) if row else None )
2024-05-18 15:52:50 -06:00
self . available_model_list_box . remove_all ( )
2024-06-25 00:36:02 -06:00
for name , model_info in self . available_models . items ( ) :
2024-05-18 15:52:50 -06:00
model = Adw . ActionRow (
2024-07-07 19:42:23 -06:00
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 " " ) ,
2024-06-26 16:38:00 -06:00
name = name
2024-05-18 15:52:50 -06:00
)
2024-06-30 13:28:08 -06:00
if model_info [ " image " ] :
image_icon = Gtk . Image . new_from_icon_name ( " image-x-generic-symbolic " )
image_icon . set_margin_start ( 5 )
2024-07-02 17:44:48 -06:00
#model.add_suffix(image_icon)
2024-06-30 13:28:08 -06:00
next_icon = Gtk . Image . new_from_icon_name ( " go-next " )
next_icon . set_margin_start ( 5 )
model . add_suffix ( next_icon )
2024-05-18 15:52:50 -06:00
self . available_model_list_box . append ( model )
2024-05-12 18:18:25 -06:00
2024-05-13 13:25:46 -06:00
def save_history ( self ) :
2024-06-04 12:07:15 -06:00
with open ( os . path . join ( self . data_dir , " chats " , " chats.json " ) , " w+ " ) as f :
2024-05-13 13:25:46 -06:00
json . dump ( self . chats , f , indent = 4 )
2024-05-19 00:17:00 -06:00
def load_history_into_chat ( self ) :
for widget in list ( self . chat_container ) : self . chat_container . remove ( widget )
2024-06-04 12:07:15 -06:00
for key , message in self . chats [ ' chats ' ] [ self . chats [ " selected_chat " ] ] [ ' messages ' ] . items ( ) :
2024-05-29 15:58:09 -06:00
if message :
if message [ ' role ' ] == ' user ' :
2024-06-04 12:07:15 -06:00
self . show_message ( message [ ' content ' ] , False , f " \n \n <small> { message [ ' date ' ] } </small> " , message [ ' images ' ] if ' images ' in message else None , message [ ' files ' ] if ' files ' in message else None , id = key )
2024-05-29 15:58:09 -06:00
else :
2024-06-04 12:07:15 -06:00
self . show_message ( message [ ' content ' ] , True , f " \n \n <small> { message [ ' model ' ] } \t | \t { message [ ' date ' ] } </small> " , id = key )
2024-05-29 15:58:09 -06:00
self . add_code_blocks ( )
self . bot_message = None
2024-05-19 00:17:00 -06:00
2024-05-13 13:25:46 -06:00
def load_history ( self ) :
2024-06-04 12:07:15 -06:00
if os . path . exists ( os . path . join ( self . data_dir , " chats " , " chats.json " ) ) :
2024-05-13 13:25:46 -06:00
try :
2024-06-04 12:07:15 -06:00
with open ( os . path . join ( self . data_dir , " chats " , " chats.json " ) , " r " ) as f :
2024-05-13 13:25:46 -06:00
self . chats = json . load ( f )
2024-06-04 12:07:15 -06:00
if len ( list ( self . chats [ " chats " ] . keys ( ) ) ) == 0 : self . chats [ " chats " ] [ _ ( " New Chat " ) ] = { " messages " : { } }
2024-06-28 14:40:32 -06:00
if " selected_chat " not in self . chats or self . chats [ " selected_chat " ] not in self . chats [ " chats " ] : self . chats [ " selected_chat " ] = list ( self . chats [ " chats " ] . keys ( ) ) [ 0 ]
if " order " not in self . chats :
self . chats [ " order " ] = [ ]
for chat_name in self . chats [ " chats " ] . keys ( ) :
self . chats [ " order " ] . append ( chat_name )
2024-05-13 13:25:46 -06:00
except Exception as e :
2024-06-30 17:11:40 -06:00
self . chats = { " chats " : { } , " selected_chat " : None , " order " : [ ] }
self . new_chat ( )
else :
self . chats = { " chats " : { } , " selected_chat " : None , " order " : [ ] }
self . new_chat ( )
self . load_history_into_chat ( )
2024-05-12 18:18:25 -06:00
2024-06-04 12:07:15 -06:00
def generate_numbered_name ( self , chat_name : str , compare_list : list ) - > str :
if chat_name in compare_list :
for i in range ( len ( compare_list ) ) :
if " . " in chat_name :
if f " { ' . ' . join ( chat_name . split ( ' . ' ) [ : - 1 ] ) } { i + 1 } . { chat_name . split ( ' . ' ) [ - 1 ] } " not in compare_list :
chat_name = f " { ' . ' . join ( chat_name . split ( ' . ' ) [ : - 1 ] ) } { i + 1 } . { chat_name . split ( ' . ' ) [ - 1 ] } "
break
2024-05-17 00:35:34 -06:00
else :
2024-06-04 12:07:15 -06:00
if f " { chat_name } { i + 1 } " not in compare_list :
chat_name = f " { chat_name } { i + 1 } "
break
2024-05-25 23:03:26 -06:00
return chat_name
2024-06-04 12:07:15 -06:00
def generate_uuid ( self ) - > str :
return f " { datetime . today ( ) . strftime ( ' % Y % m %d % H % M % S %f ' ) } { uuid . uuid4 ( ) . hex } "
2024-05-25 23:03:26 -06:00
def clear_chat ( self ) :
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 ) :
del self . chats [ ' chats ' ] [ chat_name ]
2024-06-28 15:39:26 -06:00
self . chats [ ' order ' ] . remove ( chat_name )
2024-06-04 12:07:15 -06:00
if os . path . exists ( os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] ) ) :
shutil . rmtree ( os . path . join ( self . data_dir , " chats " , self . chats [ ' selected_chat ' ] ) )
2024-05-25 23:03:26 -06:00
self . save_history ( )
self . update_chat_list ( )
if len ( self . chats [ ' chats ' ] ) == 0 :
2024-05-28 22:26:36 -06:00
self . new_chat ( )
2024-06-24 22:22:46 -06:00
if self . chats [ ' selected_chat ' ] == chat_name :
self . chat_list_box . select_row ( self . chat_list_box . get_row_at_index ( 0 ) )
2024-05-25 23:03:26 -06:00
def rename_chat ( self , old_chat_name , new_chat_name , label_element ) :
2024-06-04 12:07:15 -06:00
new_chat_name = self . generate_numbered_name ( new_chat_name , self . chats [ " chats " ] . keys ( ) )
2024-06-23 20:51:47 -06:00
if self . chats [ " selected_chat " ] == old_chat_name : self . chats [ " selected_chat " ] = new_chat_name
2024-05-25 23:03:26 -06:00
self . chats [ " chats " ] [ new_chat_name ] = self . chats [ " chats " ] [ old_chat_name ]
2024-06-28 15:39:26 -06:00
self . chats [ " order " ] [ self . chats [ " order " ] . index ( old_chat_name ) ] = new_chat_name
2024-05-25 23:03:26 -06:00
del self . chats [ " chats " ] [ old_chat_name ]
2024-06-04 12:07:15 -06:00
if os . path . exists ( os . path . join ( self . data_dir , " chats " , old_chat_name ) ) :
shutil . move ( os . path . join ( self . data_dir , " chats " , old_chat_name ) , os . path . join ( self . data_dir , " chats " , new_chat_name ) )
2024-06-30 15:06:02 -06:00
label_element . set_tooltip_text ( new_chat_name )
2024-05-25 23:03:26 -06:00
label_element . set_label ( new_chat_name )
2024-06-30 21:52:52 -06:00
label_element . set_name ( new_chat_name )
2024-05-25 23:03:26 -06:00
self . save_history ( )
2024-05-27 21:21:27 -06:00
def new_chat ( self ) :
2024-06-04 12:07:15 -06:00
chat_name = self . generate_numbered_name ( _ ( " New Chat " ) , self . chats [ " chats " ] . keys ( ) )
2024-06-04 13:39:50 -06:00
self . chats [ " chats " ] [ chat_name ] = { " messages " : { } }
2024-06-28 15:39:26 -06:00
self . chats [ " order " ] . insert ( 0 , chat_name )
2024-05-25 23:03:26 -06:00
self . save_history ( )
2024-06-28 15:39:26 -06:00
self . new_chat_element ( chat_name , True , False )
2024-05-25 23:03:26 -06:00
def stop_pull_model ( self , model_name ) :
2024-05-27 20:47:59 -06:00
self . pulling_models [ model_name ] [ ' overlay ' ] . get_parent ( ) . get_parent ( ) . remove ( self . pulling_models [ model_name ] [ ' overlay ' ] . get_parent ( ) )
2024-05-25 23:03:26 -06:00
del self . pulling_models [ model_name ]
def delete_model ( self , model_name ) :
2024-06-01 00:07:34 -06:00
response = connection_handler . simple_delete ( f " { connection_handler . url } /api/delete " , data = { " name " : model_name } )
2024-05-25 23:03:26 -06:00
self . update_list_local_models ( )
if response [ ' status ' ] == ' ok ' :
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Model deleted successfully " ) , self . manage_models_overlay )
2024-05-25 23:03:26 -06:00
else :
self . manage_models_dialog . close ( )
self . connection_error ( )
2024-06-24 22:08:05 -06:00
def chat_click_handler ( self , gesture , n_press , x , y ) :
chat_row = gesture . get_widget ( )
popover = Gtk . PopoverMenu (
2024-06-24 23:13:17 -06:00
menu_model = self . chat_right_click_menu ,
2024-06-24 22:08:05 -06:00
has_arrow = False ,
halign = 1 ,
)
2024-06-28 15:39:26 -06:00
self . selected_chat_row = chat_row
2024-06-23 20:07:27 -06:00
position = Gdk . Rectangle ( )
position . x = x
position . y = y
2024-06-24 22:08:05 -06:00
popover . set_parent ( chat_row . get_child ( ) )
2024-06-23 20:07:27 -06:00
popover . set_pointing_to ( position )
popover . popup ( )
2024-06-28 15:39:26 -06:00
def new_chat_element ( self , chat_name : str , select : bool , append : bool ) :
2024-05-25 23:03:26 -06:00
chat_label = Gtk . Label (
label = chat_name ,
2024-06-30 15:06:02 -06:00
tooltip_text = chat_name ,
2024-06-30 21:52:52 -06:00
name = chat_name ,
2024-05-25 23:03:26 -06:00
hexpand = True ,
2024-05-28 22:37:06 -06:00
halign = 0 ,
wrap = True ,
ellipsize = 3 ,
2024-05-28 22:42:03 -06:00
wrap_mode = 2 ,
xalign = 0
2024-05-19 00:17:00 -06:00
)
2024-06-23 20:07:27 -06:00
chat_row = Gtk . ListBoxRow (
css_classes = [ " chat_row " ] ,
height_request = 45 ,
2024-06-30 21:52:52 -06:00
child = chat_label
2024-06-23 20:07:27 -06:00
)
2024-06-24 22:08:05 -06:00
2024-06-23 20:07:27 -06:00
gesture = Gtk . GestureClick ( button = 3 )
2024-06-24 22:08:05 -06:00
gesture . connect ( " released " , self . chat_click_handler )
2024-06-23 20:07:27 -06:00
chat_row . add_controller ( gesture )
2024-06-28 15:39:26 -06:00
if append : self . chat_list_box . append ( chat_row )
else : self . chat_list_box . prepend ( chat_row )
2024-05-26 12:39:22 -06:00
if select : self . chat_list_box . select_row ( chat_row )
2024-05-19 00:17:00 -06:00
def update_chat_list ( self ) :
self . chat_list_box . remove_all ( )
2024-06-28 15:39:26 -06:00
for name in self . chats [ ' order ' ] :
if name in self . chats [ ' chats ' ] . keys ( ) :
self . new_chat_element ( name , self . chats [ " selected_chat " ] == name , True )
2024-05-19 00:17:00 -06:00
2024-05-21 17:58:51 -06:00
def show_preferences_dialog ( self ) :
self . preferences_dialog . present ( self )
2024-05-25 23:03:26 -06:00
def connect_remote ( self , 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 ) :
self . run_remote = False
2024-06-28 21:29:36 -06:00
connection_handler . bearer_token = None
2024-05-25 23:03:26 -06:00
connection_handler . url = f " http://127.0.0.1: { local_instance . port } "
2024-05-28 11:24:50 -06:00
local_instance . start ( )
2024-05-25 23:03:26 -06:00
if self . verify_connection ( ) == False : self . connection_error ( )
else : self . remote_connection_switch . set_active ( False )
2024-05-22 21:10:16 -06:00
2024-05-21 18:52:56 -06:00
def connection_error ( self ) :
2024-05-21 17:58:51 -06:00
if self . run_remote :
2024-05-28 10:41:52 -06:00
dialogs . reconnect_remote ( self , connection_handler . url )
2024-05-21 17:58:51 -06:00
else :
2024-05-28 11:24:50 -06:00
local_instance . reset ( )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " There was an error with the local Ollama instance, so it has been reset " ) , self . main_overlay )
2024-05-21 17:58:51 -06:00
2024-05-21 23:15:32 -06:00
def connection_switched ( self ) :
2024-05-21 17:58:51 -06:00
new_value = self . remote_connection_switch . get_active ( )
if new_value != self . run_remote :
self . run_remote = new_value
if self . run_remote :
2024-06-28 21:29:36 -06:00
connection_handler . bearer_token = self . remote_bearer_token
2024-05-25 23:03:26 -06:00
connection_handler . url = self . remote_url
2024-05-21 18:52:56 -06:00
if self . verify_connection ( ) == False : self . connection_error ( )
2024-05-26 00:01:14 -06:00
else : local_instance . stop ( )
2024-05-21 17:58:51 -06:00
else :
2024-06-28 21:29:36 -06:00
connection_handler . bearer_token = None
2024-05-25 23:03:26 -06:00
connection_handler . url = f " http://127.0.0.1: { local_instance . port } "
2024-05-28 11:24:50 -06:00
local_instance . start ( )
2024-05-21 18:52:56 -06:00
if self . verify_connection ( ) == False : self . connection_error ( )
2024-05-21 17:58:51 -06:00
2024-05-21 23:15:32 -06:00
def on_replace_contents ( self , file , result ) :
file . replace_contents_finish ( result )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Chat exported successfully " ) , self . main_overlay )
2024-05-21 23:15:32 -06:00
2024-06-23 20:07:27 -06:00
def on_export_chat ( self , file_dialog , result , chat_name ) :
2024-05-21 23:15:32 -06:00
file = file_dialog . save_finish ( result )
2024-06-04 12:07:15 -06:00
if not file : return
2024-06-23 20:07:27 -06:00
json_data = json . dumps ( { chat_name : self . chats [ " chats " ] [ chat_name ] } , indent = 4 ) . encode ( " UTF-8 " )
2024-06-04 12:07:15 -06:00
with tempfile . TemporaryDirectory ( ) as temp_dir :
json_path = os . path . join ( temp_dir , " data.json " )
with open ( json_path , " wb " ) as json_file :
json_file . write ( json_data )
2024-06-23 20:07:27 -06:00
tar_path = os . path . join ( temp_dir , chat_name )
2024-06-04 12:07:15 -06:00
with tarfile . open ( tar_path , " w " ) as tar :
tar . add ( json_path , arcname = " data.json " )
2024-06-23 20:07:27 -06:00
directory = os . path . join ( self . data_dir , " chats " , chat_name )
2024-06-04 13:39:50 -06:00
if os . path . exists ( directory ) and os . path . isdir ( directory ) :
tar . add ( directory , arcname = os . path . basename ( directory ) )
2024-06-04 12:07:15 -06:00
with open ( tar_path , " rb " ) as tar :
tar_content = tar . read ( )
file . replace_contents_async (
tar_content ,
etag = None ,
make_backup = False ,
flags = Gio . FileCreateFlags . NONE ,
cancellable = None ,
callback = self . on_replace_contents
)
2024-05-21 23:15:32 -06:00
2024-06-23 20:07:27 -06:00
def export_chat ( self , chat_name ) :
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 ) )
2024-05-21 23:15:32 -06:00
def on_chat_imported ( self , file_dialog , result ) :
file = file_dialog . open_finish ( result )
2024-06-04 12:07:15 -06:00
if not file : return
2024-05-21 23:15:32 -06:00
stream = file . read ( None )
data_stream = Gio . DataInputStream . new ( stream )
2024-06-04 12:07:15 -06:00
tar_content = data_stream . read_bytes ( 1024 * 1024 , None )
with tempfile . TemporaryDirectory ( ) as temp_dir :
tar_filename = os . path . join ( temp_dir , " imported_chat.tar " )
with open ( tar_filename , " wb " ) as tar_file :
tar_file . write ( tar_content . get_data ( ) )
with tarfile . open ( tar_filename , " r " ) as tar :
tar . extractall ( path = temp_dir )
chat_name = None
chat_content = None
for member in tar . getmembers ( ) :
if member . name == " data.json " :
json_filepath = os . path . join ( temp_dir , member . name )
with open ( json_filepath , " r " ) as json_file :
data = json . load ( json_file )
for chat_name , chat_content in data . items ( ) :
new_chat_name = self . generate_numbered_name ( chat_name , list ( self . chats [ ' chats ' ] . keys ( ) ) )
self . chats [ ' chats ' ] [ new_chat_name ] = chat_content
src_path = os . path . join ( temp_dir , chat_name )
if os . path . exists ( src_path ) and os . path . isdir ( src_path ) :
dest_path = os . path . join ( self . data_dir , " chats " , new_chat_name )
shutil . copytree ( src_path , dest_path )
2024-05-21 23:15:32 -06:00
self . update_chat_list ( )
self . save_history ( )
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Chat imported successfully " ) , self . main_overlay )
2024-05-21 23:15:32 -06:00
def import_chat ( self ) :
2024-06-04 12:07:15 -06:00
file_dialog = Gtk . FileDialog ( default_filter = self . file_filter_tar )
2024-05-21 23:15:32 -06:00
file_dialog . open ( self , None , self . on_chat_imported )
def switch_run_on_background ( self ) :
self . run_on_background = self . background_switch . get_active ( )
self . set_hide_on_close ( self . run_on_background )
self . verify_connection ( )
2024-06-04 12:07:15 -06:00
def get_content_of_file ( self , file_path , file_type ) :
2024-06-26 14:26:41 -06:00
if not os . path . exists ( file_path ) : return None
2024-06-04 12:07:15 -06:00
if file_type == ' image ' :
try :
with Image . open ( file_path ) as img :
width , height = img . size
max_size = 240
if width > height :
new_width = max_size
new_height = int ( ( max_size / width ) * height )
else :
new_height = max_size
new_width = int ( ( max_size / height ) * width )
resized_img = img . resize ( ( new_width , new_height ) , Image . LANCZOS )
with BytesIO ( ) as output :
resized_img . save ( output , format = " PNG " )
image_data = output . getvalue ( )
return base64 . b64encode ( image_data ) . decode ( " utf-8 " )
except Exception as e :
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Cannot open image " ) , self . main_overlay )
2024-06-30 23:24:29 -06:00
elif file_type == ' plain_text ' or file_type == ' youtube ' or file_type == ' website ' :
2024-06-04 12:07:15 -06:00
with open ( file_path , ' r ' ) as f :
return f . read ( )
2024-06-21 17:16:59 -06:00
elif file_type == ' pdf ' :
reader = PdfReader ( file_path )
if len ( reader . pages ) == 0 : return None
text = " "
for i , page in enumerate ( reader . pages ) :
text + = f " \n - Page { i } \n { page . extract_text ( ) } \n "
return text
2024-06-04 12:07:15 -06:00
def remove_attached_file ( self , button ) :
del self . attachments [ button . get_name ( ) ]
button . get_parent ( ) . remove ( button )
if len ( self . attachments ) == 0 : self . attachment_box . set_visible ( False )
def attach_file ( self , file_path , file_type ) :
2024-06-26 14:26:41 -06:00
name = self . generate_numbered_name ( os . path . basename ( file_path ) , self . attachments . keys ( ) )
2024-06-04 12:07:15 -06:00
content = self . get_content_of_file ( file_path , file_type )
2024-06-04 18:53:41 -06:00
if content :
button_content = Adw . ButtonContent (
2024-06-30 19:23:26 -06:00
label = name ,
2024-06-21 17:16:59 -06:00
icon_name = {
" image " : " image-x-generic-symbolic " ,
" plain_text " : " document-text-symbolic " ,
" pdf " : " document-text-symbolic " ,
2024-06-30 23:24:29 -06:00
" youtube " : " play-symbolic " ,
" website " : " globe-symbolic "
2024-06-21 17:16:59 -06:00
} [ file_type ]
2024-06-04 18:53:41 -06:00
)
button = Gtk . Button (
vexpand = True ,
valign = 3 ,
name = name ,
css_classes = [ " flat " ] ,
tooltip_text = name ,
child = button_content
)
self . attachments [ name ] = { " path " : file_path , " type " : file_type , " content " : content , " button " : button }
button . connect ( " clicked " , lambda button : dialogs . remove_attached_file ( self , button ) )
self . attachment_container . append ( button )
self . attachment_box . set_visible ( True )
2024-06-04 12:07:15 -06:00
2024-06-23 20:07:27 -06:00
def chat_actions ( self , action , user_data ) :
2024-06-28 15:39:26 -06:00
chat_row = self . selected_chat_row
2024-06-30 21:52:52 -06:00
chat_name = chat_row . get_child ( ) . get_name ( )
2024-06-28 15:39:26 -06:00
action_name = action . get_name ( )
2024-06-23 20:07:27 -06:00
if action_name == ' delete_chat ' :
dialogs . delete_chat ( self , chat_name )
2024-06-28 15:39:26 -06:00
elif action_name in ( ' rename_chat ' , ' rename_current_chat ' ) :
2024-06-24 22:08:05 -06:00
dialogs . rename_chat ( self , chat_name , chat_row . get_child ( ) )
2024-06-28 15:39:26 -06:00
elif action_name in ( ' export_chat ' , ' export_current_chat ' ) :
2024-06-23 20:07:27 -06:00
self . export_chat ( chat_name )
2024-06-28 15:39:26 -06:00
def current_chat_actions ( self , action , user_data ) :
self . selected_chat_row = self . chat_list_box . get_selected_row ( )
self . chat_actions ( action , user_data )
2024-06-30 17:45:03 -06:00
def cb_text_received ( self , clipboard , result ) :
try :
text = clipboard . read_text_finish ( result )
#Check if text is a Youtube URL
youtube_regex = re . compile (
r ' (https?://)?(www \ .)?(youtube|youtu|youtube-nocookie) \ .(com|be)/ '
r ' (watch \ ?v=|embed/|v/|.+ \ ?v=)?([^&= % \ ?] {11} ) ' )
2024-06-30 23:24:29 -06:00
url_regex = re . compile (
r ' http[s]?:// '
r ' (?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!* \\ ( \\ ),]| '
r ' (?: % [0-9a-fA-F][0-9a-fA-F]))+ '
r ' (?: \\ :[0-9] { 1,5})? '
r ' (?:/[^ \\ s]*)? '
)
2024-06-30 17:45:03 -06:00
if youtube_regex . match ( text ) :
try :
dialogs . youtube_caption ( self , text )
except Exception as e :
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " This video is not available " ) , self . main_overlay )
2024-06-30 23:24:29 -06:00
elif url_regex . match ( text ) :
dialogs . attach_website ( self , text )
2024-06-30 17:45:03 -06:00
except Exception as e : ' huh '
def cb_image_received ( self , clipboard , result ) :
try :
texture = clipboard . read_texture_finish ( result )
if texture :
2024-06-30 22:12:46 -06:00
if self . verify_if_image_can_be_used ( ) :
pixbuf = Gdk . pixbuf_get_from_texture ( texture )
if not os . path . exists ( ' /tmp/alpaca/images/ ' ) :
os . makedirs ( ' /tmp/alpaca/images/ ' )
image_name = self . generate_numbered_name ( ' image.png ' , os . listdir ( ' /tmp/alpaca/images ' ) )
pixbuf . savev ( ' /tmp/alpaca/images/ {} ' . format ( image_name ) , " png " , [ ] , [ ] )
self . attach_file ( ' /tmp/alpaca/images/ {} ' . format ( image_name ) , ' image ' )
else :
2024-07-07 20:24:29 -06:00
self . show_toast ( _ ( " Image recognition is only available on specific models " ) , self . main_overlay )
2024-06-30 17:45:03 -06:00
except Exception as e : ' huh '
2024-06-24 00:18:55 -06:00
def on_clipboard_paste ( self , textview ) :
clipboard = Gdk . Display . get_default ( ) . get_clipboard ( )
2024-06-30 17:45:03 -06:00
clipboard . read_text_async ( None , self . cb_text_received )
clipboard . read_texture_async ( None , self . cb_image_received )
2024-06-04 12:07:15 -06:00
2024-06-24 21:14:29 -06:00
def on_model_dropdown_setup ( self , factory , list_item ) :
label = Gtk . Label ( )
label . set_ellipsize ( 2 )
label . set_xalign ( 0 )
list_item . set_child ( label )
def on_model_dropdown_bind ( self , factory , list_item ) :
label = list_item . get_child ( )
item = list_item . get_item ( )
label . set_text ( item . get_string ( ) )
label . set_tooltip_text ( item . get_string ( ) )
def setup_model_dropdown ( self ) :
factory = Gtk . SignalListItemFactory ( )
factory . connect ( " setup " , self . on_model_dropdown_setup )
factory . connect ( " bind " , self . on_model_dropdown_bind )
self . model_drop_down . set_factory ( factory )
2024-05-12 18:18:25 -06:00
def __init__ ( self , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
2024-05-16 20:13:18 -06:00
GtkSource . init ( )
2024-06-25 00:36:02 -06:00
with open ( ' /app/share/Alpaca/alpaca/available_models.json ' , ' r ' ) as f :
self . available_models = json . load ( f )
2024-06-04 19:56:49 -06:00
if not os . path . exists ( os . path . join ( self . data_dir , " chats " ) ) :
os . makedirs ( os . path . join ( self . data_dir , " chats " ) )
2024-06-04 12:07:15 -06:00
if os . path . exists ( os . path . join ( self . config_dir , " chats.json " ) ) and not os . path . exists ( os . path . join ( self . data_dir , " chats " , " chats.json " ) ) :
update_history . update ( self )
2024-05-19 12:10:14 -06:00
self . set_help_overlay ( self . shortcut_window )
self . get_application ( ) . set_accels_for_action ( " win.show-help-overlay " , [ ' <primary>slash ' ] )
2024-05-28 22:45:26 -06:00
self . get_application ( ) . create_action ( ' new_chat ' , lambda * _ : self . new_chat ( ) , [ ' <primary>n ' ] )
2024-05-25 23:03:26 -06:00
self . get_application ( ) . create_action ( ' clear ' , lambda * _ : dialogs . clear_chat ( self ) , [ ' <primary>e ' ] )
2024-05-23 19:25:25 -06:00
self . get_application ( ) . create_action ( ' send ' , lambda * _ : self . send_message ( self ) , [ ' Return ' ] )
2024-06-23 20:17:45 -06:00
self . get_application ( ) . create_action ( ' import_chat ' , lambda * _ : self . import_chat ( ) , [ ' <primary>i ' ] )
2024-06-01 00:07:34 -06:00
self . get_application ( ) . create_action ( ' create_model_from_existing ' , lambda * _ : dialogs . create_model_from_existing ( self ) )
self . get_application ( ) . create_action ( ' create_model_from_file ' , lambda * _ : dialogs . create_model_from_file ( self ) )
2024-06-23 20:07:27 -06:00
self . get_application ( ) . create_action ( ' delete_chat ' , self . chat_actions )
self . get_application ( ) . create_action ( ' rename_chat ' , self . chat_actions )
2024-06-28 16:42:58 -06:00
self . get_application ( ) . create_action ( ' rename_current_chat ' , self . current_chat_actions )
2024-06-23 20:07:27 -06:00
self . get_application ( ) . create_action ( ' export_chat ' , self . chat_actions )
2024-06-28 15:39:26 -06:00
self . get_application ( ) . create_action ( ' export_current_chat ' , self . current_chat_actions )
2024-06-24 00:18:55 -06:00
self . message_text_view . connect ( " paste-clipboard " , self . on_clipboard_paste )
2024-05-27 21:21:27 -06:00
self . add_chat_button . connect ( " clicked " , lambda button : self . new_chat ( ) )
2024-06-21 17:16:59 -06:00
self . attachment_button . connect ( " clicked " , lambda button , file_filter = self . file_filter_attachments : dialogs . attach_file ( self , file_filter ) )
2024-06-01 00:07:34 -06:00
self . create_model_name . get_delegate ( ) . connect ( " insert-text " , self . check_alphanumeric )
2024-05-21 17:58:51 -06:00
self . remote_connection_entry . connect ( " entry-activated " , lambda entry : entry . set_css_classes ( [ ] ) )
2024-05-21 23:15:32 -06:00
self . remote_connection_switch . connect ( " notify " , lambda pspec , user_data : self . connection_switched ( ) )
self . background_switch . connect ( " notify " , lambda pspec , user_data : self . switch_run_on_background ( ) )
2024-06-24 21:14:29 -06:00
self . setup_model_dropdown ( )
2024-05-21 15:36:24 -06:00
if os . path . exists ( os . path . join ( self . config_dir , " server.json " ) ) :
with open ( os . path . join ( self . config_dir , " server.json " ) , " r " ) as f :
data = json . load ( f )
2024-05-21 17:58:51 -06:00
self . run_remote = data [ ' run_remote ' ]
2024-05-25 23:03:26 -06:00
local_instance . port = data [ ' local_port ' ]
2024-05-21 23:15:32 -06:00
self . remote_url = data [ ' remote_url ' ]
2024-06-28 21:29:36 -06:00
self . remote_bearer_token = data [ ' remote_bearer_token ' ] if ' remote_bearer_token ' in data else ' '
2024-05-21 23:15:32 -06:00
self . run_on_background = data [ ' run_on_background ' ]
2024-05-29 14:24:30 -06:00
#Model Tweaks
if " model_tweaks " in data : self . model_tweaks = data [ ' model_tweaks ' ]
2024-05-30 09:53:17 -06:00
self . temperature_spin . set_value ( self . model_tweaks [ ' temperature ' ] )
self . seed_spin . set_value ( self . model_tweaks [ ' seed ' ] )
self . keep_alive_spin . set_value ( self . model_tweaks [ ' keep_alive ' ] )
2024-06-03 16:31:03 -06:00
#Overrides
if " ollama_overrides " in data : local_instance . overrides = data [ ' ollama_overrides ' ]
2024-06-04 13:34:48 -06:00
for element in [
self . override_HSA_OVERRIDE_GFX_VERSION ,
self . override_CUDA_VISIBLE_DEVICES ,
self . override_HIP_VISIBLE_DEVICES ] :
override = element . get_name ( )
2024-06-04 12:55:31 -06:00
if override in local_instance . overrides :
element . set_text ( local_instance . overrides [ override ] )
2024-06-03 17:01:27 -06:00
2024-05-29 14:24:30 -06:00
2024-05-21 23:15:32 -06:00
self . background_switch . set_active ( self . run_on_background )
self . set_hide_on_close ( self . run_on_background )
self . remote_connection_entry . set_text ( self . remote_url )
2024-06-28 21:29:36 -06:00
self . remote_bearer_token_entry . set_text ( self . remote_bearer_token )
2024-05-21 17:58:51 -06:00
if self . run_remote :
2024-06-28 21:29:36 -06:00
connection_handler . bearer_token = self . remote_bearer_token
connection_handler . url = self . remote_url
2024-05-22 20:28:25 -06:00
self . remote_connection_switch . set_active ( True )
2024-05-21 17:58:51 -06:00
else :
2024-06-28 21:29:36 -06:00
connection_handler . bearer_token = None
2024-05-21 23:15:32 -06:00
self . remote_connection_switch . set_active ( False )
2024-05-25 23:03:26 -06:00
connection_handler . url = f " http://127.0.0.1: { local_instance . port } "
2024-05-28 11:24:50 -06:00
local_instance . start ( )
2024-05-19 12:33:24 -06:00
else :
2024-05-28 11:24:50 -06:00
local_instance . start ( )
2024-05-25 23:03:26 -06:00
connection_handler . url = f " http://127.0.0.1: { local_instance . port } "
2024-05-21 15:36:24 -06:00
self . welcome_dialog . present ( self )
2024-05-29 12:04:05 -06:00
if self . verify_connection ( ) is False : self . connection_error ( )
2024-05-19 17:09:58 -06:00
self . update_list_available_models ( )
2024-05-21 17:58:51 -06:00
self . load_history ( )
2024-05-19 00:30:05 -06:00
self . update_chat_list ( )