Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55a636f4d1 | ||
|
|
0fc8730272 | ||
|
|
61a2bc466e | ||
|
|
62b1923bf4 | ||
|
|
8e25376a12 | ||
|
|
a9ab5d45a4 | ||
|
|
ce2a2f0b93 | ||
|
|
9cb6b0b665 | ||
|
|
dfc21fc0e9 | ||
|
|
19b089e6c6 | ||
|
|
02aa2734e0 | ||
|
|
66f9fd7231 | ||
|
|
1b125cb704 | ||
|
|
29f5d85c7b | ||
|
|
c192a1f31c | ||
|
|
3b20daf807 | ||
|
|
760c00e8ae |
@@ -22,7 +22,7 @@ Alpaca is an [Ollama](https://github.com/ollama/ollama) client where you can man
|
|||||||
- Import / Export chats
|
- Import / Export chats
|
||||||
- Delete / Edit messages
|
- Delete / Edit messages
|
||||||
- YouTube recognition (Ask questions about a YouTube video using the transcript)
|
- YouTube recognition (Ask questions about a YouTube video using the transcript)
|
||||||
- Website recognition (Ask questions about a certain question by parsing the url)
|
- Website recognition (Ask questions about a certain website by parsing the url)
|
||||||
|
|
||||||
## Screenies
|
## Screenies
|
||||||
Chatting with a model | Image recognition | Code highlighting
|
Chatting with a model | Image recognition | Code highlighting
|
||||||
|
|||||||
@@ -70,7 +70,9 @@
|
|||||||
<caption>Multiple models being downloaded</caption>
|
<caption>Multiple models being downloaded</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<content_rating type="oars-1.1" />
|
<content_rating type="oars-1.1">
|
||||||
|
<content_attribute id="money-purchasing">mild</content_attribute>
|
||||||
|
</content_rating>
|
||||||
<url type="bugtracker">https://github.com/Jeffser/Alpaca/issues</url>
|
<url type="bugtracker">https://github.com/Jeffser/Alpaca/issues</url>
|
||||||
<url type="homepage">https://jeffser.com/alpaca/</url>
|
<url type="homepage">https://jeffser.com/alpaca/</url>
|
||||||
<url type="donation">https://github.com/sponsors/Jeffser</url>
|
<url type="donation">https://github.com/sponsors/Jeffser</url>
|
||||||
@@ -78,6 +80,25 @@
|
|||||||
<url type="contribute">https://github.com/Jeffser/Alpaca/discussions/154</url>
|
<url type="contribute">https://github.com/Jeffser/Alpaca/discussions/154</url>
|
||||||
<url type="vcs-browser">https://github.com/Jeffser/Alpaca</url>
|
<url type="vcs-browser">https://github.com/Jeffser/Alpaca</url>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.0.3" date="2024-08-01">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.3</url>
|
||||||
|
<description>
|
||||||
|
<p>New</p>
|
||||||
|
<ul>
|
||||||
|
<li>Bearer Token entry on connection error dialog</li>
|
||||||
|
<li>Small appearance changes</li>
|
||||||
|
<li>Compatibility with code blocks without explicit language</li>
|
||||||
|
<li>Rare, optional and dismissible support dialog</li>
|
||||||
|
</ul>
|
||||||
|
<p>Fixes</p>
|
||||||
|
<ul>
|
||||||
|
<li>Date format for Simplified Chinese translation</li>
|
||||||
|
<li>Bug with unsupported localizations</li>
|
||||||
|
<li>Min height being too large to be used on mobile</li>
|
||||||
|
<li>Remote connection checker bug</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="1.0.2" date="2024-07-29">
|
<release version="1.0.2" date="2024-07-29">
|
||||||
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.2</url>
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.2</url>
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
project('Alpaca', 'c',
|
project('Alpaca', 'c',
|
||||||
version: '1.0.2',
|
version: '1.0.3',
|
||||||
meson_version: '>= 0.62.0',
|
meson_version: '>= 0.62.0',
|
||||||
default_options: [ 'warning_level=2', 'werror=false', ],
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -172,26 +172,38 @@ def remove_attached_file(self, name):
|
|||||||
|
|
||||||
# RECONNECT REMOTE | WORKS
|
# RECONNECT REMOTE | WORKS
|
||||||
|
|
||||||
def reconnect_remote_response(self, dialog, task, entry):
|
def reconnect_remote_response(self, dialog, task, url_entry, bearer_entry):
|
||||||
response = dialog.choose_finish(task)
|
response = dialog.choose_finish(task)
|
||||||
if not task or response == "remote":
|
if not task or response == "remote":
|
||||||
self.connect_remote(entry.get_text())
|
self.connect_remote(url_entry.get_text(), bearer_entry.get_text())
|
||||||
elif response == "local":
|
elif response == "local":
|
||||||
self.connect_local()
|
self.connect_local()
|
||||||
elif response == "close":
|
elif response == "close":
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def reconnect_remote(self, current_url):
|
def reconnect_remote(self, current_url, current_bearer_token):
|
||||||
entry = Gtk.Entry(
|
entry_url = Gtk.Entry(
|
||||||
css_classes = ["error"],
|
css_classes = ["error"],
|
||||||
text = current_url
|
text = current_url,
|
||||||
|
placeholder_text = "URL"
|
||||||
)
|
)
|
||||||
|
entry_bearer_token = Gtk.Entry(
|
||||||
|
css_classes = ["error"] if current_bearer_token else None,
|
||||||
|
text = current_bearer_token,
|
||||||
|
placeholder_text = "Bearer Token (Optional)"
|
||||||
|
)
|
||||||
|
container = Gtk.Box(
|
||||||
|
orientation = 1,
|
||||||
|
spacing = 10
|
||||||
|
)
|
||||||
|
container.append(entry_url)
|
||||||
|
container.append(entry_bearer_token)
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Connection Error"),
|
heading=_("Connection Error"),
|
||||||
body=_("The remote instance has disconnected"),
|
body=_("The remote instance has disconnected"),
|
||||||
extra_child=entry
|
extra_child=container
|
||||||
)
|
)
|
||||||
entry.connect("activate", lambda entry, dialog: reconnect_remote_response(self, dialog, None, entry))
|
#entry.connect("activate", lambda entry, dialog: reconnect_remote_response(self, dialog, None, entry))
|
||||||
dialog.add_response("close", _("Close Alpaca"))
|
dialog.add_response("close", _("Close Alpaca"))
|
||||||
dialog.add_response("local", _("Use local instance"))
|
dialog.add_response("local", _("Use local instance"))
|
||||||
dialog.add_response("remote", _("Connect"))
|
dialog.add_response("remote", _("Connect"))
|
||||||
@@ -199,7 +211,7 @@ def reconnect_remote(self, current_url):
|
|||||||
dialog.choose(
|
dialog.choose(
|
||||||
parent = self,
|
parent = self,
|
||||||
cancellable = None,
|
cancellable = None,
|
||||||
callback = lambda dialog, task, entry=entry: reconnect_remote_response(self, dialog, task, entry)
|
callback = lambda dialog, task, url_entry=entry_url, bearer_entry=entry_bearer_token: reconnect_remote_response(self, dialog, task, url_entry, bearer_entry)
|
||||||
)
|
)
|
||||||
|
|
||||||
# CREATE MODEL | WORKS
|
# CREATE MODEL | WORKS
|
||||||
@@ -357,3 +369,31 @@ def attach_website(self, url):
|
|||||||
cancellable = None,
|
cancellable = None,
|
||||||
callback = lambda dialog, task, url=url: attach_website_response(self, dialog, task, url)
|
callback = lambda dialog, task, url=url: attach_website_response(self, dialog, task, url)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Begging for money :3
|
||||||
|
|
||||||
|
def support_response(self, dialog, task):
|
||||||
|
res = dialog.choose_finish(task)
|
||||||
|
if res == 'later': return
|
||||||
|
elif res == 'support':
|
||||||
|
self.show_toast(_("Thank you!"), self.main_overlay)
|
||||||
|
os.system('xdg-open https://github.com/sponsors/Jeffser')
|
||||||
|
self.show_support = False
|
||||||
|
self.save_server_config()
|
||||||
|
|
||||||
|
def support(self):
|
||||||
|
dialog = Adw.AlertDialog(
|
||||||
|
heading=_("Support"),
|
||||||
|
body=_("Are you enjoying Alpaca? Consider sponsoring the project!"),
|
||||||
|
close_response="nope"
|
||||||
|
)
|
||||||
|
dialog.add_response("nope", _("Don't show again"))
|
||||||
|
dialog.set_response_appearance("nope", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
|
dialog.add_response("later", _("Later"))
|
||||||
|
dialog.add_response("support", _("Support"))
|
||||||
|
dialog.set_response_appearance("support", Adw.ResponseAppearance.SUGGESTED)
|
||||||
|
dialog.choose(
|
||||||
|
parent = self,
|
||||||
|
cancellable = None,
|
||||||
|
callback = lambda dialog, task: support_response(self, dialog, task)
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,3 +12,7 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
.chat_row:selected {
|
||||||
|
background: mix(@theme_bg_color, @theme_selected_bg_color, 0.3);
|
||||||
|
color: mix(@window_fg_color, @theme_selected_bg_color, 0.5);
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import gi
|
|||||||
gi.require_version('GtkSource', '5')
|
gi.require_version('GtkSource', '5')
|
||||||
gi.require_version('GdkPixbuf', '2.0')
|
gi.require_version('GdkPixbuf', '2.0')
|
||||||
from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf
|
from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf
|
||||||
import json, requests, threading, os, re, base64, sys, gettext, locale, subprocess, uuid, shutil, tarfile, tempfile, logging
|
import json, requests, threading, os, re, base64, sys, gettext, locale, subprocess, uuid, shutil, tarfile, tempfile, logging, random
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -44,7 +44,6 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale')
|
localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale')
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
|
||||||
gettext.bindtextdomain('com.jeffser.Alpaca', localedir)
|
gettext.bindtextdomain('com.jeffser.Alpaca', localedir)
|
||||||
gettext.textdomain('com.jeffser.Alpaca')
|
gettext.textdomain('com.jeffser.Alpaca')
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
@@ -61,6 +60,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
pulling_models = {}
|
pulling_models = {}
|
||||||
chats = {"chats": {_("New Chat"): {"messages": {}}}, "selected_chat": "New Chat", "order": []}
|
chats = {"chats": {_("New Chat"): {"messages": {}}}, "selected_chat": "New Chat", "order": []}
|
||||||
attachments = {}
|
attachments = {}
|
||||||
|
show_support = True
|
||||||
|
|
||||||
#Override elements
|
#Override elements
|
||||||
override_HSA_OVERRIDE_GFX_VERSION = Gtk.Template.Child()
|
override_HSA_OVERRIDE_GFX_VERSION = Gtk.Template.Child()
|
||||||
@@ -167,7 +167,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
buffer = self.editing_message["text_view"].get_buffer()
|
buffer = self.editing_message["text_view"].get_buffer()
|
||||||
text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False).rstrip('\n')
|
text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False).rstrip('\n')
|
||||||
footer = "<small>" + self.editing_message["footer"] + "</small>"
|
footer = "<small>" + self.editing_message["footer"] + "</small>"
|
||||||
buffer.insert_markup(buffer.get_end_iter(), footer, len(footer))
|
buffer.insert_markup(buffer.get_end_iter(), footer, len(footer.encode('utf-8')))
|
||||||
self.chats["chats"][self.chats["selected_chat"]]["messages"][self.editing_message["id"]]["content"] = text
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][self.editing_message["id"]]["content"] = text
|
||||||
self.editing_message = None
|
self.editing_message = None
|
||||||
self.save_history()
|
self.save_history()
|
||||||
@@ -229,7 +229,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
#self.attachments[name] = {"path": file_path, "type": file_type, "content": content}
|
#self.attachments[name] = {"path": file_path, "type": file_type, "content": content}
|
||||||
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)
|
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)
|
||||||
formated_date = self.generate_datetime_format(current_datetime)
|
formated_date = GLib.markup_escape_text(self.generate_datetime_format(current_datetime))
|
||||||
self.show_message(raw_message, False, f"\n\n<small>{formated_date}</small>", attached_images, attached_files, id=id)
|
self.show_message(raw_message, False, f"\n\n<small>{formated_date}</small>", attached_images, attached_files, id=id)
|
||||||
self.message_text_view.get_buffer().set_text("", 0)
|
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.loading_spinner = Gtk.Spinner(spinning=True, margin_top=12, margin_bottom=12, hexpand=True)
|
||||||
@@ -290,6 +290,9 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
@Gtk.Template.Callback()
|
@Gtk.Template.Callback()
|
||||||
def change_remote_url(self, entry):
|
def change_remote_url(self, entry):
|
||||||
|
if not entry.get_text().startswith("http"):
|
||||||
|
entry.set_text("http://{}".format(entry.get_text()))
|
||||||
|
return
|
||||||
self.remote_url = entry.get_text()
|
self.remote_url = entry.get_text()
|
||||||
logger.debug(f"Changing remote url: {self.remote_url}")
|
logger.debug(f"Changing remote url: {self.remote_url}")
|
||||||
if self.run_remote:
|
if self.run_remote:
|
||||||
@@ -596,7 +599,7 @@ Generate a title following these rules:
|
|||||||
)
|
)
|
||||||
message_buffer = message_text.get_buffer()
|
message_buffer = message_text.get_buffer()
|
||||||
message_buffer.insert(message_buffer.get_end_iter(), msg)
|
message_buffer.insert(message_buffer.get_end_iter(), msg)
|
||||||
if footer is not None: message_buffer.insert_markup(message_buffer.get_end_iter(), footer, len(footer))
|
if footer is not None: message_buffer.insert_markup(message_buffer.get_end_iter(), footer, len(footer.encode('utf-8')))
|
||||||
|
|
||||||
delete_button = Gtk.Button(
|
delete_button = Gtk.Button(
|
||||||
icon_name = "user-trash-symbolic",
|
icon_name = "user-trash-symbolic",
|
||||||
@@ -778,14 +781,18 @@ Generate a title following these rules:
|
|||||||
|
|
||||||
def save_server_config(self):
|
def save_server_config(self):
|
||||||
with open(os.path.join(self.config_dir, "server.json"), "w+") as f:
|
with open(os.path.join(self.config_dir, "server.json"), "w+") as f:
|
||||||
json.dump({'remote_url': self.remote_url, 'remote_bearer_token': self.remote_bearer_token, 'run_remote': self.run_remote, 'local_port': local_instance.port, 'run_on_background': self.run_on_background, 'model_tweaks': self.model_tweaks, 'ollama_overrides': local_instance.overrides}, f, indent=6)
|
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, 'show_support': self.show_support}, f, indent=6)
|
||||||
|
|
||||||
def verify_connection(self):
|
def verify_connection(self):
|
||||||
|
try:
|
||||||
response = connection_handler.simple_get(f"{connection_handler.url}/api/tags")
|
response = connection_handler.simple_get(f"{connection_handler.url}/api/tags")
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
self.save_server_config()
|
self.save_server_config()
|
||||||
self.update_list_local_models()
|
self.update_list_local_models()
|
||||||
return response.status_code == 200
|
return response.status_code == 200
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return False
|
||||||
|
|
||||||
def add_code_blocks(self):
|
def add_code_blocks(self):
|
||||||
text = self.bot_message.get_text(self.bot_message.get_start_iter(), self.bot_message.get_end_iter(), True)
|
text = self.bot_message.get_text(self.bot_message.get_start_iter(), self.bot_message.get_end_iter(), True)
|
||||||
@@ -803,13 +810,23 @@ Generate a title following these rules:
|
|||||||
code_text = match.group(2)
|
code_text = match.group(2)
|
||||||
parts.append({"type": "code", "text": code_text, "language": language})
|
parts.append({"type": "code", "text": code_text, "language": language})
|
||||||
pos = end
|
pos = end
|
||||||
|
# Match code blocks without language
|
||||||
|
no_lang_code_block_pattern = re.compile(r'`\n(.*?)\n`', re.DOTALL)
|
||||||
|
for match in no_lang_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()})
|
||||||
|
code_text = match.group(1)
|
||||||
|
parts.append({"type": "code", "text": code_text, "language": None})
|
||||||
|
pos = end
|
||||||
# Extract any remaining normal text after the last code block
|
# Extract any remaining normal text after the last code block
|
||||||
if pos < len(text):
|
if pos < len(text):
|
||||||
normal_text = text[pos:]
|
normal_text = text[pos:]
|
||||||
if normal_text.strip():
|
if normal_text.strip():
|
||||||
parts.append({"type": "normal", "text": normal_text.strip()})
|
parts.append({"type": "normal", "text": normal_text.strip()})
|
||||||
bold_pattern = re.compile(r'\*\*(.*?)\*\*') #"**text**"
|
bold_pattern = re.compile(r'\*\*(.*?)\*\*') #"**text**"
|
||||||
code_pattern = re.compile(r'`(.*?)`') #"`text`"
|
code_pattern = re.compile(r'`([^`\n]*?)`') #"`text`"
|
||||||
h1_pattern = re.compile(r'^#\s(.*)$') #"# text"
|
h1_pattern = re.compile(r'^#\s(.*)$') #"# text"
|
||||||
h2_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
|
markup_pattern = re.compile(r'<(b|u|tt|span.*)>(.*?)<\/(b|u|tt|span)>') #heh butt span, I'm so funny
|
||||||
@@ -843,16 +860,18 @@ Generate a title following these rules:
|
|||||||
start, end = match.span()
|
start, end = match.span()
|
||||||
if position < start:
|
if position < start:
|
||||||
message_buffer.insert(message_buffer.get_end_iter(), part['text'][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)))
|
message_buffer.insert_markup(message_buffer.get_end_iter(), match.group(0), len(match.group(0).encode('utf-8')))
|
||||||
position = end
|
position = end
|
||||||
|
|
||||||
if position < len(part['text']):
|
if position < len(part['text']):
|
||||||
message_buffer.insert(message_buffer.get_end_iter(), part['text'][position:])
|
message_buffer.insert(message_buffer.get_end_iter(), part['text'][position:])
|
||||||
|
|
||||||
if footer: message_buffer.insert_markup(message_buffer.get_end_iter(), footer, len(footer))
|
if footer: message_buffer.insert_markup(message_buffer.get_end_iter(), footer, len(footer.encode('utf-8')))
|
||||||
|
|
||||||
self.bot_message_box.append(message_text)
|
self.bot_message_box.append(message_text)
|
||||||
else:
|
else:
|
||||||
|
language = None
|
||||||
|
if part['language']:
|
||||||
language = GtkSource.LanguageManager.get_default().get_language(part['language'])
|
language = GtkSource.LanguageManager.get_default().get_language(part['language'])
|
||||||
if language:
|
if language:
|
||||||
buffer = GtkSource.Buffer.new_with_language(language)
|
buffer = GtkSource.Buffer.new_with_language(language)
|
||||||
@@ -871,7 +890,7 @@ Generate a title following these rules:
|
|||||||
source_view.set_editable(False)
|
source_view.set_editable(False)
|
||||||
code_block_box = Gtk.Box(css_classes=["card"], orientation=1, overflow=1)
|
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 = 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))
|
title_box.append(Gtk.Label(label=language.get_name() if language else _("Code Block"), hexpand=True, xalign=0))
|
||||||
copy_button = Gtk.Button(icon_name="edit-copy-symbolic", css_classes=["flat", "circular"], tooltip_text=_("Copy Message"))
|
copy_button = Gtk.Button(icon_name="edit-copy-symbolic", css_classes=["flat", "circular"], tooltip_text=_("Copy Message"))
|
||||||
copy_button.connect("clicked", self.on_copy_code_clicked, buffer)
|
copy_button.connect("clicked", self.on_copy_code_clicked, buffer)
|
||||||
title_box.append(copy_button)
|
title_box.append(copy_button)
|
||||||
@@ -917,9 +936,9 @@ Generate a title following these rules:
|
|||||||
if id not in self.chats["chats"][self.chats["selected_chat"]]["messages"] or vadjustment.get_value() + 50 >= vadjustment.get_upper() - vadjustment.get_page_size():
|
if id not in self.chats["chats"][self.chats["selected_chat"]]["messages"] or vadjustment.get_value() + 50 >= vadjustment.get_upper() - vadjustment.get_page_size():
|
||||||
GLib.idle_add(vadjustment.set_value, vadjustment.get_upper())
|
GLib.idle_add(vadjustment.set_value, vadjustment.get_upper())
|
||||||
if data['done']:
|
if data['done']:
|
||||||
formated_date = self.generate_datetime_format(datetime.strptime(self.chats["chats"][self.chats["selected_chat"]]["messages"][id]["date"], '%Y/%m/%d %H:%M:%S'))
|
formated_date = GLib.markup_escape_text(self.generate_datetime_format(datetime.strptime(self.chats["chats"][self.chats["selected_chat"]]["messages"][id]["date"], '%Y/%m/%d %H:%M:%S')))
|
||||||
text = f"\n\n<small>{data['model'].split(':')[0].replace('-', ' ').title()} ({data['model'].split(':')[1]})\n{formated_date}</small>"
|
text = f"\n\n{data['model'].split(':')[0].replace('-', ' ').title()} ({data['model'].split(':')[1]})\n<small>{formated_date}</small>"
|
||||||
GLib.idle_add(self.bot_message.insert_markup, self.bot_message.get_end_iter(), text, len(text))
|
GLib.idle_add(self.bot_message.insert_markup, self.bot_message.get_end_iter(), text, len(text.encode('utf-8')))
|
||||||
self.save_history()
|
self.save_history()
|
||||||
GLib.idle_add(self.bot_message_button_container.set_visible, True)
|
GLib.idle_add(self.bot_message_button_container.set_visible, True)
|
||||||
#Notification
|
#Notification
|
||||||
@@ -1082,11 +1101,11 @@ Generate a title following these rules:
|
|||||||
for widget in list(self.chat_container): self.chat_container.remove(widget)
|
for widget in list(self.chat_container): self.chat_container.remove(widget)
|
||||||
for key, message in self.chats['chats'][self.chats["selected_chat"]]['messages'].items():
|
for key, message in self.chats['chats'][self.chats["selected_chat"]]['messages'].items():
|
||||||
if message:
|
if message:
|
||||||
formated_date = self.generate_datetime_format(datetime.strptime(message['date'] + (":00" if message['date'].count(":") == 1 else ""), '%Y/%m/%d %H:%M:%S'))
|
formated_date = GLib.markup_escape_text(self.generate_datetime_format(datetime.strptime(message['date'] + (":00" if message['date'].count(":") == 1 else ""), '%Y/%m/%d %H:%M:%S')))
|
||||||
if message['role'] == 'user':
|
if message['role'] == 'user':
|
||||||
self.show_message(message['content'], False, f"\n\n<small>{formated_date}</small>", message['images'] if 'images' in message else None, message['files'] if 'files' in message else None, id=key)
|
self.show_message(message['content'], False, f"\n\n<small>{formated_date}</small>", message['images'] if 'images' in message else None, message['files'] if 'files' in message else None, id=key)
|
||||||
else:
|
else:
|
||||||
self.show_message(message['content'], True, f"\n\n<small>{message['model'].split(':')[0].replace('-', ' ').title()} ({message['model'].split(':')[1]})\n{formated_date}</small>", id=key)
|
self.show_message(message['content'], True, f"\n\n{message['model'].split(':')[0].replace('-', ' ').title()} ({message['model'].split(':')[1]})\n<small>{formated_date}</small>", id=key)
|
||||||
self.add_code_blocks()
|
self.add_code_blocks()
|
||||||
self.bot_message = None
|
self.bot_message = None
|
||||||
|
|
||||||
@@ -1234,9 +1253,10 @@ Generate a title following these rules:
|
|||||||
logger.debug("Showing preferences dialog")
|
logger.debug("Showing preferences dialog")
|
||||||
self.preferences_dialog.present(self)
|
self.preferences_dialog.present(self)
|
||||||
|
|
||||||
def connect_remote(self, url):
|
def connect_remote(self, url, bearer_token):
|
||||||
logger.debug(f"Connecting to remote: {url}")
|
logger.debug(f"Connecting to remote: {url}")
|
||||||
connection_handler.url = url
|
connection_handler.url = url
|
||||||
|
connection_handler.bearer_token = bearer_token
|
||||||
self.remote_url = connection_handler.url
|
self.remote_url = connection_handler.url
|
||||||
self.remote_connection_entry.set_text(self.remote_url)
|
self.remote_connection_entry.set_text(self.remote_url)
|
||||||
if self.verify_connection() == False: self.connection_error()
|
if self.verify_connection() == False: self.connection_error()
|
||||||
@@ -1253,7 +1273,7 @@ Generate a title following these rules:
|
|||||||
def connection_error(self):
|
def connection_error(self):
|
||||||
logger.error("Connection error")
|
logger.error("Connection error")
|
||||||
if self.run_remote:
|
if self.run_remote:
|
||||||
dialogs.reconnect_remote(self, connection_handler.url)
|
dialogs.reconnect_remote(self, connection_handler.url, connection_handler.bearer_token)
|
||||||
else:
|
else:
|
||||||
local_instance.reset()
|
local_instance.reset()
|
||||||
self.show_toast(_("There was an error with the local Ollama instance, so it has been reset"), self.main_overlay)
|
self.show_toast(_("There was an error with the local Ollama instance, so it has been reset"), self.main_overlay)
|
||||||
@@ -1558,7 +1578,11 @@ Generate a title following these rules:
|
|||||||
if override in local_instance.overrides:
|
if override in local_instance.overrides:
|
||||||
element.set_text(local_instance.overrides[override])
|
element.set_text(local_instance.overrides[override])
|
||||||
|
|
||||||
|
#Support dialog
|
||||||
|
if 'show_support' not in data or data['show_support']:
|
||||||
|
if random.randint(0, 99) == 0:
|
||||||
|
dialogs.support(self)
|
||||||
|
if 'show_support' in data: self.show_support = data['show_support']
|
||||||
self.background_switch.set_active(self.run_on_background)
|
self.background_switch.set_active(self.run_on_background)
|
||||||
self.set_hide_on_close(self.run_on_background)
|
self.set_hide_on_close(self.run_on_background)
|
||||||
self.remote_connection_entry.set_text(self.remote_url)
|
self.remote_connection_entry.set_text(self.remote_url)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<signal name="close-request" handler="closing_app"/>
|
<signal name="close-request" handler="closing_app"/>
|
||||||
<property name="resizable">True</property>
|
<property name="resizable">True</property>
|
||||||
<property name="width-request">360</property>
|
<property name="width-request">360</property>
|
||||||
<property name="height-request">700</property>
|
<property name="height-request">400</property>
|
||||||
<property name="default-width">1300</property>
|
<property name="default-width">1300</property>
|
||||||
<property name="default-height">800</property>
|
<property name="default-height">800</property>
|
||||||
<property name="title">Alpaca</property>
|
<property name="title">Alpaca</property>
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
<property name="valign">3</property>
|
<property name="valign">3</property>
|
||||||
<property name="tooltip-text" translatable="yes">Send Message</property>
|
<property name="tooltip-text" translatable="yes">Send Message</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="suggested-action"/>
|
<class name="accent"/>
|
||||||
<class name="circular"/>
|
<class name="circular"/>
|
||||||
</style>
|
</style>
|
||||||
<child>
|
<child>
|
||||||
|
|||||||
Reference in New Issue
Block a user