Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c0ec3957f | ||
|
|
72063a15d9 | ||
|
|
0d1b15aafc | ||
|
|
ca10369bdc | ||
|
|
42af75d8d2 | ||
|
|
a02871dd28 | ||
|
|
e65a8bc648 | ||
|
|
b373b6a34f | ||
|
|
6d6a0255e2 | ||
|
|
003d6a3d5f | ||
|
|
77a2c60fe5 | ||
|
|
ac3bd699ee | ||
|
|
596498c81e | ||
|
|
c95f764c77 | ||
|
|
5c5be05843 | ||
|
|
3fb26ec49e | ||
|
|
3f767d22e9 | ||
|
|
7f3fb0d82d | ||
|
|
d56c132459 | ||
|
|
acdce762c9 | ||
|
|
bd557d9652 | ||
|
|
3363d13fa0 | ||
|
|
52ba44e260 | ||
|
|
f06c2dae23 |
@@ -59,7 +59,7 @@ You can change anything except `$HOME` and `$OLLAMA_HOST`, to do this go to `~/.
|
|||||||
- [TylerLaBree](https://github.com/TylerLaBree) for their requests and ideas
|
- [TylerLaBree](https://github.com/TylerLaBree) for their requests and ideas
|
||||||
- [Alexkdeveloper](https://github.com/alexkdeveloper) for their help translating the app to Russian
|
- [Alexkdeveloper](https://github.com/alexkdeveloper) for their help translating the app to Russian
|
||||||
- [Imbev](https://github.com/imbev) for their reports and suggestions
|
- [Imbev](https://github.com/imbev) for their reports and suggestions
|
||||||
- [Nokse](https://github.com/Nokse22) for their contributions to the UI
|
- [Nokse](https://github.com/Nokse22) for their contributions to the UI and table rendering
|
||||||
- [Louis Chauvet-Villaret](https://github.com/loulou64490) for their suggestions and help translating the app to French
|
- [Louis Chauvet-Villaret](https://github.com/loulou64490) for their suggestions and help translating the app to French
|
||||||
- [CounterFlow64](https://github.com/CounterFlow64) for their help translating the app to Norwegian
|
- [CounterFlow64](https://github.com/CounterFlow64) for their help translating the app to Norwegian
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,42 @@
|
|||||||
<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.5" date="2024-08-02">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.5</url>
|
||||||
|
<description>
|
||||||
|
<p>New</p>
|
||||||
|
<ul>
|
||||||
|
<li>Regenerate any response, even if they are incomplete</li>
|
||||||
|
<li>Support for pulling models by name:tag</li>
|
||||||
|
<li>Stable support for GGUF model files</li>
|
||||||
|
<li>Restored sidebar toggle button</li>
|
||||||
|
</ul>
|
||||||
|
<p>Fixes</p>
|
||||||
|
<ul>
|
||||||
|
<li>Reverted back to standard styles</li>
|
||||||
|
<li>Fixed generated titles having "'S" for some reason</li>
|
||||||
|
<li>Changed min width for model dropdown</li>
|
||||||
|
<li>Changed message entry shadow</li>
|
||||||
|
<li>The last model used is now restored when the user changes chat</li>
|
||||||
|
<li>Better check for message finishing</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<release version="1.0.4" date="2024-08-01">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.4</url>
|
||||||
|
<description>
|
||||||
|
<p>New</p>
|
||||||
|
<ul>
|
||||||
|
<li>Added table rendering (Thanks Nokse)</li>
|
||||||
|
</ul>
|
||||||
|
<p>Fixes</p>
|
||||||
|
<ul>
|
||||||
|
<li>Made support dialog more common</li>
|
||||||
|
<li>Dialog title on tag chooser when downloading models didn't display properly</li>
|
||||||
|
<li>Prevent chat generation from generating a title with multiple lines</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="1.0.3" date="2024-08-01">
|
<release version="1.0.3" date="2024-08-01">
|
||||||
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.3</url>
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/1.0.3</url>
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
project('Alpaca', 'c',
|
project('Alpaca', 'c',
|
||||||
version: '1.0.3',
|
version: '1.0.5',
|
||||||
meson_version: '>= 0.62.0',
|
meson_version: '>= 0.62.0',
|
||||||
default_options: [ 'warning_level=2', 'werror=false', ],
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
)
|
)
|
||||||
|
|||||||
1218
po/alpaca.pot
1218
po/alpaca.pot
File diff suppressed because it is too large
Load Diff
1303
po/nb_NO.po
1303
po/nb_NO.po
File diff suppressed because it is too large
Load Diff
1235
po/pt_BR.po
1235
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
1247
po/zh_CN.po
1247
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,7 @@
|
|||||||
<file alias="icons/scalable/status/edit-find-symbolic.svg">icons/edit-find-symbolic.svg</file>
|
<file alias="icons/scalable/status/edit-find-symbolic.svg">icons/edit-find-symbolic.svg</file>
|
||||||
<file alias="icons/scalable/status/edit-symbolic.svg">icons/edit-symbolic.svg</file>
|
<file alias="icons/scalable/status/edit-symbolic.svg">icons/edit-symbolic.svg</file>
|
||||||
<file alias="icons/scalable/status/image-missing-symbolic.svg">icons/image-missing-symbolic.svg</file>
|
<file alias="icons/scalable/status/image-missing-symbolic.svg">icons/image-missing-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/update-symbolic.svg">icons/update-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks">window.ui</file>
|
<file preprocess="xml-stripblanks">window.ui</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ def create_model_from_existing_response(self, dialog, task, dropdown):
|
|||||||
def create_model_from_existing(self):
|
def create_model_from_existing(self):
|
||||||
string_list = Gtk.StringList()
|
string_list = Gtk.StringList()
|
||||||
for model in self.local_models:
|
for model in self.local_models:
|
||||||
string_list.append(model)
|
string_list.append(self.convert_model_name(model, 0))
|
||||||
|
|
||||||
dropdown = Gtk.DropDown()
|
dropdown = Gtk.DropDown()
|
||||||
dropdown.set_model(string_list)
|
dropdown.set_model(string_list)
|
||||||
@@ -257,6 +257,27 @@ def create_model_from_file(self):
|
|||||||
file_dialog = Gtk.FileDialog(default_filter=self.file_filter_gguf)
|
file_dialog = Gtk.FileDialog(default_filter=self.file_filter_gguf)
|
||||||
file_dialog.open(self, None, lambda file_dialog, result: create_model_from_file_response(self, file_dialog, result))
|
file_dialog.open(self, None, lambda file_dialog, result: create_model_from_file_response(self, file_dialog, result))
|
||||||
|
|
||||||
|
def create_model_from_name_response(self, dialog, task, entry):
|
||||||
|
model = entry.get_text().lower().strip()
|
||||||
|
if dialog.choose_finish(task) == 'accept' and model:
|
||||||
|
self.pull_model(model)
|
||||||
|
|
||||||
|
def create_model_from_name(self):
|
||||||
|
entry = Gtk.Entry()
|
||||||
|
entry.get_delegate().connect("insert-text", self.check_alphanumeric)
|
||||||
|
dialog = Adw.AlertDialog(
|
||||||
|
heading=_("Pull Model"),
|
||||||
|
body=_("Input the name of the model in this format\nname:tag"),
|
||||||
|
extra_child=entry
|
||||||
|
)
|
||||||
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
|
dialog.add_response("accept", _("Accept"))
|
||||||
|
dialog.set_response_appearance("accept", Adw.ResponseAppearance.SUGGESTED)
|
||||||
|
dialog.choose(
|
||||||
|
parent = self,
|
||||||
|
cancellable = None,
|
||||||
|
callback = lambda dialog, task, entry=entry: create_model_from_name_response(self, dialog, task, entry)
|
||||||
|
)
|
||||||
# FILE CHOOSER | WORKS
|
# FILE CHOOSER | WORKS
|
||||||
|
|
||||||
def attach_file_response(self, file_dialog, result):
|
def attach_file_response(self, file_dialog, result):
|
||||||
@@ -378,6 +399,8 @@ def support_response(self, dialog, task):
|
|||||||
elif res == 'support':
|
elif res == 'support':
|
||||||
self.show_toast(_("Thank you!"), self.main_overlay)
|
self.show_toast(_("Thank you!"), self.main_overlay)
|
||||||
os.system('xdg-open https://github.com/sponsors/Jeffser')
|
os.system('xdg-open https://github.com/sponsors/Jeffser')
|
||||||
|
elif res == 'nope':
|
||||||
|
self.show_toast(_("Visit Alpaca's website if you change your mind!"), self.main_overlay)
|
||||||
self.show_support = False
|
self.show_support = False
|
||||||
self.save_server_config()
|
self.save_server_config()
|
||||||
|
|
||||||
|
|||||||
2
src/icons/update-symbolic.svg
Normal file
2
src/icons/update-symbolic.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 7.957031 2 c -0.082031 0 -0.164062 0.003906 -0.246093 0.007812 c -0.1875 0.011719 -0.375 0.03125 -0.5625 0.0625 c -1.582032 0.226563 -3.007813 1.070313 -3.96875 2.34375 c -0.804688 1.074219 -1.183594 2.332032 -1.179688 3.585938 h 2.003906 c 0 -0.832031 0.253906 -1.671875 0.796875 -2.398438 c 1.335938 -1.777343 3.820313 -2.113281 5.597657 -0.78125 c 0.429687 0.320313 0.769531 0.734376 1.03125 1.1875 h -1.4375 c -0.550782 0 -1 0.449219 -1 1 v 1 h 6 v -6 h -1 c -0.550782 0 -1 0.449219 -1 1 v 1.6875 c -1.113282 -1.695312 -3.007813 -2.710937 -5.039063 -2.695312 z m 0 0"/><path d="m 8.035156 15.007812 c 0.082032 0 0.164063 -0.003906 0.246094 -0.007812 c 0.1875 -0.011719 0.375 -0.03125 0.5625 -0.0625 c 1.582031 -0.226562 3.007812 -1.066406 3.96875 -2.34375 c 0.804688 -1.074219 1.183594 -2.332031 1.179688 -3.585938 h -2.003907 c -0.003906 0.832032 -0.257812 1.675782 -0.796875 2.398438 c -1.335937 1.777344 -3.820312 2.113281 -5.597656 0.78125 c -0.429688 -0.320312 -0.769531 -0.734375 -1.03125 -1.1875 h 1.4375 c 0.550781 0 1 -0.449219 1 -1 v -1 h -6 v 6 h 1 c 0.550781 0 1 -0.449219 1 -1 v -1.6875 c 1.113281 1.695312 3.007812 2.710938 5.035156 2.695312 z m 0 0"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -43,7 +43,8 @@ alpaca_sources = [
|
|||||||
'dialogs.py',
|
'dialogs.py',
|
||||||
'local_instance.py',
|
'local_instance.py',
|
||||||
'available_models.json',
|
'available_models.json',
|
||||||
'available_models_descriptions.py'
|
'available_models_descriptions.py',
|
||||||
|
'table_widget.py'
|
||||||
]
|
]
|
||||||
|
|
||||||
install_data(alpaca_sources, install_dir: moduledir)
|
install_data(alpaca_sources, install_dir: moduledir)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
.message_text_view {
|
.message_text_view, .modelfile_textview {
|
||||||
background-color: rgba(0,0,0,0);
|
background-color: rgba(0,0,0,0);
|
||||||
}
|
}
|
||||||
.chat_image_button {
|
.chat_image_button {
|
||||||
@@ -12,7 +12,3 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|||||||
126
src/table_widget.py
Normal file
126
src/table_widget.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import gi
|
||||||
|
from gi.repository import Adw
|
||||||
|
from gi.repository import Gtk, GObject, Gio
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
class MarkdownTable:
|
||||||
|
def __init__(self):
|
||||||
|
self.headers = []
|
||||||
|
self.rows = Gio.ListStore()
|
||||||
|
self.alignments = []
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
table_repr = 'Headers: {}\n'.format(self.headers)
|
||||||
|
table_repr += 'Alignments: {}\n'.format(self.alignments)
|
||||||
|
table_repr += 'Rows:\n'
|
||||||
|
for row in self.rows:
|
||||||
|
table_repr += ' | '.join(row) + '\n'
|
||||||
|
return table_repr
|
||||||
|
|
||||||
|
class Row(GObject.GObject):
|
||||||
|
def __init__(self, _values):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.values = _values
|
||||||
|
|
||||||
|
def get_column_value(self, index):
|
||||||
|
return self.values[index]
|
||||||
|
|
||||||
|
class TableWidget(Gtk.Frame):
|
||||||
|
__gtype_name__ = 'TableWidget'
|
||||||
|
|
||||||
|
def __init__(self, markdown):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.table = MarkdownTable()
|
||||||
|
|
||||||
|
self.set_halign(Gtk.Align.START)
|
||||||
|
|
||||||
|
self.table_widget = Gtk.ColumnView(
|
||||||
|
show_column_separators=True,
|
||||||
|
show_row_separators=True,
|
||||||
|
reorderable=False,
|
||||||
|
)
|
||||||
|
scrolled_window = Gtk.ScrolledWindow(
|
||||||
|
vscrollbar_policy=Gtk.PolicyType.NEVER,
|
||||||
|
propagate_natural_width=True
|
||||||
|
)
|
||||||
|
self.set_child(scrolled_window)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.parse_markdown_table(markdown)
|
||||||
|
self.make_table()
|
||||||
|
scrolled_window.set_child(self.table_widget)
|
||||||
|
except:
|
||||||
|
label = Gtk.Label(
|
||||||
|
label=markdown.lstrip('\n').rstrip('\n'),
|
||||||
|
selectable=True,
|
||||||
|
margin_top=6,
|
||||||
|
margin_bottom=6,
|
||||||
|
margin_start=6,
|
||||||
|
margin_end=6
|
||||||
|
)
|
||||||
|
scrolled_window.set_child(label)
|
||||||
|
|
||||||
|
def parse_markdown_table(self, markdown_text):
|
||||||
|
# Define regex patterns for matching the table components
|
||||||
|
header_pattern = r'^\|(.+?)\|$'
|
||||||
|
separator_pattern = r'^\|(\s*[:-]+:?\s*\|)+$'
|
||||||
|
row_pattern = r'^\|(.+?)\|$'
|
||||||
|
|
||||||
|
# Split the text into lines
|
||||||
|
lines = markdown_text.strip().split('\n')
|
||||||
|
|
||||||
|
# Extract headers
|
||||||
|
header_match = re.match(header_pattern, lines[0], re.MULTILINE)
|
||||||
|
if header_match:
|
||||||
|
headers = [header.strip() for header in header_match.group(1).replace("*", "").split('|') if header.strip()]
|
||||||
|
self.table.headers = headers
|
||||||
|
|
||||||
|
# Extract alignments
|
||||||
|
separator_match = re.match(separator_pattern, lines[1], re.MULTILINE)
|
||||||
|
if separator_match:
|
||||||
|
alignments = []
|
||||||
|
separator_columns = lines[1].replace(" ", "").split('|')[1:-1]
|
||||||
|
for sep in separator_columns:
|
||||||
|
if ':' in sep:
|
||||||
|
if sep.startswith('-') and sep.endswith(':'):
|
||||||
|
alignments.append(1)
|
||||||
|
elif sep.startswith(':') and sep.endswith('-'):
|
||||||
|
alignments.append(0)
|
||||||
|
else:
|
||||||
|
alignments.append(0.5)
|
||||||
|
else:
|
||||||
|
alignments.append(0) # Default alignment is start
|
||||||
|
self.table.alignments = alignments
|
||||||
|
|
||||||
|
# Extract rows
|
||||||
|
for line in lines[2:]:
|
||||||
|
row_match = re.match(row_pattern, line, re.MULTILINE)
|
||||||
|
if row_match:
|
||||||
|
rows = line.split('|')[1:-1]
|
||||||
|
row = Row(rows)
|
||||||
|
self.table.rows.append(row)
|
||||||
|
|
||||||
|
def make_table(self):
|
||||||
|
|
||||||
|
def _on_factory_setup(_factory, list_item, align):
|
||||||
|
label = Gtk.Label(xalign=align, ellipsize=3, selectable=True)
|
||||||
|
list_item.set_child(label)
|
||||||
|
|
||||||
|
def _on_factory_bind(_factory, list_item, index):
|
||||||
|
label_widget = list_item.get_child()
|
||||||
|
row = list_item.get_item()
|
||||||
|
label_widget.set_label(row.get_column_value(index))
|
||||||
|
|
||||||
|
for index, column_name in enumerate(self.table.headers):
|
||||||
|
column = Gtk.ColumnViewColumn(title=column_name, expand=True)
|
||||||
|
factory = Gtk.SignalListItemFactory()
|
||||||
|
factory.connect("setup", _on_factory_setup, self.table.alignments[index])
|
||||||
|
factory.connect("bind", _on_factory_bind, index)
|
||||||
|
column.set_factory(factory)
|
||||||
|
self.table_widget.append_column(column)
|
||||||
|
|
||||||
|
selection = Gtk.NoSelection.new(model=self.table.rows)
|
||||||
|
self.table_widget.set_model(model=selection)
|
||||||
218
src/window.py
218
src/window.py
@@ -28,7 +28,7 @@ from PIL import Image
|
|||||||
from pypdf import PdfReader
|
from pypdf import PdfReader
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from . import dialogs, local_instance, connection_handler, available_models_descriptions
|
from . import dialogs, local_instance, connection_handler, available_models_descriptions
|
||||||
|
from .table_widget import TableWidget
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -68,11 +68,11 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
override_HIP_VISIBLE_DEVICES = Gtk.Template.Child()
|
override_HIP_VISIBLE_DEVICES = Gtk.Template.Child()
|
||||||
|
|
||||||
#Elements
|
#Elements
|
||||||
|
regenerate_button : Gtk.Button = None
|
||||||
create_model_base = Gtk.Template.Child()
|
create_model_base = Gtk.Template.Child()
|
||||||
create_model_name = Gtk.Template.Child()
|
create_model_name = Gtk.Template.Child()
|
||||||
create_model_system = Gtk.Template.Child()
|
create_model_system = Gtk.Template.Child()
|
||||||
create_model_template = Gtk.Template.Child()
|
create_model_modelfile = Gtk.Template.Child()
|
||||||
create_model_dialog = Gtk.Template.Child()
|
|
||||||
temperature_spin = Gtk.Template.Child()
|
temperature_spin = Gtk.Template.Child()
|
||||||
seed_spin = Gtk.Template.Child()
|
seed_spin = Gtk.Template.Child()
|
||||||
keep_alive_spin = Gtk.Template.Child()
|
keep_alive_spin = Gtk.Template.Child()
|
||||||
@@ -135,7 +135,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
def verify_if_image_can_be_used(self, pspec=None, user_data=None):
|
def verify_if_image_can_be_used(self, pspec=None, user_data=None):
|
||||||
logger.debug("Verifying if image can be used")
|
logger.debug("Verifying if image can be used")
|
||||||
if self.model_drop_down.get_selected_item() == None: return True
|
if self.model_drop_down.get_selected_item() == None: return True
|
||||||
selected = self.model_drop_down.get_selected_item().get_string().split(" (")[0].lower()
|
selected = self.convert_model_name(self.model_drop_down.get_selected_item().get_string(), 1).split(":")[0]
|
||||||
if selected in [key for key, value in self.available_models.items() if value["image"]]:
|
if selected in [key for key, value in self.available_models.items() if value["image"]]:
|
||||||
for name, content in self.attachments.items():
|
for name, content in self.attachments.items():
|
||||||
if content['type'] == 'image':
|
if content['type'] == 'image':
|
||||||
@@ -183,8 +183,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.chats['order'].remove(self.chats['selected_chat'])
|
self.chats['order'].remove(self.chats['selected_chat'])
|
||||||
self.chats['order'].insert(0, self.chats['selected_chat'])
|
self.chats['order'].insert(0, self.chats['selected_chat'])
|
||||||
self.save_history()
|
self.save_history()
|
||||||
current_model = self.model_drop_down.get_selected_item().get_string().split(' (')
|
current_model = self.convert_model_name(self.model_drop_down.get_selected_item().get_string(), 1)
|
||||||
current_model = '{}:{}'.format(current_model[0].replace(' ', '-').lower(), current_model[1][:-1])
|
|
||||||
if current_model is None:
|
if current_model is None:
|
||||||
self.show_toast(_("Please select a model before chatting"), self.main_overlay)
|
self.show_toast(_("Please select a model before chatting"), self.main_overlay)
|
||||||
return
|
return
|
||||||
@@ -282,8 +281,10 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.chats["selected_chat"] = row.get_child().get_name()
|
self.chats["selected_chat"] = row.get_child().get_name()
|
||||||
self.load_history_into_chat()
|
self.load_history_into_chat()
|
||||||
if len(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys()) > 0:
|
if len(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys()) > 0:
|
||||||
|
last_model_used = self.chats["chats"][self.chats["selected_chat"]]["messages"][list(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys())[-1]]["model"]
|
||||||
|
last_model_used = self.convert_model_name(last_model_used, 0)
|
||||||
for i in range(self.model_string_list.get_n_items()):
|
for i in range(self.model_string_list.get_n_items()):
|
||||||
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"]:
|
if self.model_string_list.get_string(i) == last_model_used:
|
||||||
self.model_drop_down.set_selected(i)
|
self.model_drop_down.set_selected(i)
|
||||||
break
|
break
|
||||||
self.save_history()
|
self.save_history()
|
||||||
@@ -343,19 +344,18 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
@Gtk.Template.Callback()
|
@Gtk.Template.Callback()
|
||||||
def create_model_start(self, button):
|
def create_model_start(self, button):
|
||||||
base = self.create_model_base.get_subtitle()
|
name = self.create_model_name.get_text().lower().replace(":", "")
|
||||||
name = self.create_model_name.get_text()
|
modelfile_buffer = self.create_model_modelfile.get_buffer()
|
||||||
system = self.create_model_system.get_text()
|
modelfile_raw = modelfile_buffer.get_text(modelfile_buffer.get_start_iter(), modelfile_buffer.get_end_iter(), False)
|
||||||
template = self.create_model_template.get_text()
|
modelfile = ["FROM {}".format(self.create_model_base.get_subtitle()), "SYSTEM {}".format(self.create_model_system.get_text())]
|
||||||
if "/" in base:
|
for line in modelfile_raw.split('\n'):
|
||||||
modelfile = f"FROM {base}\nSYSTEM {system}\nTEMPLATE {template}"
|
if not line.startswith('SYSTEM') and not line.startswith('FROM'):
|
||||||
else:
|
modelfile.append(line)
|
||||||
modelfile = f"FROM {base}\nSYSTEM {system}"
|
|
||||||
self.pulling_model_list_box.set_visible(True)
|
self.pulling_model_list_box.set_visible(True)
|
||||||
model_row = Adw.ActionRow(
|
model_row = Adw.ActionRow(
|
||||||
title = name
|
title = name
|
||||||
)
|
)
|
||||||
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": name, "modelfile": modelfile})
|
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": name, "modelfile": '\n'.join(modelfile)})
|
||||||
overlay = Gtk.Overlay()
|
overlay = Gtk.Overlay()
|
||||||
progress_bar = Gtk.ProgressBar(
|
progress_bar = Gtk.ProgressBar(
|
||||||
valign = 2,
|
valign = 2,
|
||||||
@@ -377,7 +377,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
overlay.set_child(model_row)
|
overlay.set_child(model_row)
|
||||||
overlay.add_overlay(progress_bar)
|
overlay.add_overlay(progress_bar)
|
||||||
self.pulling_model_list_box.append(overlay)
|
self.pulling_model_list_box.append(overlay)
|
||||||
self.create_model_dialog.close()
|
self.navigation_view_manage_models.pop()
|
||||||
self.manage_models_dialog.present(self)
|
self.manage_models_dialog.present(self)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
@@ -415,38 +415,37 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.available_model_list_box.set_visible(True)
|
self.available_model_list_box.set_visible(True)
|
||||||
self.no_results_page.set_visible(False)
|
self.no_results_page.set_visible(False)
|
||||||
|
|
||||||
|
def convert_model_name(self, name:str, mode:int) -> str: # mode=0 name:tag -> Name (tag) | mode=1 Name (tag) -> name:tag
|
||||||
|
if mode == 0: return "{} ({})".format(name.split(":")[0].replace("-", " ").title(), name.split(":")[1])
|
||||||
|
if mode == 1: return "{}:{}".format(name.split(" (")[0].replace(" ", "-").lower(), name.split(" (")[1][:-1])
|
||||||
|
|
||||||
def check_alphanumeric(self, editable, text, length, position):
|
def check_alphanumeric(self, editable, text, length, position):
|
||||||
new_text = ''.join([char for char in text if char.isalnum() or char in ['-', '_']])
|
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")
|
if new_text != text: editable.stop_emission_by_name("insert-text")
|
||||||
|
|
||||||
def create_model(self, model:str, file:bool):
|
def create_model(self, model:str, file:bool):
|
||||||
name = ""
|
modelfile_buffer = self.create_model_modelfile.get_buffer()
|
||||||
system = ""
|
modelfile_buffer.delete(modelfile_buffer.get_start_iter(), modelfile_buffer.get_end_iter())
|
||||||
template = ""
|
self.create_model_system.set_text('')
|
||||||
if not file:
|
if not file:
|
||||||
response = connection_handler.simple_post(f"{connection_handler.url}/api/show", json.dumps({"name": model}))
|
response = connection_handler.simple_post(f"{connection_handler.url}/api/show", json.dumps({"name": self.convert_model_name(model, 1)}))
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = json.loads(response.text)
|
data = json.loads(response.text)
|
||||||
|
modelfile = []
|
||||||
for line in data['modelfile'].split('\n'):
|
for line in data['modelfile'].split('\n'):
|
||||||
if line.startswith('SYSTEM'):
|
if line.startswith('SYSTEM'):
|
||||||
system = line[len('SYSTEM'):].strip()
|
self.create_model_system.set_text(line[len('SYSTEM'):].strip())
|
||||||
elif line.startswith('TEMPLATE'):
|
if not line.startswith('SYSTEM') and not line.startswith('FROM') and not line.startswith('#'):
|
||||||
template = line[len('TEMPLATE'):].strip()
|
modelfile.append(line)
|
||||||
self.create_model_template.set_sensitive(False)
|
self.create_model_name.set_text(self.convert_model_name(model, 1).split(':')[0] + "-custom")
|
||||||
name = model.split(':')[0]
|
modelfile_buffer.insert(modelfile_buffer.get_start_iter(), '\n'.join(modelfile), len('\n'.join(modelfile).encode('utf-8')))
|
||||||
else:
|
else:
|
||||||
self.create_model_template.set_sensitive(True)
|
##TODO ERROR MESSAGE
|
||||||
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|>"""'
|
return
|
||||||
name = model.split("/")[-1].split(".")[0]
|
else:
|
||||||
self.create_model_base.set_subtitle(model)
|
self.create_model_name.set_text(model.split("/")[-1].split(".")[0])
|
||||||
self.create_model_name.set_text(name)
|
self.create_model_base.set_subtitle(self.convert_model_name(model, 1))
|
||||||
self.create_model_system.set_text(system)
|
self.navigation_view_manage_models.push_by_tag('model_create_page')
|
||||||
self.create_model_template.set_text(template)
|
|
||||||
self.manage_models_dialog.close()
|
|
||||||
self.create_model_dialog.present(self)
|
|
||||||
|
|
||||||
|
|
||||||
def show_toast(self, message:str, overlay):
|
def show_toast(self, message:str, overlay):
|
||||||
logger.info(message)
|
logger.info(message)
|
||||||
@@ -574,13 +573,12 @@ Generate a title following these rules:
|
|||||||
```PROMPT
|
```PROMPT
|
||||||
{message['content']}
|
{message['content']}
|
||||||
```"""
|
```"""
|
||||||
current_model = self.model_drop_down.get_selected_item().get_string().split(' (')
|
current_model = self.convert_model_name(self.model_drop_down.get_selected_item().get_string(), 1)
|
||||||
current_model = '{}:{}'.format(current_model[0].replace(' ', '-').lower(), current_model[1][:-1])
|
|
||||||
data = {"model": current_model, "prompt": prompt, "stream": False}
|
data = {"model": current_model, "prompt": prompt, "stream": False}
|
||||||
if 'images' in message: data["images"] = message['images']
|
if 'images' in message: data["images"] = message['images']
|
||||||
response = connection_handler.simple_post(f"{connection_handler.url}/api/generate", data=json.dumps(data))
|
response = connection_handler.simple_post(f"{connection_handler.url}/api/generate", data=json.dumps(data))
|
||||||
|
|
||||||
new_chat_name = json.loads(response.text)["response"].strip().removeprefix("Title: ").removeprefix("title: ").strip('\'"').title()
|
new_chat_name = json.loads(response.text)["response"].strip().removeprefix("Title: ").removeprefix("title: ").strip('\'"').replace('\n', ' ').title().replace('\'S', '\'s')
|
||||||
new_chat_name = new_chat_name[:50] + (new_chat_name[50:] and '...')
|
new_chat_name = new_chat_name[:50] + (new_chat_name[50:] and '...')
|
||||||
self.rename_chat(label_element.get_name(), new_chat_name, label_element)
|
self.rename_chat(label_element.get_name(), new_chat_name, label_element)
|
||||||
|
|
||||||
@@ -616,6 +614,11 @@ Generate a title following these rules:
|
|||||||
css_classes = ["flat", "circular"],
|
css_classes = ["flat", "circular"],
|
||||||
tooltip_text = _("Edit Message")
|
tooltip_text = _("Edit Message")
|
||||||
)
|
)
|
||||||
|
regenerate_button = Gtk.Button(
|
||||||
|
icon_name = "update-symbolic",
|
||||||
|
css_classes = ["flat", "circular"],
|
||||||
|
tooltip_text = _("Regenerate Message")
|
||||||
|
)
|
||||||
|
|
||||||
button_container = Gtk.Box(
|
button_container = Gtk.Box(
|
||||||
orientation=0,
|
orientation=0,
|
||||||
@@ -731,9 +734,10 @@ Generate a title following these rules:
|
|||||||
delete_button.connect("clicked", lambda button, element=overlay: self.delete_message(element))
|
delete_button.connect("clicked", lambda button, element=overlay: self.delete_message(element))
|
||||||
copy_button.connect("clicked", lambda button, element=overlay: self.copy_message(element))
|
copy_button.connect("clicked", lambda button, element=overlay: self.copy_message(element))
|
||||||
edit_button.connect("clicked", lambda button, element=overlay, textview=message_text, button_container=button_container: self.edit_message(element, textview, button_container))
|
edit_button.connect("clicked", lambda button, element=overlay, textview=message_text, button_container=button_container: self.edit_message(element, textview, button_container))
|
||||||
|
regenerate_button.connect('clicked', lambda button, id=id, bot_message_box=message_box, bot_message_button_container=button_container : self.regenerate_message(id, bot_message_box, bot_message_button_container))
|
||||||
button_container.append(delete_button)
|
button_container.append(delete_button)
|
||||||
button_container.append(copy_button)
|
button_container.append(copy_button)
|
||||||
if not bot: button_container.append(edit_button)
|
button_container.append(regenerate_button if bot else edit_button)
|
||||||
overlay.add_overlay(button_container)
|
overlay.add_overlay(button_container)
|
||||||
self.chat_container.append(overlay)
|
self.chat_container.append(overlay)
|
||||||
|
|
||||||
@@ -756,25 +760,25 @@ Generate a title following these rules:
|
|||||||
else:
|
else:
|
||||||
self.local_model_list_box.set_visible(True)
|
self.local_model_list_box.set_visible(True)
|
||||||
for model in json.loads(response.text)['models']:
|
for model in json.loads(response.text)['models']:
|
||||||
|
model_name = self.convert_model_name(model["name"], 0)
|
||||||
model_row = Adw.ActionRow(
|
model_row = Adw.ActionRow(
|
||||||
title = "<b>{}</b>".format(model["name"].split(":")[0].replace("-", " ").title()),
|
title = "<b>{}</b>".format(model_name.split(" (")[0]),
|
||||||
subtitle = model["name"].split(":")[1]
|
subtitle = model_name.split(" (")[1][:-1]
|
||||||
)
|
)
|
||||||
button = Gtk.Button(
|
button = Gtk.Button(
|
||||||
icon_name = "user-trash-symbolic",
|
icon_name = "user-trash-symbolic",
|
||||||
vexpand = False,
|
vexpand = False,
|
||||||
valign = 3,
|
valign = 3,
|
||||||
css_classes = ["error", "circular"],
|
css_classes = ["error", "circular"],
|
||||||
tooltip_text = _("Remove '{} ({})'").format(model["name"].split(":")[0].replace('-', ' ').title(), model["name"].split(":")[1])
|
tooltip_text = _("Remove '{}'").format(model_name)
|
||||||
)
|
)
|
||||||
button.connect("clicked", lambda button=button, model_name=model["name"]: dialogs.delete_model(self, model_name))
|
button.connect("clicked", lambda button=button, model_name=model["name"]: dialogs.delete_model(self, model_name))
|
||||||
model_row.add_suffix(button)
|
model_row.add_suffix(button)
|
||||||
self.local_model_list_box.append(model_row)
|
self.local_model_list_box.append(model_row)
|
||||||
|
|
||||||
self.model_string_list.append(f"{model['name'].split(':')[0].replace('-', ' ').title()} ({model['name'].split(':')[1]})")
|
self.model_string_list.append(model_name)
|
||||||
self.local_models.append(model["name"])
|
self.local_models.append(model["name"])
|
||||||
self.model_drop_down.set_selected(0)
|
#self.verify_if_image_can_be_used()
|
||||||
self.verify_if_image_can_be_used()
|
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.connection_error()
|
self.connection_error()
|
||||||
@@ -820,6 +824,16 @@ Generate a title following these rules:
|
|||||||
code_text = match.group(1)
|
code_text = match.group(1)
|
||||||
parts.append({"type": "code", "text": code_text, "language": None})
|
parts.append({"type": "code", "text": code_text, "language": None})
|
||||||
pos = end
|
pos = end
|
||||||
|
# Match tables
|
||||||
|
table_pattern = re.compile(r'((\r?\n){2}|^)([^\r\n]*\|[^\r\n]*(\r?\n)?)+(?=(\r?\n){2}|$)', re.MULTILINE)
|
||||||
|
for match in table_pattern.finditer(text):
|
||||||
|
start, end = match.span()
|
||||||
|
if pos < start:
|
||||||
|
normal_text = text[pos:start]
|
||||||
|
parts.append({"type": "normal", "text": normal_text.strip()})
|
||||||
|
table_text = match.group(0)
|
||||||
|
parts.append({"type": "table", "text": table_text})
|
||||||
|
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:]
|
||||||
@@ -869,7 +883,7 @@ Generate a title following these rules:
|
|||||||
if footer: message_buffer.insert_markup(message_buffer.get_end_iter(), footer, len(footer.encode('utf-8')))
|
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:
|
elif part['type'] == 'code':
|
||||||
language = None
|
language = None
|
||||||
if part['language']:
|
if part['language']:
|
||||||
language = GtkSource.LanguageManager.get_default().get_language(part['language'])
|
language = GtkSource.LanguageManager.get_default().get_language(part['language'])
|
||||||
@@ -899,6 +913,9 @@ Generate a title following these rules:
|
|||||||
code_block_box.append(source_view)
|
code_block_box.append(source_view)
|
||||||
self.bot_message_box.append(code_block_box)
|
self.bot_message_box.append(code_block_box)
|
||||||
self.style_manager.connect("notify::dark", self.on_theme_changed, buffer)
|
self.style_manager.connect("notify::dark", self.on_theme_changed, buffer)
|
||||||
|
elif part['type'] == 'table':
|
||||||
|
table = TableWidget(part['text'])
|
||||||
|
self.bot_message_box.append(table)
|
||||||
vadjustment = self.chat_window.get_vadjustment()
|
vadjustment = self.chat_window.get_vadjustment()
|
||||||
vadjustment.set_value(vadjustment.get_upper())
|
vadjustment.set_value(vadjustment.get_upper())
|
||||||
self.bot_message = None
|
self.bot_message = None
|
||||||
@@ -935,9 +952,9 @@ Generate a title following these rules:
|
|||||||
vadjustment = self.chat_window.get_vadjustment()
|
vadjustment = self.chat_window.get_vadjustment()
|
||||||
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 'done' in data and data['done']:
|
||||||
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')))
|
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{data['model'].split(':')[0].replace('-', ' ').title()} ({data['model'].split(':')[1]})\n<small>{formated_date}</small>"
|
text = f"\n\n{self.convert_model_name(data['model'], 0)}\n<small>{formated_date}</small>"
|
||||||
GLib.idle_add(self.bot_message.insert_markup, self.bot_message.get_end_iter(), text, len(text.encode('utf-8')))
|
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)
|
||||||
@@ -945,15 +962,9 @@ Generate a title following these rules:
|
|||||||
first_paragraph = self.bot_message.get_text(self.bot_message.get_start_iter(), self.bot_message.get_end_iter(), False).split("\n")[0]
|
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"))
|
GLib.idle_add(self.show_notification, self.chats["selected_chat"], first_paragraph[:100] + (first_paragraph[100:] and '...'), Gio.ThemedIcon.new("chat-message-new-symbolic"))
|
||||||
else:
|
else:
|
||||||
if id not in self.chats["chats"][self.chats["selected_chat"]]["messages"]:
|
if not self.chats["chats"][self.chats["selected_chat"]]["messages"][id]["content"] and self.loading_spinner:
|
||||||
GLib.idle_add(self.chat_container.remove, self.loading_spinner)
|
GLib.idle_add(self.chat_container.remove, self.loading_spinner)
|
||||||
self.loading_spinner = None
|
self.loading_spinner = None
|
||||||
self.chats["chats"][self.chats["selected_chat"]]["messages"][id] = {
|
|
||||||
"role": "assistant",
|
|
||||||
"model": data['model'],
|
|
||||||
"date": datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
|
|
||||||
"content": ''
|
|
||||||
}
|
|
||||||
GLib.idle_add(self.bot_message.insert, self.bot_message.get_end_iter(), data['message']['content'])
|
GLib.idle_add(self.bot_message.insert, self.bot_message.get_end_iter(), data['message']['content'])
|
||||||
self.chats["chats"][self.chats["selected_chat"]]["messages"][id]['content'] += data['message']['content']
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id]['content'] += data['message']['content']
|
||||||
|
|
||||||
@@ -968,17 +979,69 @@ Generate a title following these rules:
|
|||||||
def run_message(self, messages, model, id):
|
def run_message(self, messages, model, id):
|
||||||
logger.debug("Running message")
|
logger.debug("Running message")
|
||||||
self.bot_message_button_container.set_visible(False)
|
self.bot_message_button_container.set_visible(False)
|
||||||
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id] = {
|
||||||
|
"role": "assistant",
|
||||||
|
"model": model,
|
||||||
|
"date": datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
|
||||||
|
"content": ''
|
||||||
|
}
|
||||||
|
if self.regenerate_button:
|
||||||
|
GLib.idle_add(self.chat_container.remove, self.regenerate_button)
|
||||||
|
try:
|
||||||
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))
|
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))
|
||||||
|
if response.status_code != 200: raise Exception('Network Error')
|
||||||
GLib.idle_add(self.add_code_blocks)
|
GLib.idle_add(self.add_code_blocks)
|
||||||
|
except Exception as e:
|
||||||
|
GLib.idle_add(self.connection_error)
|
||||||
|
self.regenerate_button = Gtk.Button(
|
||||||
|
child=Adw.ButtonContent(
|
||||||
|
icon_name='update-symbolic',
|
||||||
|
label=_('Regenerate Response')
|
||||||
|
),
|
||||||
|
css_classes=["suggested-action"],
|
||||||
|
halign=3
|
||||||
|
)
|
||||||
|
GLib.idle_add(self.chat_container.append, self.regenerate_button)
|
||||||
|
self.regenerate_button.connect('clicked', lambda button, id=id, bot_message_box=self.bot_message_box, bot_message_button_container=self.bot_message_button_container : self.regenerate_message(id, bot_message_box, bot_message_button_container))
|
||||||
|
finally:
|
||||||
GLib.idle_add(self.switch_send_stop_button)
|
GLib.idle_add(self.switch_send_stop_button)
|
||||||
GLib.idle_add(self.toggle_ui_sensitive, True)
|
GLib.idle_add(self.toggle_ui_sensitive, True)
|
||||||
if self.loading_spinner:
|
if self.loading_spinner:
|
||||||
GLib.idle_add(self.chat_container.remove, self.loading_spinner)
|
GLib.idle_add(self.chat_container.remove, self.loading_spinner)
|
||||||
self.loading_spinner = None
|
self.loading_spinner = None
|
||||||
if response.status_code != 200:
|
|
||||||
GLib.idle_add(self.connection_error)
|
def regenerate_message(self, id, bot_message_box, bot_message_button_container):
|
||||||
|
self.bot_message_button_container = bot_message_button_container
|
||||||
|
self.bot_message_view = Gtk.TextView(
|
||||||
|
editable=False,
|
||||||
|
focusable=True,
|
||||||
|
wrap_mode= Gtk.WrapMode.WORD,
|
||||||
|
margin_top=12,
|
||||||
|
margin_bottom=12,
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=["flat"]
|
||||||
|
)
|
||||||
|
self.bot_message = self.bot_message_view.get_buffer()
|
||||||
|
for widget in list(bot_message_box): bot_message_box.remove(widget)
|
||||||
|
bot_message_box.append(self.bot_message_view)
|
||||||
|
history = self.convert_history_to_ollama()[:list(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys()).index(id)]
|
||||||
|
if id in self.chats["chats"][self.chats["selected_chat"]]["messages"]:
|
||||||
|
del self.chats["chats"][self.chats["selected_chat"]]["messages"][id]
|
||||||
|
data = {
|
||||||
|
"model": self.convert_model_name(self.model_drop_down.get_selected_item().get_string(), 1),
|
||||||
|
"messages": history,
|
||||||
|
"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)
|
||||||
|
thread = threading.Thread(target=self.run_message, args=(data['messages'], data['model'], id))
|
||||||
|
thread.start()
|
||||||
|
|
||||||
def pull_model_update(self, data, model_name):
|
def pull_model_update(self, data, model_name):
|
||||||
|
if 'error' in data:
|
||||||
|
self.pulling_models[model_name]['error'] = data['error']
|
||||||
|
return
|
||||||
if model_name in list(self.pulling_models.keys()):
|
if model_name in list(self.pulling_models.keys()):
|
||||||
if 'completed' in data and 'total' in data:
|
if 'completed' in data and 'total' in data:
|
||||||
GLib.idle_add(self.pulling_models[model_name]['row'].set_subtitle, '<tt>{}%</tt>'.format(round(data['completed'] / data['total'] * 100, 2)))
|
GLib.idle_add(self.pulling_models[model_name]['row'].set_subtitle, '<tt>{}%</tt>'.format(round(data['completed'] / data['total'] * 100, 2)))
|
||||||
@@ -999,28 +1062,31 @@ Generate a title following these rules:
|
|||||||
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))
|
response = connection_handler.stream_post(f"{connection_handler.url}/api/pull", data=json.dumps(data), callback=lambda data, model_name=model: self.pull_model_update(data, model_name))
|
||||||
GLib.idle_add(self.update_list_local_models)
|
GLib.idle_add(self.update_list_local_models)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200 and 'error' not in self.pulling_models[model]:
|
||||||
GLib.idle_add(self.show_notification, _("Task Complete"), _("Model '{}' pulled successfully.").format(model), Gio.ThemedIcon.new("emblem-ok-symbolic"))
|
GLib.idle_add(self.show_notification, _("Task Complete"), _("Model '{}' pulled successfully.").format(model), Gio.ThemedIcon.new("emblem-ok-symbolic"))
|
||||||
GLib.idle_add(self.show_toast, _("Model '{}' pulled successfully.").format(model), self.manage_models_overlay)
|
GLib.idle_add(self.show_toast, _("Model '{}' pulled successfully.").format(model), self.manage_models_overlay)
|
||||||
GLib.idle_add(self.pulling_models[model]['overlay'].get_parent().get_parent().remove, self.pulling_models[model]['overlay'].get_parent())
|
elif response.status_code == 200 and self.pulling_models[model]['error']:
|
||||||
del self.pulling_models[model]
|
GLib.idle_add(self.show_notification, _("Pull Model Error"), _("Failed to pull model '{}': {}").format(model, self.pulling_models[model]['error']), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
||||||
|
GLib.idle_add(self.show_toast, _("Error pulling '{}': {}").format(model, self.pulling_models[model]['error']), self.manage_models_overlay)
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(self.show_notification, _("Pull Model Error"), _("Failed to pull model '{}' due to network error.").format(model), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
GLib.idle_add(self.show_notification, _("Pull Model Error"), _("Failed to pull model '{}' due to network error.").format(model), Gio.ThemedIcon.new("dialog-error-symbolic"))
|
||||||
GLib.idle_add(self.pulling_models[model]['overlay'].get_parent().get_parent().remove, self.pulling_models[model]['overlay'].get_parent())
|
GLib.idle_add(self.show_toast, _("Error pulling '{}'").format(model), self.manage_models_overlay)
|
||||||
del self.pulling_models[model]
|
|
||||||
GLib.idle_add(self.manage_models_dialog.close)
|
GLib.idle_add(self.manage_models_dialog.close)
|
||||||
GLib.idle_add(self.connection_error)
|
GLib.idle_add(self.connection_error)
|
||||||
|
|
||||||
|
GLib.idle_add(self.pulling_models[model]['overlay'].get_parent().get_parent().remove, self.pulling_models[model]['overlay'].get_parent())
|
||||||
|
del self.pulling_models[model]
|
||||||
if len(list(self.pulling_models.keys())) == 0:
|
if len(list(self.pulling_models.keys())) == 0:
|
||||||
GLib.idle_add(self.pulling_model_list_box.set_visible, False)
|
GLib.idle_add(self.pulling_model_list_box.set_visible, False)
|
||||||
|
|
||||||
def pull_model(self, model):
|
def pull_model(self, model):
|
||||||
|
if model in list(self.pulling_models.keys()) or model in self.local_models or ":" not in model: return
|
||||||
logger.info("Pulling model")
|
logger.info("Pulling model")
|
||||||
if model in list(self.pulling_models.keys()) or model in self.local_models:
|
|
||||||
return
|
|
||||||
self.pulling_model_list_box.set_visible(True)
|
self.pulling_model_list_box.set_visible(True)
|
||||||
#self.pulling_model_list_box.connect('row_selected', lambda list_box, row: dialogs.stop_pull_model(self, row.get_name()) if row else None) #It isn't working for some reason
|
#self.pulling_model_list_box.connect('row_selected', lambda list_box, row: dialogs.stop_pull_model(self, row.get_name()) if row else None) #It isn't working for some reason
|
||||||
|
model_name = self.convert_model_name(model, 0)
|
||||||
model_row = Adw.ActionRow(
|
model_row = Adw.ActionRow(
|
||||||
title = "<b>{}</b> <small>{}</small>".format(model.split(":")[0].replace("-", " ").title(), model.split(":")[1]),
|
title = "<b>{}</b> <small>{}</small>".format(model_name.split(" (")[0], model_name.split(" (")[1][:-1]),
|
||||||
name = model
|
name = model
|
||||||
)
|
)
|
||||||
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": model, "modelfile": None})
|
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": model, "modelfile": None})
|
||||||
@@ -1037,7 +1103,7 @@ Generate a title following these rules:
|
|||||||
vexpand = False,
|
vexpand = False,
|
||||||
valign = 3,
|
valign = 3,
|
||||||
css_classes = ["error", "circular"],
|
css_classes = ["error", "circular"],
|
||||||
tooltip_text = _("Stop Pulling '{} ({})'").format(model.split(':')[0].replace('-', ' ').title(), model.split(':')[1])
|
tooltip_text = _("Stop Pulling '{}'").format(model_name)
|
||||||
)
|
)
|
||||||
button.connect("clicked", lambda button, model_name=model : dialogs.stop_pull_model(self, model_name))
|
button.connect("clicked", lambda button, model_name=model : dialogs.stop_pull_model(self, model_name))
|
||||||
model_row.add_suffix(button)
|
model_row.add_suffix(button)
|
||||||
@@ -1056,7 +1122,7 @@ Generate a title following these rules:
|
|||||||
def list_available_model_tags(self, model_name):
|
def list_available_model_tags(self, model_name):
|
||||||
logger.debug("Listing available model tags")
|
logger.debug("Listing available model tags")
|
||||||
self.navigation_view_manage_models.push_by_tag('model_tags_page')
|
self.navigation_view_manage_models.push_by_tag('model_tags_page')
|
||||||
self.navigation_view_manage_models.find_page('model_tags_page').set_title(model_name.capitalize())
|
self.navigation_view_manage_models.find_page('model_tags_page').set_title(model_name.replace("-", " ").title())
|
||||||
self.model_link_button.set_name(self.available_models[model_name]['url'])
|
self.model_link_button.set_name(self.available_models[model_name]['url'])
|
||||||
self.model_link_button.set_tooltip_text(self.available_models[model_name]['url'])
|
self.model_link_button.set_tooltip_text(self.available_models[model_name]['url'])
|
||||||
self.available_model_list_box.unselect_all()
|
self.available_model_list_box.unselect_all()
|
||||||
@@ -1105,7 +1171,7 @@ Generate a title following these rules:
|
|||||||
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{message['model'].split(':')[0].replace('-', ' ').title()} ({message['model'].split(':')[1]})\n<small>{formated_date}</small>", id=key)
|
self.show_message(message['content'], True, f"\n\n{self.convert_model_name(message['model'], 0)}\n<small>{formated_date}</small>", id=key)
|
||||||
self.add_code_blocks()
|
self.add_code_blocks()
|
||||||
self.bot_message = None
|
self.bot_message = None
|
||||||
|
|
||||||
@@ -1121,6 +1187,13 @@ Generate a title following these rules:
|
|||||||
self.chats["order"] = []
|
self.chats["order"] = []
|
||||||
for chat_name in self.chats["chats"].keys():
|
for chat_name in self.chats["chats"].keys():
|
||||||
self.chats["order"].append(chat_name)
|
self.chats["order"].append(chat_name)
|
||||||
|
if len(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys()) > 0:
|
||||||
|
last_model_used = self.chats["chats"][self.chats["selected_chat"]]["messages"][list(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys())[-1]]["model"]
|
||||||
|
last_model_used = self.convert_model_name(last_model_used, 0)
|
||||||
|
for i in range(self.model_string_list.get_n_items()):
|
||||||
|
if self.model_string_list.get_string(i) == last_model_used:
|
||||||
|
self.model_drop_down.set_selected(i)
|
||||||
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
self.chats = {"chats": {}, "selected_chat": None, "order": []}
|
self.chats = {"chats": {}, "selected_chat": None, "order": []}
|
||||||
@@ -1541,6 +1614,7 @@ Generate a title following these rules:
|
|||||||
self.get_application().create_action('import_chat', lambda *_: self.import_chat(), ['<primary>i'])
|
self.get_application().create_action('import_chat', lambda *_: self.import_chat(), ['<primary>i'])
|
||||||
self.get_application().create_action('create_model_from_existing', lambda *_: dialogs.create_model_from_existing(self))
|
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))
|
self.get_application().create_action('create_model_from_file', lambda *_: dialogs.create_model_from_file(self))
|
||||||
|
self.get_application().create_action('create_model_from_name', lambda *_: dialogs.create_model_from_name(self))
|
||||||
self.get_application().create_action('delete_chat', self.chat_actions)
|
self.get_application().create_action('delete_chat', self.chat_actions)
|
||||||
self.get_application().create_action('rename_chat', self.chat_actions)
|
self.get_application().create_action('rename_chat', self.chat_actions)
|
||||||
self.get_application().create_action('rename_current_chat', self.current_chat_actions)
|
self.get_application().create_action('rename_current_chat', self.current_chat_actions)
|
||||||
@@ -1580,7 +1654,7 @@ Generate a title following these rules:
|
|||||||
|
|
||||||
#Support dialog
|
#Support dialog
|
||||||
if 'show_support' not in data or data['show_support']:
|
if 'show_support' not in data or data['show_support']:
|
||||||
if random.randint(0, 99) == 0:
|
if random.randint(0, 49) == 0 or True:
|
||||||
dialogs.support(self)
|
dialogs.support(self)
|
||||||
if 'show_support' in data: self.show_support = data['show_support']
|
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)
|
||||||
|
|||||||
275
src/window.ui
275
src/window.ui
@@ -14,7 +14,6 @@
|
|||||||
<object class="AdwBreakpoint">
|
<object class="AdwBreakpoint">
|
||||||
<condition>max-width: 800sp</condition>
|
<condition>max-width: 800sp</condition>
|
||||||
<setter object="split_view_overlay" property="collapsed">true</setter>
|
<setter object="split_view_overlay" property="collapsed">true</setter>
|
||||||
<setter object="show_sidebar_button" property="visible">true</setter>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@@ -23,10 +22,8 @@
|
|||||||
<object class="AdwBreakpoint">
|
<object class="AdwBreakpoint">
|
||||||
<condition>max-width: 500sp</condition>
|
<condition>max-width: 500sp</condition>
|
||||||
<setter object="split_view_overlay" property="collapsed">true</setter>
|
<setter object="split_view_overlay" property="collapsed">true</setter>
|
||||||
<setter object="show_sidebar_button" property="visible">true</setter>
|
|
||||||
<setter object="welcome_dialog" property="width-request">360</setter>
|
<setter object="welcome_dialog" property="width-request">360</setter>
|
||||||
<setter object="manage_models_dialog" property="width-request">360</setter>
|
<setter object="manage_models_dialog" property="width-request">360</setter>
|
||||||
<setter object="create_model_dialog" property="width-request">360</setter>
|
|
||||||
<setter object="preferences_dialog" property="width-request">360</setter>
|
<setter object="preferences_dialog" property="width-request">360</setter>
|
||||||
<setter object="file_preview_dialog" property="width-request">360</setter>
|
<setter object="file_preview_dialog" property="width-request">360</setter>
|
||||||
</object>
|
</object>
|
||||||
@@ -80,7 +77,6 @@
|
|||||||
<object class="AdwHeaderBar" id="header_bar">
|
<object class="AdwHeaderBar" id="header_bar">
|
||||||
<child type="start">
|
<child type="start">
|
||||||
<object class="GtkToggleButton" id="show_sidebar_button">
|
<object class="GtkToggleButton" id="show_sidebar_button">
|
||||||
<property name="visible">false</property>
|
|
||||||
<property name="icon-name">sidebar-show-symbolic</property>
|
<property name="icon-name">sidebar-show-symbolic</property>
|
||||||
<property name="tooltip-text" translatable="yes">Toggle Sidebar</property>
|
<property name="tooltip-text" translatable="yes">Toggle Sidebar</property>
|
||||||
<property name="active" bind-source="split_view_overlay" bind-property="show-sidebar" bind-flags="sync-create"/>
|
<property name="active" bind-source="split_view_overlay" bind-property="show-sidebar" bind-flags="sync-create"/>
|
||||||
@@ -93,7 +89,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkDropDown" id="model_drop_down">
|
<object class="GtkDropDown" id="model_drop_down">
|
||||||
<signal name="notify" handler="verify_if_image_can_be_used"/>
|
<signal name="notify" handler="verify_if_image_can_be_used"/>
|
||||||
<property name="width-request">150</property>
|
<property name="width-request">175</property>
|
||||||
<property name="enable-search">true</property>
|
<property name="enable-search">true</property>
|
||||||
<property name="tooltip-text">Select Model</property>
|
<property name="tooltip-text">Select Model</property>
|
||||||
<property name="model">
|
<property name="model">
|
||||||
@@ -217,6 +213,8 @@
|
|||||||
<object class="GtkScrolledWindow">
|
<object class="GtkScrolledWindow">
|
||||||
<property name="max-content-height">150</property>
|
<property name="max-content-height">150</property>
|
||||||
<property name="propagate-natural-height">true</property>
|
<property name="propagate-natural-height">true</property>
|
||||||
|
<property name="margin-start">10</property>
|
||||||
|
<property name="margin-end">10</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="message_input_scroll_window"/>
|
<class name="message_input_scroll_window"/>
|
||||||
</style>
|
</style>
|
||||||
@@ -226,8 +224,6 @@
|
|||||||
<class name="message_text_view"/>
|
<class name="message_text_view"/>
|
||||||
</style>
|
</style>
|
||||||
<property name="wrap-mode">word</property>
|
<property name="wrap-mode">word</property>
|
||||||
<property name="margin-start">10</property>
|
|
||||||
<property name="margin-end">10</property>
|
|
||||||
<property name="top-margin">10</property>
|
<property name="top-margin">10</property>
|
||||||
<property name="bottom-margin">10</property>
|
<property name="bottom-margin">10</property>
|
||||||
<property name="hexpand">true</property>
|
<property name="hexpand">true</property>
|
||||||
@@ -246,6 +242,7 @@
|
|||||||
<style>
|
<style>
|
||||||
<class name="accent"/>
|
<class name="accent"/>
|
||||||
<class name="circular"/>
|
<class name="circular"/>
|
||||||
|
<class name="suggested-action"/>
|
||||||
</style>
|
</style>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwButtonContent">
|
<object class="AdwButtonContent">
|
||||||
@@ -456,127 +453,6 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
<object class="AdwDialog" id="create_model_dialog">
|
|
||||||
<property name="can-close">true</property>
|
|
||||||
<property name="width-request">400</property>
|
|
||||||
<property name="height-request">600</property>
|
|
||||||
<child>
|
|
||||||
<object class="AdwToastOverlay" id="create_model_overlay">
|
|
||||||
<child>
|
|
||||||
<object class="AdwToolbarView">
|
|
||||||
<child type="bottom">
|
|
||||||
<object class="GtkActionBar">
|
|
||||||
<property name="revealed">true</property>
|
|
||||||
<child type="end">
|
|
||||||
<object class="GtkButton">
|
|
||||||
<property name="label" translatable="yes">Create</property>
|
|
||||||
<signal name="clicked" handler="create_model_start"/>
|
|
||||||
<style>
|
|
||||||
<class name="suggested-action"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="top">
|
|
||||||
<object class="AdwHeaderBar">
|
|
||||||
<property name="title-widget">
|
|
||||||
<object class="AdwWindowTitle">
|
|
||||||
<property name="title" translatable="yes">Create Model</property>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow">
|
|
||||||
<property name="vexpand">true</property>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="margin-start">12</property>
|
|
||||||
<property name="margin-end">12</property>
|
|
||||||
<property name="margin-top">12</property>
|
|
||||||
<property name="margin-bottom">12</property>
|
|
||||||
<property name="orientation">1</property>
|
|
||||||
<property name="spacing">12</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkListBox">
|
|
||||||
<style>
|
|
||||||
<class name="boxed-list"/>
|
|
||||||
<class name="card"/>
|
|
||||||
</style>
|
|
||||||
<property name="selection-mode">none</property>
|
|
||||||
<child>
|
|
||||||
<object class="AdwActionRow" id="create_model_base">
|
|
||||||
<property name="title" translatable="yes">Base</property>
|
|
||||||
<property name="subtitle"/>
|
|
||||||
<style>
|
|
||||||
<class name="property"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkListBox">
|
|
||||||
<style>
|
|
||||||
<class name="boxed-list"/>
|
|
||||||
<class name="card"/>
|
|
||||||
</style>
|
|
||||||
<property name="selection-mode">none</property>
|
|
||||||
<child>
|
|
||||||
<object class="AdwEntryRow" id="create_model_name">
|
|
||||||
<property name="title" translatable="yes">Name</property>
|
|
||||||
<property name="input-purpose">alpha</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="AdwEntryRow" id="create_model_system">
|
|
||||||
<property name="title" translatable="yes">Context</property>
|
|
||||||
<property name="input-purpose">alpha</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkListBox">
|
|
||||||
<style>
|
|
||||||
<class name="boxed-list"/>
|
|
||||||
<class name="card"/>
|
|
||||||
</style>
|
|
||||||
<property name="selection-mode">none</property>
|
|
||||||
<child>
|
|
||||||
<object class="AdwEntryRow" id="create_model_template">
|
|
||||||
<property name="title" translatable="yes">Template</property>
|
|
||||||
<property name="input-purpose">alpha</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="label" translatable="yes">Some models require a specific template. Please visit the model's website for more information if you're unsure.</property>
|
|
||||||
<property name="margin-start">12</property>
|
|
||||||
<property name="margin-end">12</property>
|
|
||||||
<property name="margin-top">12</property>
|
|
||||||
<property name="margin-bottom">12</property>
|
|
||||||
<property name="halign">1</property>
|
|
||||||
<property name="wrap">true</property>
|
|
||||||
<style>
|
|
||||||
<class name="caption"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
|
|
||||||
<object class="AdwDialog" id="manage_models_dialog">
|
<object class="AdwDialog" id="manage_models_dialog">
|
||||||
<property name="can-close">true</property>
|
<property name="can-close">true</property>
|
||||||
<property name="width-request">400</property>
|
<property name="width-request">400</property>
|
||||||
@@ -730,6 +606,143 @@
|
|||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwNavigationPage">
|
||||||
|
<property name="title" translatable="yes">Create Model</property>
|
||||||
|
<property name="tag">model_create_page</property>
|
||||||
|
<property name="child">
|
||||||
|
<object class="AdwToolbarView">
|
||||||
|
<child type="top">
|
||||||
|
<object class="AdwHeaderBar">
|
||||||
|
<child type="start">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<signal name="clicked" handler="link_button_handler"/>
|
||||||
|
<property name="icon-name">globe-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<property name="content">
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="margin-start">12</property>
|
||||||
|
<property name="margin-end">12</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="margin-bottom">12</property>
|
||||||
|
<property name="orientation">1</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox">
|
||||||
|
<style>
|
||||||
|
<class name="boxed-list"/>
|
||||||
|
<class name="card"/>
|
||||||
|
</style>
|
||||||
|
<property name="selection-mode">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwActionRow" id="create_model_base">
|
||||||
|
<property name="title" translatable="yes">Base</property>
|
||||||
|
<property name="sensitive">false</property>
|
||||||
|
<property name="subtitle"/>
|
||||||
|
<style>
|
||||||
|
<class name="property"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox">
|
||||||
|
<style>
|
||||||
|
<class name="boxed-list"/>
|
||||||
|
<class name="card"/>
|
||||||
|
</style>
|
||||||
|
<property name="selection-mode">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="create_model_name">
|
||||||
|
<property name="title" translatable="yes">Name</property>
|
||||||
|
<property name="input-purpose">alpha</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="create_model_system">
|
||||||
|
<property name="title" translatable="yes">Context</property>
|
||||||
|
<property name="input-purpose">alpha</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox">
|
||||||
|
<style>
|
||||||
|
<class name="boxed-list"/>
|
||||||
|
<class name="card"/>
|
||||||
|
</style>
|
||||||
|
<property name="selection-mode">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="height-request">140</property>
|
||||||
|
<property name="margin-top">10</property>
|
||||||
|
<property name="margin-bottom">10</property>
|
||||||
|
<property name="margin-start">10</property>
|
||||||
|
<property name="margin-end">10</property>
|
||||||
|
<style>
|
||||||
|
<class name="card"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="margin-start">10</property>
|
||||||
|
<property name="margin-end">10</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="create_model_modelfile">
|
||||||
|
<style>
|
||||||
|
<class name="modelfile_textview"/>
|
||||||
|
</style>
|
||||||
|
<property name="wrap-mode">word</property>
|
||||||
|
<property name="top-margin">10</property>
|
||||||
|
<property name="bottom-margin">10</property>
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="label" translatable="yes">Some models require a modelfile, Alpaca fills FROM and SYSTEM (context) instructions automatically. Please visit the model's website or Ollama documentation for more information if you're unsure.</property>
|
||||||
|
<property name="margin-top">10</property>
|
||||||
|
<property name="margin-bottom">10</property>
|
||||||
|
<property name="margin-start">10</property>
|
||||||
|
<property name="margin-end">10</property>
|
||||||
|
<property name="halign">1</property>
|
||||||
|
<property name="wrap">true</property>
|
||||||
|
<style>
|
||||||
|
<class name="caption"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label" translatable="yes">Create</property>
|
||||||
|
<signal name="clicked" handler="create_model_start"/>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -1085,9 +1098,13 @@ By downloading any model you accept their license agreement available on the mod
|
|||||||
<attribute name="action">app.create_model_from_existing</attribute>
|
<attribute name="action">app.create_model_from_existing</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">From GGUF File (Experimental)</attribute>
|
<attribute name="label" translatable="yes">From GGUF File</attribute>
|
||||||
<attribute name="action">app.create_model_from_file</attribute>
|
<attribute name="action">app.create_model_from_file</attribute>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">From Name</attribute>
|
||||||
|
<attribute name="action">app.create_model_from_name</attribute>
|
||||||
|
</item>
|
||||||
</section>
|
</section>
|
||||||
</menu>
|
</menu>
|
||||||
<object class="GtkFileFilter" id="file_filter_attachments">
|
<object class="GtkFileFilter" id="file_filter_attachments">
|
||||||
|
|||||||
Reference in New Issue
Block a user