Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4d26b2bda | ||
|
|
d7961f2510 | ||
|
|
45f5214ec9 | ||
|
|
5e1e770aee | ||
|
|
5eb18d00d3 | ||
|
|
bcbfd44e1f | ||
|
|
073c619d89 | ||
|
|
71ab8cfba4 | ||
|
|
0ec812099c | ||
|
|
f7f05a0538 | ||
|
|
82a0ab0d9e | ||
|
|
60b24da482 | ||
|
|
66d209e4c6 | ||
|
|
0c9ab4e17e | ||
|
|
fff3a68b29 | ||
|
|
580a104894 | ||
|
|
3fe9b6e121 | ||
|
|
6979d1a775 | ||
|
|
90385ce461 | ||
|
|
c579e6ec99 | ||
|
|
b8d93cfd17 | ||
|
|
359f28f5c1 | ||
|
|
6e8bff8493 | ||
|
|
ad25d5a50f | ||
|
|
4b17601baf | ||
|
|
c994307954 | ||
|
|
a13ffd22f1 | ||
|
|
7b8f40e117 | ||
|
|
add4dad8c0 | ||
|
|
7faa805176 | ||
|
|
91651b918e | ||
|
|
725e0930fe | ||
|
|
93ba903fc8 | ||
|
|
56064831d3 | ||
|
|
4bdb813b64 | ||
|
|
ad69887e4a | ||
|
|
c8f148d1f8 | ||
|
|
365e39a20a | ||
|
|
48c2fd2e81 | ||
|
|
3a5dc568c5 | ||
|
|
a180f7a46c | ||
|
|
07a12ba41f | ||
|
|
dd5351a3de | ||
|
|
ae48a17301 |
@@ -15,6 +15,7 @@ Alpaca is an [Ollama](https://github.com/ollama/ollama) client where you can man
|
|||||||
- Talk to multiple models in the same conversation
|
- Talk to multiple models in the same conversation
|
||||||
- Pull and delete models from the app
|
- Pull and delete models from the app
|
||||||
- Image recognition
|
- Image recognition
|
||||||
|
- Document recognition (plain text files)
|
||||||
- Code highlighting
|
- Code highlighting
|
||||||
- Multiple conversations
|
- Multiple conversations
|
||||||
- Notifications
|
- Notifications
|
||||||
@@ -22,13 +23,12 @@ Alpaca is an [Ollama](https://github.com/ollama/ollama) client where you can man
|
|||||||
- Delete messages
|
- Delete messages
|
||||||
|
|
||||||
## Future features!
|
## Future features!
|
||||||
- Document recognition (Probably only Markdown because it's really easy for AI to read)
|
|
||||||
- YouTube recognition (Ask questions about a YouTube video using the transcript)
|
- YouTube recognition (Ask questions about a YouTube video using the transcript)
|
||||||
- Edit messages
|
- Edit messages
|
||||||
- Snap Package (maybe)
|
- Snap Package (maybe)
|
||||||
|
|
||||||
## Screenies
|
## Screenies
|
||||||
Login to Ollama instance | Chatting with models | Managing models
|
Code highlighting | Chatting with models | Managing models
|
||||||
:-------------------------:|:-------------------------:|:-------------------------:
|
:-------------------------:|:-------------------------:|:-------------------------:
|
||||||
 |  | 
|
 |  | 
|
||||||
|
|
||||||
@@ -50,11 +50,14 @@ For now Alpaca doesn't offer a way to do this from the GUI but it's really simpl
|
|||||||
Go to `~/.var/app/com.jeffser.Alpaca/config/server.json` and change the `"local_port"` value, by default it is `11435`.
|
Go to `~/.var/app/com.jeffser.Alpaca/config/server.json` and change the `"local_port"` value, by default it is `11435`.
|
||||||
|
|
||||||
### Backup all the chats
|
### Backup all the chats
|
||||||
The chat data is located in `~/.var/app/com.jeffser.Alpaca/config/chats.json` you can copy that file wherever you want to.
|
The chat data is located in `~/.var/app/com.jeffser.Alpaca/data/chats` you can copy that directory wherever you want to.
|
||||||
|
|
||||||
### Force showing the welcome dialog
|
### Force showing the welcome dialog
|
||||||
To do that you just need to delete the file `~/.var/app/com.jeffser.Alpaca/config/server.json`, this won't affect your saved chats or models.
|
To do that you just need to delete the file `~/.var/app/com.jeffser.Alpaca/config/server.json`, this won't affect your saved chats or models.
|
||||||
|
|
||||||
|
### Add/Change environment variables for Ollama
|
||||||
|
You can change anything except `$HOME` and `$OLLAMA_HOST`, to do this go to `~/.var/app/com.jeffser.Alpaca/config/server.json` and change `ollama_overrides` accordingly, some overrides are available to change on the GUI.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|||||||
@@ -80,16 +80,16 @@
|
|||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://github.com/ollama/ollama/releases/download/v0.1.38/ollama-linux-amd64",
|
"url": "https://github.com/ollama/ollama/releases/download/v0.1.39/ollama-linux-amd64",
|
||||||
"sha256": "c3360812503a9756a507ebb9d78667e2b21800a760b45046bc48a8f3b81972f4",
|
"sha256": "4d19be355842a6297c93ab5ada139fe396126736bf3c3882a879dc245dffe3af",
|
||||||
"only-arches": [
|
"only-arches": [
|
||||||
"x86_64"
|
"x86_64"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://github.com/ollama/ollama/releases/download/v0.1.38/ollama-linux-arm64",
|
"url": "https://github.com/ollama/ollama/releases/download/v0.1.39/ollama-linux-arm64",
|
||||||
"sha256": "f2d091afe665b2d5ba8b68e2473d36cdfaf80c61c7d2844a0a8f533c4e62f547",
|
"sha256": "60ec2d36928c11d6c6d84fe91451308a46aafaedbdba44664e5a6080009ce097",
|
||||||
"only-arches": [
|
"only-arches": [
|
||||||
"aarch64"
|
"aarch64"
|
||||||
]
|
]
|
||||||
|
|||||||
6
data/com.jeffser.Alpaca.SearchProvider.ini.in
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[Shell Search Provider]
|
||||||
|
DesktopId=@appid@.desktop
|
||||||
|
BusName=@appid@.SearchProvider
|
||||||
|
ObjectPath=/com/jeffser/Alpaca/SearchProvider
|
||||||
|
Version=2
|
||||||
|
|
||||||
3
data/com.jeffser.Alpaca.SearchProvider.service.in
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[D-BUS Service]
|
||||||
|
Name=@appid@.SearchProvider
|
||||||
|
Exec=@libexecdir@/alpaca_search_provider
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<li>Pull and delete models from the app</li>
|
<li>Pull and delete models from the app</li>
|
||||||
<li>Have multiple conversations</li>
|
<li>Have multiple conversations</li>
|
||||||
<li>Image recognition (Only available with compatible models)</li>
|
<li>Image recognition (Only available with compatible models)</li>
|
||||||
|
<li>Plain text documents recognition</li>
|
||||||
<li>Import and export chats</li>
|
<li>Import and export chats</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Disclaimer</p>
|
<p>Disclaimer</p>
|
||||||
@@ -63,6 +64,52 @@
|
|||||||
<url type="homepage">https://github.com/Jeffser/Alpaca</url>
|
<url type="homepage">https://github.com/Jeffser/Alpaca</url>
|
||||||
<url type="donation">https://github.com/sponsors/Jeffser</url>
|
<url type="donation">https://github.com/sponsors/Jeffser</url>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="0.9.4" date="2024-06-04">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/0.9.4</url>
|
||||||
|
<description>
|
||||||
|
<p>Huge Update</p>
|
||||||
|
<ul>
|
||||||
|
<li>Added: Support for plain text files</li>
|
||||||
|
<li>Added: New backend system for storing messages</li>
|
||||||
|
<li>Added: Support for changing Ollama's overrides</li>
|
||||||
|
<li>General Optimization</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<release version="0.9.3" date="2024-06-01">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/0.9.3</url>
|
||||||
|
<description>
|
||||||
|
<p>Big Update</p>
|
||||||
|
<ul>
|
||||||
|
<li>Added: Support for GGUF models (experimental)</li>
|
||||||
|
<li>Added: Support for customization and creation of models</li>
|
||||||
|
<li>Fixed: Icons don't appear on non Gnome systems</li>
|
||||||
|
<li>Update Ollama to v0.1.39</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<release version="0.9.2" date="2024-05-30">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/0.9.2</url>
|
||||||
|
<description>
|
||||||
|
<p>Fix</p>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed: app didn't open if models tweaks wasn't present in the config files</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<release version="0.9.1" date="2024-05-29">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/0.9.1</url>
|
||||||
|
<description>
|
||||||
|
<p>Big Update</p>
|
||||||
|
<ul>
|
||||||
|
<li>Changed multiple icons (paper airplane for the send button)</li>
|
||||||
|
<li>Combined export / import chat buttons into a menu</li>
|
||||||
|
<li>Added 'model tweaks' (temperature, seed, keep_alive)</li>
|
||||||
|
<li>Fixed send / stop button</li>
|
||||||
|
<li>Fixed app not checking if remote connection works when starting</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="0.9.0" date="2024-05-29">
|
<release version="0.9.0" date="2024-05-29">
|
||||||
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/0.9.0</url>
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/0.9.0</url>
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -33,4 +33,24 @@ test('Validate schema file',
|
|||||||
compile_schemas,
|
compile_schemas,
|
||||||
args: ['--strict', '--dry-run', meson.current_source_dir()])
|
args: ['--strict', '--dry-run', meson.current_source_dir()])
|
||||||
|
|
||||||
|
#service_conf = configuration_data()
|
||||||
|
#service_conf.set('appid', application_id)
|
||||||
|
#service_conf.set('libexecdir', join_paths(get_option('prefix'), get_option('bindir')))
|
||||||
|
#configure_file(
|
||||||
|
#input: 'com.jeffser.Alpaca.SearchProvider.service.in',
|
||||||
|
#output: '@0@.SearchProvider.service'.format(application_id),
|
||||||
|
#configuration: service_conf,
|
||||||
|
#install_dir: join_paths(join_paths(get_option('prefix'), get_option('datadir')), 'dbus-1', 'services')
|
||||||
|
#)
|
||||||
|
|
||||||
|
#search_provider_conf = configuration_data()
|
||||||
|
#search_provider_conf.set('appid', application_id)
|
||||||
|
#configure_file(
|
||||||
|
#configuration: search_provider_conf,
|
||||||
|
#input: files('com.jeffser.Alpaca.SearchProvider.ini.in'),
|
||||||
|
#install_dir: join_paths(get_option('datadir'), 'gnome-shell', 'search-providers'),
|
||||||
|
#output: '@0@.SearchProvider.ini'.format(application_id)
|
||||||
|
#)
|
||||||
|
|
||||||
|
|
||||||
subdir('icons')
|
subdir('icons')
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
project('Alpaca',
|
project('Alpaca', 'c',
|
||||||
version: '0.9.0',
|
version: '0.9.4',
|
||||||
meson_version: '>= 0.62.0',
|
meson_version: '>= 0.62.0',
|
||||||
default_options: [ 'warning_level=2', 'werror=false', ],
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
)
|
)
|
||||||
|
|
||||||
i18n = import('i18n')
|
i18n = import('i18n')
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
|
application_id = 'com.jeffser.Alpaca'
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
|||||||
597
po/alpaca.pot
633
po/pt_BR.po
655
po/pt_BR.po~
@@ -3,6 +3,27 @@
|
|||||||
<gresource prefix="/com/jeffser/Alpaca">
|
<gresource prefix="/com/jeffser/Alpaca">
|
||||||
<file>style.css</file>
|
<file>style.css</file>
|
||||||
<file>style-dark.css</file>
|
<file>style-dark.css</file>
|
||||||
|
<file alias="icons/scalable/status/library-symbolic.svg">icons/library-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/paper-plane-symbolic.svg">icons/paper-plane-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/globe-symbolic.svg">icons/globe-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/chat-message-new-symbolic.svg">icons/chat-message-new-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/dialog-warning-symbolic.svg">icons/dialog-warning-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/document-edit-symbolic.svg">icons/document-edit-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/edit-copy-symbolic.svg">icons/edit-copy-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/folder-download-symbolic.svg">icons/folder-download-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/image-x-generic-symbolic.svg">icons/image-x-generic-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/media-playback-stop-symbolic.svg">icons/media-playback-stop-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/open-menu-symbolicsvg">icons/open-menu-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/preferences-other-symbolic.svg">icons/preferences-other-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/preferences-system-symbolic.svg">icons/preferences-system-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/sidebar-show-symbolic.svg">icons/sidebar-show-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/user-trash-symbolic.svg">icons/user-trash-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/view-more-symbolic.svg">icons/view-more-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/document-open-symbolic.svg">icons/document-open-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/list-add-symbolic.svg">icons/list-add-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/brain-augemnted-symbolic.svg">icons/brain-augemnted-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/chain-link-loose-symbolic.svg">icons/chain-link-loose-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/document-text-symbolic.svg">icons/document-text-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>
|
||||||
|
|||||||
127
src/alpaca_search_provider.in
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#!@PYTHON@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from gi.repository import Gio, GLib
|
||||||
|
|
||||||
|
DBUS_NAME = "com.jeffser.Alpaca.SearchProvider"
|
||||||
|
DBUS_OBJECT_PATH = "/com/jeffser/Alpaca/SearchProvider"
|
||||||
|
DBUS_INTERFACE = "org.gnome.Shell.SearchProvider2"
|
||||||
|
|
||||||
|
class SearchProvider:
|
||||||
|
def __init__(self):
|
||||||
|
print("ALPACA __init__")
|
||||||
|
self.connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||||
|
if not self.connection:
|
||||||
|
print("ALPACA Failed to get D-Bus connection")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("ALPACA D-Bus connection obtained")
|
||||||
|
interface_info = self.get_interface_info()
|
||||||
|
|
||||||
|
self.registration_id = self.connection.register_object(
|
||||||
|
DBUS_OBJECT_PATH,
|
||||||
|
interface_info,
|
||||||
|
self.handle_method_call,
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.registration_id > 0:
|
||||||
|
print(f"ALPACA Object registered with ID: {self.registration_id}")
|
||||||
|
else:
|
||||||
|
print("ALPACA Failed to register object")
|
||||||
|
|
||||||
|
def get_interface_info(self):
|
||||||
|
print("ALPACA get_interface_info")
|
||||||
|
xml = """
|
||||||
|
<node>
|
||||||
|
<interface name="org.gnome.Shell.SearchProvider2">
|
||||||
|
|
||||||
|
<method name="GetInitialResultSet">
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="as" name="results" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="GetSubsearchResultSet">
|
||||||
|
<arg type="as" name="previous_results" direction="in" />
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="as" name="results" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="GetResultMetas">
|
||||||
|
<arg type="as" name="identifiers" direction="in" />
|
||||||
|
<arg type="aa{sv}" name="metas" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="ActivateResult">
|
||||||
|
<arg type="s" name="identifier" direction="in" />
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="u" name="timestamp" direction="in" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="LaunchSearch">
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="u" name="timestamp" direction="in" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
|
"""
|
||||||
|
return Gio.DBusNodeInfo.new_for_xml(xml).interfaces[0]
|
||||||
|
|
||||||
|
def handle_method_call(self, connection, sender, object_path, interface_name, method_name, parameters, invocation):
|
||||||
|
print(f"ALPACA handle_method_call: {method_name}")
|
||||||
|
if method_name == "GetInitialResultSet":
|
||||||
|
self.handle_get_initial_result_set(invocation, parameters)
|
||||||
|
elif method_name == "GetSubsearchResultSet":
|
||||||
|
self.handle_get_subsearch_result_set(invocation, parameters)
|
||||||
|
elif method_name == "GetResultMetas":
|
||||||
|
self.handle_get_result_metas(invocation, parameters)
|
||||||
|
elif method_name == "ActivateResult":
|
||||||
|
self.handle_activate_result(invocation, parameters)
|
||||||
|
|
||||||
|
def handle_get_initial_result_set(self, invocation, parameters):
|
||||||
|
print("ALPACA handle_get_initial_result_set")
|
||||||
|
terms = parameters.unpack()[0]
|
||||||
|
print(f"Initial search terms: {terms}")
|
||||||
|
results = ["result1", "result2"]
|
||||||
|
if "Alpaca" in terms:
|
||||||
|
results.append("alpaca_placeholder_result")
|
||||||
|
print(f"Returning results: {results}")
|
||||||
|
invocation.return_value(GLib.Variant("(as)", [results]))
|
||||||
|
|
||||||
|
def handle_get_subsearch_result_set(self, invocation, parameters):
|
||||||
|
print("ALPACA handle_get_subsearch_result_set")
|
||||||
|
previous_results, terms = parameters.unpack()
|
||||||
|
print(f"Subsearch terms: {terms}, previous results: {previous_results}")
|
||||||
|
results = ["result3", "result4"]
|
||||||
|
if "Alpaca" in terms:
|
||||||
|
results.append("sub_alpaca_placeholder_result")
|
||||||
|
print(f"Returning subsearch results: {results}")
|
||||||
|
invocation.return_value(GLib.Variant("(as)", [results]))
|
||||||
|
|
||||||
|
def handle_get_result_metas(self, invocation, parameters):
|
||||||
|
print("ALPACA handle_get_result_metas")
|
||||||
|
identifiers = parameters.unpack()[0]
|
||||||
|
print(f"Result metas for identifiers: {identifiers}")
|
||||||
|
metas = []
|
||||||
|
for identifier in identifiers:
|
||||||
|
meta = {"name": GLib.Variant("s", "Placeholder result for " + identifier)}
|
||||||
|
metas.append(GLib.Variant("a{sv}", meta))
|
||||||
|
print(f"Returning metas: {metas}")
|
||||||
|
invocation.return_value(GLib.Variant("(a{sv})", [metas]))
|
||||||
|
|
||||||
|
def handle_activate_result(self, invocation, parameters):
|
||||||
|
print("ALPACA handle_activate_result")
|
||||||
|
identifier, terms, timestamp = parameters.unpack()
|
||||||
|
print(f"Activated result: {identifier}, terms: {terms}, timestamp: {timestamp}")
|
||||||
|
invocation.return_value(None)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
provider = SearchProvider()
|
||||||
|
if provider.registration_id > 0:
|
||||||
|
loop = GLib.MainLoop()
|
||||||
|
print("ALPACA Running main loop")
|
||||||
|
loop.run()
|
||||||
|
else:
|
||||||
|
print("ALPACA Failed to start main loop due to object registration failure")
|
||||||
@@ -13,6 +13,19 @@ def simple_get(connection_url:str) -> dict:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "error", "status_code": 0}
|
return {"status": "error", "status_code": 0}
|
||||||
|
|
||||||
|
def simple_post(connection_url:str, data) -> dict:
|
||||||
|
try:
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
response = requests.post(connection_url, headers=headers, data=data, stream=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return {"status": "ok", "text": response.text, "status_code": response.status_code}
|
||||||
|
else:
|
||||||
|
return {"status": "error", "status_code": response.status_code}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "status_code": 0}
|
||||||
|
|
||||||
def simple_delete(connection_url:str, data) -> dict:
|
def simple_delete(connection_url:str, data) -> dict:
|
||||||
try:
|
try:
|
||||||
response = requests.delete(connection_url, json=data)
|
response = requests.delete(connection_url, json=data)
|
||||||
@@ -38,4 +51,3 @@ def stream_post(connection_url:str, data, callback:callable) -> dict:
|
|||||||
return {"status": "error", "status_code": response.status_code}
|
return {"status": "error", "status_code": response.status_code}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "error", "status_code": 0}
|
return {"status": "error", "status_code": 0}
|
||||||
|
|
||||||
|
|||||||
@@ -177,14 +177,14 @@ def pull_model(self, model_name):
|
|||||||
|
|
||||||
# REMOVE IMAGE | WORKS
|
# REMOVE IMAGE | WORKS
|
||||||
|
|
||||||
def remove_image_response(self, dialog, task):
|
def remove_attached_file_response(self, dialog, task, button):
|
||||||
if dialog.choose_finish(task) == 'remove':
|
if dialog.choose_finish(task) == 'remove':
|
||||||
self.remove_image()
|
self.remove_attached_file(button)
|
||||||
|
|
||||||
def remove_image(self):
|
def remove_attached_file(self, button):
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Remove Image"),
|
heading=_("Remove File"),
|
||||||
body=_("Are you sure you want to remove image?"),
|
body=_("Are you sure you want to remove file?"),
|
||||||
close_response="cancel"
|
close_response="cancel"
|
||||||
)
|
)
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
@@ -193,10 +193,10 @@ def remove_image(self):
|
|||||||
dialog.choose(
|
dialog.choose(
|
||||||
parent = self,
|
parent = self,
|
||||||
cancellable = None,
|
cancellable = None,
|
||||||
callback = lambda dialog, task: remove_image_response(self, dialog, task)
|
callback = lambda dialog, task, button=button: remove_attached_file_response(self, dialog, task, button)
|
||||||
)
|
)
|
||||||
|
|
||||||
# RECONNECT REMOTE |
|
# RECONNECT REMOTE | WORKS
|
||||||
|
|
||||||
def reconnect_remote_response(self, dialog, task, entry):
|
def reconnect_remote_response(self, dialog, task, entry):
|
||||||
response = dialog.choose_finish(task)
|
response = dialog.choose_finish(task)
|
||||||
@@ -227,3 +227,58 @@ def reconnect_remote(self, current_url):
|
|||||||
cancellable = None,
|
cancellable = None,
|
||||||
callback = lambda dialog, task, entry=entry: reconnect_remote_response(self, dialog, task, entry)
|
callback = lambda dialog, task, entry=entry: reconnect_remote_response(self, dialog, task, entry)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# CREATE MODEL | WORKS
|
||||||
|
|
||||||
|
def create_model_from_existing_response(self, dialog, task, dropdown):
|
||||||
|
model = dropdown.get_selected_item().get_string()
|
||||||
|
if dialog.choose_finish(task) == 'accept' and model:
|
||||||
|
self.create_model(model, False)
|
||||||
|
|
||||||
|
def create_model_from_existing(self):
|
||||||
|
string_list = Gtk.StringList()
|
||||||
|
for model in self.local_models:
|
||||||
|
string_list.append(model)
|
||||||
|
|
||||||
|
dropdown = Gtk.DropDown()
|
||||||
|
dropdown.set_model(string_list)
|
||||||
|
dialog = Adw.AlertDialog(
|
||||||
|
heading=_("Select Model"),
|
||||||
|
body=_("This model will be used as the base for the new model"),
|
||||||
|
extra_child=dropdown
|
||||||
|
)
|
||||||
|
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, dropdown=dropdown: create_model_from_existing_response(self, dialog, task, dropdown)
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_model_from_file_response(self, file_dialog, result):
|
||||||
|
try: file = file_dialog.open_finish(result)
|
||||||
|
except: return
|
||||||
|
try:
|
||||||
|
self.create_model(file.get_path(), True)
|
||||||
|
except Exception as e:
|
||||||
|
self.show_toast("error", 5, self.main_overlay) ##TODO NEW ERROR MESSAGE
|
||||||
|
|
||||||
|
def create_model_from_file(self):
|
||||||
|
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 CHOOSER | WORKS
|
||||||
|
|
||||||
|
def attach_file_response(self, file_dialog, result, file_type):
|
||||||
|
try: file = file_dialog.open_finish(result)
|
||||||
|
except: return
|
||||||
|
self.attach_file(file.get_path(), file_type)
|
||||||
|
|
||||||
|
|
||||||
|
def attach_file(self, filter, file_type):
|
||||||
|
if file_type == 'image' and not self.verify_if_image_can_be_used():
|
||||||
|
self.show_toast('error', 8, self.main_overlay)
|
||||||
|
return
|
||||||
|
file_dialog = Gtk.FileDialog(default_filter=filter)
|
||||||
|
file_dialog.open(self, None, lambda file_dialog, result, file_type=file_type: attach_file_response(self, file_dialog, result, file_type))
|
||||||
|
|||||||
2
src/icons/brain-augemnted-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 5.976562 2 c 0.546876 0 1 0.453125 1 1 v 10 c 0 0.546875 -0.453124 1 -1 1 h -0.976562 c -1.652344 0 -3 -1.347656 -3 -3 v -6 c 0 -1.652344 1.347656 -3 3 -3 z m -5.976562 3 v 6 c 0 2.765625 2.234375 5 5 5 h 0.976562 c 1.660157 0 3 -1.339844 3 -3 v -10 c 0 -1.660156 -1.339843 -3 -3 -3 h -0.976562 c -2.765625 0 -5 2.234375 -5 5 z m 0 0"/><path d="m 1.488281 8.996094 h 1.511719 c 1.101562 0 1.988281 -0.886719 1.988281 -1.984375 v -0.515625 c 0 -0.273438 -0.222656 -0.5 -0.5 -0.5 c -0.273437 0 -0.5 0.226562 -0.5 0.5 v 0.515625 c 0 0.542969 -0.445312 0.984375 -0.988281 0.984375 h -1.511719 c -0.273437 0 -0.5 0.226562 -0.5 0.5 c 0 0.277344 0.226563 0.5 0.5 0.5 z m 0 0"/><path d="m 7.5 9.992188 h -1.511719 c -1.101562 0 -1.988281 0.886718 -1.988281 1.984374 v 0.515626 c 0 0.273437 0.222656 0.5 0.5 0.5 s 0.5 -0.226563 0.5 -0.5 v -0.515626 c 0 -0.539062 0.445312 -0.984374 0.988281 -0.984374 h 1.511719 c 0.277344 0 0.5 -0.226563 0.5 -0.5 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 z m 0 0"/><path d="m 11.015625 14 h -1.035156 c -0.546875 0 -1 -0.453125 -1 -1 v -10 c 0 -0.546875 0.453125 -1 1 -1 h 1.035156 v -2 h -1.035156 c -1.664063 0 -3 1.339844 -3 3 v 10 c 0 1.660156 1.335937 3 3 3 h 1.035156 z m 0 0"/><path d="m 10 5 h 2.242188 l 2.148437 -2.6875 l -0.78125 -0.625 l -2 2.5 l 0.390625 -0.1875 h -2 z m 0 0"/><path d="m 10 11 h 2 l -0.390625 -0.1875 l 2 2.5 l 0.78125 -0.625 l -2.148437 -2.6875 h -2.242188 z m 0 0"/><path d="m 14.488281 1.976562 c -0.265625 0 -0.488281 -0.21875 -0.488281 -0.488281 c 0 -0.265625 0.222656 -0.488281 0.488281 -0.488281 c 0.269531 0 0.488281 0.222656 0.488281 0.488281 c 0 0.269531 -0.21875 0.488281 -0.488281 0.488281 z m 0 -1.976562 c -0.824219 0 -1.488281 0.664062 -1.488281 1.488281 s 0.664062 1.488281 1.488281 1.488281 s 1.488281 -0.664062 1.488281 -1.488281 s -0.664062 -1.488281 -1.488281 -1.488281 z m 0 0"/><path d="m 14.488281 13.976562 c -0.265625 0 -0.488281 -0.21875 -0.488281 -0.488281 c 0 -0.265625 0.222656 -0.488281 0.488281 -0.488281 c 0.269531 0 0.488281 0.222656 0.488281 0.488281 c 0 0.269531 -0.21875 0.488281 -0.488281 0.488281 z m 0 -1.976562 c -0.824219 0 -1.488281 0.664062 -1.488281 1.488281 s 0.664062 1.488281 1.488281 1.488281 s 1.488281 -0.664062 1.488281 -1.488281 s -0.664062 -1.488281 -1.488281 -1.488281 z m 0 0"/><path d="m 14.488281 7.976562 c -0.265625 0 -0.488281 -0.21875 -0.488281 -0.488281 c 0 -0.265625 0.222656 -0.488281 0.488281 -0.488281 c 0.269531 0 0.488281 0.222656 0.488281 0.488281 c 0 0.269531 -0.21875 0.488281 -0.488281 0.488281 z m 0 -1.976562 c -0.824219 0 -1.488281 0.664062 -1.488281 1.488281 s 0.664062 1.488281 1.488281 1.488281 s 1.488281 -0.664062 1.488281 -1.488281 s -0.664062 -1.488281 -1.488281 -1.488281 z m 0 0"/></g><path d="m 10 7.53125 h 4" fill="none" stroke="#222222"/><path d="m 4.5 4 h 3 c 0.277344 0 0.5 0.222656 0.5 0.5 s -0.222656 0.5 -0.5 0.5 h -3 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 s 0.222656 -0.5 0.5 -0.5 z m 0 0" fill="#222222"/></svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
2
src/icons/chain-link-loose-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 2.683594 9.777344 c -1.570313 -0.542969 -2.683594 -2.039063 -2.683594 -3.777344 c 0 -2.199219 1.800781 -4 4 -4 h 3 c 2.199219 0 4 1.800781 4 4 c 0 1.640625 -0.992188 3.070312 -2.421875 3.679688 l -0.785156 -1.839844 c 0.710937 -0.304688 1.207031 -1 1.207031 -1.839844 c 0 -1.125 -0.875 -2 -2 -2 h -3 c -1.125 0 -2 0.875 -2 2 c 0 0.890625 0.558594 1.621094 1.339844 1.890625 z m 0 0"/><path d="m 8 14 c -2.199219 0 -4 -1.800781 -4 -4 c 0 -1.621094 0.96875 -3.03125 2.367188 -3.65625 l 0.816406 1.828125 c -0.699219 0.3125 -1.183594 1 -1.183594 1.828125 c 0 1.125 0.875 2 2 2 h 3 c 1.125 0 2 -0.875 2 -2 c 0 -0.867188 -0.53125 -1.582031 -1.277344 -1.867188 l 0.714844 -1.867187 c 1.503906 0.574219 2.5625 2.039063 2.5625 3.734375 c 0 2.199219 -1.800781 4 -4 4 z m 0 0"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 934 B |
4
src/icons/chat-message-new-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 3 1 c -1.644531 0 -3 1.355469 -3 3 v 6 c 0 1.644531 1.355469 3 3 3 h 1 v 3 l 3 -3 v -1 c 0 -0.550781 -0.449219 -1 -1 -1 h -3 c -0.570312 0 -1 -0.429688 -1 -1 v -6 c 0 -0.554688 0.445312 -1 1 -1 h 10 c 0.554688 0 1 0.445312 1 1 v 4 c 0 0.550781 0.449219 1 1 1 s 1 -0.449219 1 -1 v -4 c 0 -1.644531 -1.355469 -3 -3 -3 z m 8 7 v 3 h -3 v 2 h 3 v 3 h 2 v -3 h 3 v -2 h -3 v -3 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 548 B |
4
src/icons/dialog-warning-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 7.90625 0.09375 c -0.527344 -0.0273438 -1.039062 0.28125 -1.4375 0.96875 l -6.25 11.59375 c -0.535156 0.964844 0.046875 2.34375 1.09375 2.34375 h 13.15625 c 0.980469 0 1.902344 -1.160156 1.21875 -2.34375 l -6.3125 -11.53125 c -0.398438 -0.644531 -0.941406 -1.003906 -1.46875 -1.03125 z m 1.09375 3.90625 v 5 c 0.007812 0.527344 -0.472656 1 -1 1 s -1.007812 -0.472656 -1 -1 v -5 z m -1 7 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 s -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 649 B |
4
src/icons/document-edit-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 12.277344 0.832031 c -0.578125 0.007813 -1.167969 0.230469 -1.691406 0.753907 l -9 9 c -0.375 0.375 -0.585938 0.882812 -0.585938 1.414062 v 3 h 3 c 0.53125 0 1.039062 -0.210938 1.414062 -0.585938 l 9 -9 c 1.789063 -1.789062 0.082032 -4.390624 -1.890624 -4.570312 c -0.082032 -0.011719 -0.164063 -0.011719 -0.246094 -0.011719 z m -1.777344 3.605469 l 1.0625 1.0625 l -7.0625 7.0625 l -1.0625 -1.0625 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 574 B |
7
src/icons/document-open-symbolic.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#2e3436">
|
||||||
|
<path d="m 3 1 c -1.644531 0 -3 1.355469 -3 3 v 8 c 0 1.644531 1.355469 3 3 3 h 8.882812 c 0.832032 0 1.578126 -0.402344 2.054688 -0.9375 c 0.472656 -0.53125 0.738281 -1.167969 0.910156 -1.800781 l 0.972656 -2.609375 c 0.390626 -1.449219 -0.09375 -2.652344 -0.820312 -3.167969 c -0.484375 -0.34375 -0.714844 -0.292969 -1 -0.324219 v -1.160156 c 0 -0.855469 -0.558594 -1.589844 -1.09375 -1.828125 c -0.53125 -0.238281 -1.011719 -0.167969 -1.011719 -0.167969 l 0.105469 -0.003906 h -3.585938 l -1.707031 -1.707031 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 2 h 2.585938 l 1.707031 1.707031 c 0.1875 0.1875 0.441406 0.292969 0.707031 0.292969 h 4 c 0.035156 0 0.070312 -0.003906 0.105469 -0.007812 c 0 0 0.019531 0.019531 -0.011719 0.003906 c -0.035156 -0.011719 -0.09375 -0.25 -0.09375 0.003906 v 2 c 0 0.550781 0.449219 1 1 1 c 1 0 1.046875 0.703125 0.886719 1.128906 l -0.972657 2.609375 c -0.117187 0.4375 -0.296874 0.800781 -0.472656 0.996094 c -0.175781 0.199219 -0.285156 0.265625 -0.558594 0.265625 h -8.882812 c -0.570312 0 -1 -0.429688 -1 -1 v -8 c 0 -0.570312 0.46875 -0.792969 1 -1 z m 0 0"/>
|
||||||
|
<path d="m 7 6 l 0.042969 0.003906 c -0.914063 -0.042968 -1.75 0.390625 -2.195313 0.96875 c -0.710937 1.222656 -1.15625 2.277344 -1.800781 3.71875 c -0.171875 0.523438 0.117187 1.089844 0.640625 1.261719 c 0.527344 0.171875 1.09375 -0.117187 1.261719 -0.640625 c 0.488281 -1.011719 0.921875 -1.816406 1.339843 -2.808594 c 0.210938 -0.503906 0.703126 -0.492187 0.898438 -0.503906 h 5.8125 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
2
src/icons/document-text-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 9 2 v 5 h 4 v -1 z m -4 2 v 1 h 3 v -1 z m 0 2 v 1 h 3 v -1 z m 0 2 v 1 h 6 v -1 z m 0 2 v 1 h 6 v -1 z m 0 2 v 1 h 6 v -1 z m 0 0"/><path d="m 2 13 c 0 1.660156 1.339844 3 3 3 h 6 c 1.660156 0 3 -1.339844 3 -3 v -6 c 0 -0.90625 -0.359375 -1.773438 -1 -2.414062 l -2.585938 -2.585938 c -0.640624 -0.640625 -1.507812 -1 -2.414062 -1 h -3 c -1.660156 0 -3 1.339844 -3 3 z m 3 -10 h 3 c 0.375 0 0.734375 0.148438 1 0.414062 l 2.585938 2.585938 c 0.265624 0.265625 0.414062 0.625 0.414062 1 v 6 c 0 0.546875 -0.453125 1 -1 1 h -6 c -0.546875 0 -1 -0.453125 -1 -1 v -9 c 0 -0.546875 0.453125 -1 1 -1 z m 0 0"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 771 B |
4
src/icons/edit-copy-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 0 3 c 0 -1.644531 1.355469 -3 3 -3 h 5 c 1.644531 0 3 1.355469 3 3 c 0 0.550781 -0.449219 1 -1 1 s -1 -0.449219 -1 -1 c 0 -0.570312 -0.429688 -1 -1 -1 h -5 c -0.570312 0 -1 0.429688 -1 1 v 5 c 0 0.570312 0.429688 1 1 1 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 c -1.644531 0 -3 -1.355469 -3 -3 z m 5 5 c 0 -1.644531 1.355469 -3 3 -3 h 5 c 1.644531 0 3 1.355469 3 3 v 5 c 0 1.644531 -1.355469 3 -3 3 h -5 c -1.644531 0 -3 -1.355469 -3 -3 z m 2 0 v 5 c 0 0.570312 0.429688 1 1 1 h 5 c 0.570312 0 1 -0.429688 1 -1 v -5 c 0 -0.570312 -0.429688 -1 -1 -1 h -5 c -0.570312 0 -1 0.429688 -1 1 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 765 B |
4
src/icons/folder-download-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 8 0 c -0.550781 0 -1 0.449219 -1 1 v 8.585938 l -2.292969 -2.292969 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 s -0.519531 0.105469 -0.707031 0.292969 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 l 4 4 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 l 4 -4 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 s -1.023437 -0.390625 -1.414062 0 l -2.292969 2.292969 v -8.585938 c 0 -0.550781 -0.449219 -1 -1 -1 z m -6 14 v 2 h 12 v -2 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 630 B |
2
src/icons/globe-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"><path d="m 7.5 0 c -4.128906 0 -7.5 3.371094 -7.5 7.5 s 3.371094 7.5 7.5 7.5 s 7.5 -3.371094 7.5 -7.5 s -3.371094 -7.5 -7.5 -7.5 z m 0 2 c 0.257812 0 0.503906 0.023438 0.75 0.054688 c 0.191406 0.261718 0.382812 0.59375 0.550781 1.027343 c 0.105469 0.277344 0.203125 0.585938 0.289063 0.917969 h -3.179688 c 0.085938 -0.332031 0.183594 -0.640625 0.289063 -0.917969 c 0.167969 -0.433593 0.359375 -0.765625 0.550781 -1.027343 c 0.246094 -0.03125 0.492188 -0.054688 0.75 -0.054688 z m -2.085938 0.40625 c -0.050781 0.109375 -0.105468 0.203125 -0.148437 0.316406 c -0.148437 0.386719 -0.269531 0.820313 -0.378906 1.277344 h -1.617188 c 0.570313 -0.691406 1.296875 -1.246094 2.144531 -1.59375 z m 4.171876 0 c 0.847656 0.347656 1.574218 0.902344 2.144531 1.59375 h -1.617188 c -0.109375 -0.457031 -0.230469 -0.890625 -0.378906 -1.277344 c -0.042969 -0.113281 -0.097656 -0.207031 -0.148437 -0.316406 z m -6.980469 2.59375 h 2.082031 c -0.097656 0.628906 -0.148438 1.300781 -0.167969 2 h -2.480469 c 0.0625 -0.714844 0.253907 -1.390625 0.566407 -2 z m 3.09375 0 h 3.601562 c 0.101563 0.617188 0.15625 1.292969 0.179688 2 h -3.960938 c 0.023438 -0.707031 0.078125 -1.382812 0.179688 -2 z m 4.613281 0 h 2.082031 c 0.3125 0.609375 0.503907 1.285156 0.566407 2 h -2.480469 c -0.019531 -0.699219 -0.070313 -1.371094 -0.167969 -2 z m -8.273438 3 h 2.480469 c 0.019531 0.699219 0.070313 1.375 0.167969 2 h -2.082031 c -0.3125 -0.609375 -0.503907 -1.285156 -0.566407 -2 z m 3.480469 0 h 3.960938 c -0.023438 0.707031 -0.078125 1.382812 -0.179688 2 h -3.601562 c -0.101563 -0.617188 -0.15625 -1.292969 -0.179688 -2 z m 4.960938 0 h 2.480469 c -0.0625 0.714844 -0.253907 1.390625 -0.566407 2 h -2.082031 c 0.097656 -0.625 0.148438 -1.300781 0.167969 -2 z m -7.210938 3 h 1.617188 c 0.109375 0.457031 0.230469 0.890625 0.378906 1.273438 c 0.042969 0.117187 0.097656 0.210937 0.148437 0.320312 c -0.847656 -0.347656 -1.574218 -0.902344 -2.144531 -1.59375 z m 2.640625 0 h 3.179688 c -0.085938 0.332031 -0.183594 0.640625 -0.289063 0.917969 c -0.167969 0.433593 -0.359375 0.765625 -0.550781 1.027343 c -0.246094 0.03125 -0.496094 0.054688 -0.75 0.054688 s -0.503906 -0.023438 -0.75 -0.054688 c -0.191406 -0.261718 -0.382812 -0.59375 -0.550781 -1.027343 c -0.105469 -0.277344 -0.203125 -0.585938 -0.289063 -0.917969 z m 4.203125 0 h 1.617188 c -0.570313 0.691406 -1.296875 1.246094 -2.144531 1.59375 c 0.050781 -0.109375 0.105468 -0.203125 0.148437 -0.320312 c 0.148437 -0.382813 0.269531 -0.816407 0.378906 -1.273438 z m 0 0" fill="#222222"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
7
src/icons/image-x-generic-symbolic.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#2e3436">
|
||||||
|
<path d="m 6 5 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 3.5 3 l -2 2 l -1.5 -1 l -2 2 v 0.5 c 0 0.5 0.5 0.5 0.5 0.5 h 7 s 0.472656 -0.035156 0.5 -0.5 v -1 z m 0 0"/>
|
||||||
|
<path d="m 4 1 c -1.644531 0 -3 1.355469 -3 3 v 8 c 0 1.644531 1.355469 3 3 3 h 8 c 1.644531 0 3 -1.355469 3 -3 v -8 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 2 h 8 c 0.570312 0 1 0.429688 1 1 v 8 c 0 0.570312 -0.429688 1 -1 1 h -8 c -0.570312 0 -1 -0.429688 -1 -1 v -8 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 708 B |
2
src/icons/library-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 1.5 2 h 2 c 0.277344 0 0.5 0.222656 0.5 0.5 v 12 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -2 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -12 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/><path d="m 5.5 4 h 1 c 0.277344 0 0.5 0.222656 0.5 0.5 v 10 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -1 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -10 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/><path d="m 8.5 3 h 1 c 0.277344 0 0.5 0.222656 0.5 0.5 v 11 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -1 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -11 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/><path d="m 10.707031 1.460938 l 0.964844 -0.261719 c 0.265625 -0.070313 0.539063 0.089843 0.613281 0.355469 l 3.363282 12.558593 c 0.070312 0.265625 -0.085938 0.539063 -0.351563 0.609375 l -0.96875 0.261719 c -0.265625 0.070313 -0.539063 -0.089844 -0.613281 -0.355469 l -3.363282 -12.554687 c -0.070312 -0.269531 0.085938 -0.542969 0.355469 -0.613281 z m 0 0"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
4
src/icons/list-add-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 7 1 v 6 h -6 v 2 h 6 v 6 h 2 v -6 h 6 v -2 h -6 v -6 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 228 B |
4
src/icons/media-playback-stop-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 3.5 2 h 9 c 0.828125 0 1.5 0.671875 1.5 1.5 v 9 c 0 0.828125 -0.671875 1.5 -1.5 1.5 h -9 c -0.828125 0 -1.5 -0.671875 -1.5 -1.5 v -9 c 0 -0.828125 0.671875 -1.5 1.5 -1.5 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 345 B |
8
src/icons/open-menu-symbolic.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#2e3436">
|
||||||
|
<path d="m 1 2 h 14 v 2 h -14 z m 0 0"/>
|
||||||
|
<path d="m 1 7 h 14 v 2 h -14 z m 0 0"/>
|
||||||
|
<path d="m 1 12 h 14 v 2 h -14 z m 0 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 314 B |
2
src/icons/paper-plane-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"><path d="m 15 8 l -14 -7 v 6 l 8 1 l -8 1 v 6 z m 0 0" fill="#222222"/></svg>
|
||||||
|
After Width: | Height: | Size: 204 B |
4
src/icons/preferences-other-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 12.5 0.0234375 c -0.292969 -0.0039063 -0.59375 0.0195313 -0.882812 0.0937505 c -1.867188 0.488281 -2.984376 2.382812 -2.496094 4.234374 c 0.101562 0.386719 0.289062 0.742188 0.5 1.054688 c -0.109375 0.015625 -1.625 2.59375 -1.625 2.59375 l 3.410156 0.023438 l 0.800781 -1.117188 c 0.378907 0.03125 0.78125 0.023438 1.171875 -0.078125 c 1.863282 -0.484375 2.980469 -2.382813 2.496094 -4.234375 c -0.105469 -0.386719 -0.292969 -0.738281 -0.503906 -1.054688 l -1.359375 2.304688 c -0.28125 0.476562 -0.882813 0.632812 -1.355469 0.355469 l -0.875 -0.507813 c -0.476562 -0.277344 -0.636719 -0.875 -0.355469 -1.347656 l 1.363281 -2.3085938 c -0.097656 -0.0039062 -0.195312 -0.0117187 -0.289062 -0.0117187 z m -8.875 0.6914065 c -0.011719 0 -0.019531 0 -0.027344 0.003906 c -0.230468 0.046875 -0.445312 0.132812 -0.65625 0.21875 c -0.019531 0.625 0.085938 1.441406 -0.15625 1.65625 c -0.242187 0.210938 -1.039062 0.035156 -1.652344 -0.03125 c -0.132812 0.238281 -0.238281 0.484375 -0.3125 0.75 c 0.476563 0.394531 1.136719 0.839844 1.152344 1.15625 c 0.015625 0.320312 -0.625 0.804688 -1.058594 1.25 c 0.101563 0.257812 0.25 0.492188 0.40625 0.71875 c 0.605469 -0.125 1.359376 -0.375 1.621094 -0.1875 c 0.261719 0.1875 0.261719 1.003906 0.34375 1.625 c 0.246094 0.074219 0.507813 0.105469 0.777344 0.125 c 0.28125 -0.554688 0.566406 -1.320312 0.875 -1.40625 c 0.316406 -0.089844 0.960938 0.457031 1.5 0.78125 c 0.214844 -0.152344 0.410156 -0.335938 0.589844 -0.53125 c -0.253906 -0.574219 -0.71875 -1.292969 -0.589844 -1.59375 c 0.125 -0.300781 0.96875 -0.46875 1.558594 -0.6875 c 0.003906 -0.074219 0.03125 -0.144531 0.03125 -0.21875 c 0 -0.191406 -0.035156 -0.378906 -0.0625 -0.5625 c -0.605469 -0.160156 -1.464844 -0.242188 -1.621094 -0.53125 c -0.160156 -0.289062 0.238281 -1.0625 0.433594 -1.65625 c -0.199219 -0.179688 -0.417969 -0.335938 -0.652344 -0.46875 c -0.503906 0.375 -1.085938 0.996094 -1.40625 0.9375 c -0.316406 -0.054688 -0.660156 -0.820312 -0.996094 -1.34375 c -0.03125 0.003906 -0.066406 0 -0.097656 -0.003906 z m 0.59375 1.878906 c 0.929688 0 1.6875 0.753906 1.6875 1.6875 s -0.757812 1.6875 -1.6875 1.6875 s -1.683594 -0.753906 -1.683594 -1.6875 s 0.753906 -1.6875 1.683594 -1.6875 z m 8.621094 6.332031 c -0.144532 0 -0.273438 0.023438 -0.359375 0.074219 h -9.050781 c -0.246094 -0.105469 -0.746094 -0.046875 -0.964844 0.179688 l -2.160156 2.113281 c -0.191407 0.175781 -0.30078175 0.484375 -0.304688 0.707031 c -0.0546875 0.886719 1 1.320312 1.636719 0.792969 l 1.363281 -1.332031 l 0.003906 3.539062 c 0 0.570312 0.457032 1 0.996094 1 h 7.992188 c 0.523437 0 0.996093 -0.445312 0.996093 -0.9375 l -0.003906 -3.601562 l 1.335937 1.308593 c 0.636719 0.527344 1.6875 0.09375 1.636719 -0.792969 c -0.003906 -0.222656 -0.113281 -0.53125 -0.300781 -0.703124 l -2.164062 -2.117188 c -0.144532 -0.152344 -0.417969 -0.226562 -0.652344 -0.230469 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
4
src/icons/preferences-system-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 13.855469 0 l -1.539063 1.4375 c -0.453125 0.421875 -0.53125 1.148438 -0.269531 1.707031 l -5.886719 5.996094 c -0.011718 0 -0.019531 0 -0.03125 0 c -0.257812 -0.128906 -0.550781 -0.183594 -0.839844 -0.148437 c -0.328124 0.046874 -0.632812 0.199218 -0.867187 0.441406 l -3.945313 3.996094 c -0.3906245 0.375 -0.5468745 0.933593 -0.4062495 1.457031 c 0.1406255 0.523437 0.5546875 0.929687 1.0820315 1.058593 c 0.527344 0.132813 1.082031 -0.03125 1.453125 -0.425781 l 3.945312 -3.996093 c 0.472657 -0.453126 0.59375 -1.15625 0.296875 -1.738282 l 5.890625 -5.964844 c 0.558594 0.25 1.273438 0.148438 1.707031 -0.289062 l 1.414063 -1.5625 z m -10.308594 0.0898438 c -0.398437 0 -0.785156 0.0937502 -1.140625 0.2187502 l 1.882812 1.878906 c 0.390626 0.382812 0.390626 1 0 1.386719 l -0.710937 0.707031 c -0.386719 0.386719 -1 0.386719 -1.390625 0 l -1.882812 -1.878906 c -0.125 0.355468 -0.2187505 0.742187 -0.2187505 1.140625 c 0 1.90625 1.5507815 3.453125 3.4609375 3.453125 c 0.402344 0 0.789063 -0.09375 1.144531 -0.21875 l 1.175782 1.171875 h 0.058593 l 2.070313 -2.0625 l -1.203125 -1.203125 c 0.125 -0.359375 0.214843 -0.742188 0.214843 -1.140625 c 0 -1.90625 -1.546874 -3.4531252 -3.460937 -3.4531252 z m 6.550781 7.8906252 l -2.070312 2.066406 c 0.011718 0.027344 0.023437 0.058594 0.03125 0.089844 l 1.144531 1.140625 c -0.125 0.355468 -0.21875 0.742187 -0.21875 1.140625 c 0 1.902343 1.550781 3.449219 3.460937 3.449219 c 0.433594 0 0.855469 -0.101563 1.238282 -0.246094 l -2.007813 -2 c -0.386719 -0.386719 -0.386719 -1.035156 0 -1.417969 l 0.679688 -0.679687 c 0.195312 -0.191407 0.457031 -0.308594 0.710937 -0.308594 s 0.515625 0.117187 0.710938 0.308594 l 1.945312 1.941406 c 0.105469 -0.328125 0.183594 -0.683594 0.183594 -1.046875 c 0 -1.90625 -1.546875 -3.453125 -3.460938 -3.453125 c -0.398437 0 -0.785156 0.09375 -1.140624 0.21875 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
8
src/icons/sidebar-show-symbolic.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#2e3436">
|
||||||
|
<path d="m 6.5 14 v -12 h -5 v 12 z m 0 0" fill-opacity="0.34902"/>
|
||||||
|
<path d="m 3 1 c -1.644531 0 -3 1.355469 -3 3 v 8 c 0 1.644531 1.355469 3 3 3 h 10 c 1.644531 0 3 -1.355469 3 -3 v -8 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 2 h 10 c 0.570312 0 1 0.429688 1 1 v 8 c 0 0.570312 -0.429688 1 -1 1 h -10 c -0.570312 0 -1 -0.429688 -1 -1 v -8 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/>
|
||||||
|
<path d="m 6 2 h 1 v 12 h -1 z m 0 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 610 B |
10
src/icons/user-trash-symbolic.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#2e3436">
|
||||||
|
<path d="m 1 3 h 14 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 h -14 c -0.550781 0 -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 z m 0 0"/>
|
||||||
|
<path d="m 4 4 v -1.5 c 0 -1.386719 1.113281 -2.5 2.5 -2.5 h 2.980469 c 1.382812 0 2.5 1.113281 2.5 2.5 v 1.5 h -2 v -1.5 c 0 -0.269531 -0.230469 -0.5 -0.5 -0.5 h -2.980469 c -0.269531 0 -0.5 0.230469 -0.5 0.5 v 1.5 z m 0 0"/>
|
||||||
|
<path d="m 4 4 v 9 c 0 0.546875 0.453125 1 1 1 h 6 c 0.546875 0 1 -0.453125 1 -1 v -9 h 2 v 9 c 0 1.660156 -1.339844 3 -3 3 h -6 c -1.660156 0 -3 -1.339844 -3 -3 v -9 z m 0 0"/>
|
||||||
|
<path d="m 7 7 v 5 c 0 0.277344 -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 v -5 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 s 0.5 0.222656 0.5 0.5 z m 0 0"/>
|
||||||
|
<path d="m 10 7 v 5 c 0 0.277344 -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 v -5 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 s 0.5 0.222656 0.5 0.5 z m 0 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
4
src/icons/view-more-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m 7.996094 0 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 c 1.101562 0 2 -0.894531 2 -2 s -0.898438 -2 -2 -2 z m 0 6 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 c 1.101562 0 2 -0.894531 2 -2 s -0.898438 -2 -2 -2 z m 0 6 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 c 1.101562 0 2 -0.894531 2 -2 s -0.898438 -2 -2 -2 z m 0 0" fill="#2e3436"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 499 B |
@@ -5,16 +5,25 @@ from time import sleep
|
|||||||
instance = None
|
instance = None
|
||||||
port = 11435
|
port = 11435
|
||||||
data_dir = os.getenv("XDG_DATA_HOME")
|
data_dir = os.getenv("XDG_DATA_HOME")
|
||||||
|
overrides = {}
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
instance = subprocess.Popen(["/app/bin/ollama", "serve"], env={**os.environ, 'OLLAMA_HOST': f"127.0.0.1:{port}", "HOME": data_dir}, stderr=subprocess.PIPE, text=True)
|
global instance, overrides
|
||||||
|
params = overrides.copy()
|
||||||
|
params["OLLAMA_HOST"] = f"127.0.0.1:{port}" # You can't change this directly sorry :3
|
||||||
|
params["HOME"] = data_dir
|
||||||
|
instance = subprocess.Popen(["/app/bin/ollama", "serve"], env={**os.environ, **params}, stderr=subprocess.PIPE, text=True)
|
||||||
print("Starting Alpaca's Ollama instance...")
|
print("Starting Alpaca's Ollama instance...")
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("Started Alpaca's Ollama instance")
|
print("Started Alpaca's Ollama instance")
|
||||||
|
|
||||||
def stop():
|
def stop():
|
||||||
if instance: instance.kill()
|
global instance
|
||||||
print("Stopped Alpaca's Ollama instance")
|
if instance:
|
||||||
|
instance.kill()
|
||||||
|
instance.wait()
|
||||||
|
instance = None
|
||||||
|
print("Stopped Alpaca's Ollama instance")
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
stop()
|
stop()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# main.py
|
# main.py
|
||||||
#
|
#
|
||||||
# Copyright 2024 Unknown
|
# Copyright 2024 Jeffser
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -47,7 +47,7 @@ class AlpacaApplication(Adw.Application):
|
|||||||
application_name='Alpaca',
|
application_name='Alpaca',
|
||||||
application_icon='com.jeffser.Alpaca',
|
application_icon='com.jeffser.Alpaca',
|
||||||
developer_name='Jeffry Samuel Eduarte Rojas',
|
developer_name='Jeffry Samuel Eduarte Rojas',
|
||||||
version='0.9.0',
|
version='0.9.4',
|
||||||
developers=['Jeffser https://jeffser.com'],
|
developers=['Jeffser https://jeffser.com'],
|
||||||
designers=['Jeffser https://jeffser.com'],
|
designers=['Jeffser https://jeffser.com'],
|
||||||
translator_credits='Alex K (Russian) https://github.com/alexkdeveloper\nJeffser (Spanish) https://jeffser.com\nDaimar Stein (Brazilian Portuguese) https://github.com/not-a-dev-stein\nLouis Chauvet-Villaret (French) https://github.com/loulou64490',
|
translator_credits='Alex K (Russian) https://github.com/alexkdeveloper\nJeffser (Spanish) https://jeffser.com\nDaimar Stein (Brazilian Portuguese) https://github.com/not-a-dev-stein\nLouis Chauvet-Villaret (French) https://github.com/loulou64490',
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ configure_file(
|
|||||||
install_mode: 'r-xr-xr-x'
|
install_mode: 'r-xr-xr-x'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#configure_file(
|
||||||
|
#input: 'alpaca_search_provider.in',
|
||||||
|
#output: 'alpaca_search_provider',
|
||||||
|
#configuration: conf,
|
||||||
|
#install: true,
|
||||||
|
#install_dir: get_option('bindir'),
|
||||||
|
#install_mode: 'r-xr-xr-x'
|
||||||
|
#)
|
||||||
|
|
||||||
alpaca_sources = [
|
alpaca_sources = [
|
||||||
'__init__.py',
|
'__init__.py',
|
||||||
'main.py',
|
'main.py',
|
||||||
@@ -33,7 +42,8 @@ alpaca_sources = [
|
|||||||
'connection_handler.py',
|
'connection_handler.py',
|
||||||
'available_models.py',
|
'available_models.py',
|
||||||
'dialogs.py',
|
'dialogs.py',
|
||||||
'local_instance.py'
|
'local_instance.py',
|
||||||
|
'update_history.py'
|
||||||
]
|
]
|
||||||
|
|
||||||
install_data(alpaca_sources, install_dir: moduledir)
|
install_data(alpaca_sources, install_dir: moduledir)
|
||||||
|
|||||||
38
src/update_history.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# update_history.py
|
||||||
|
# This script updates the old chats.json file to the structure needed for the new version
|
||||||
|
import os, json, base64
|
||||||
|
from PIL import Image
|
||||||
|
import io
|
||||||
|
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
old_data = None
|
||||||
|
new_data = {"chats": {}}
|
||||||
|
with open(os.path.join(self.config_dir, "chats.json"), 'r') as f:
|
||||||
|
old_data = json.load(f)["chats"]
|
||||||
|
for chat_name, content in old_data.items():
|
||||||
|
directory = os.path.join(self.data_dir, "chats", chat_name)
|
||||||
|
if not os.path.exists(directory): os.makedirs(directory)
|
||||||
|
new_messages = {}
|
||||||
|
for message in content['messages']:
|
||||||
|
message_id = self.generate_uuid()
|
||||||
|
if 'images' in message:
|
||||||
|
if not os.path.exists(os.path.join(directory, message_id)): os.makedirs(os.path.join(directory, message_id))
|
||||||
|
new_images = []
|
||||||
|
for image in message['images']:
|
||||||
|
file_name = f"{self.generate_uuid()}.png"
|
||||||
|
decoded = base64.b64decode(image)
|
||||||
|
buffer = io.BytesIO(decoded)
|
||||||
|
im = Image.open(buffer)
|
||||||
|
im.save(os.path.join(directory, message_id, file_name))
|
||||||
|
new_images.append(file_name)
|
||||||
|
message['images'] = new_images
|
||||||
|
new_messages[message_id] = message
|
||||||
|
new_data['chats'][chat_name] = {}
|
||||||
|
new_data['chats'][chat_name]['messages'] = new_messages
|
||||||
|
|
||||||
|
with open(os.path.join(self.data_dir, "chats", "chats.json"), "w+") as f:
|
||||||
|
json.dump(new_data, f, indent=6)
|
||||||
|
|
||||||
|
os.remove(os.path.join(self.config_dir, "chats.json"))
|
||||||
|
|
||||||
710
src/window.py
@@ -1,6 +1,6 @@
|
|||||||
# window.py
|
# window.py
|
||||||
#
|
#
|
||||||
# Copyright 2024 Unknown
|
# Copyright 2024 Jeffser
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,18 +21,20 @@ 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, webbrowser, subprocess
|
import json, requests, threading, os, re, base64, sys, gettext, locale, webbrowser, subprocess, uuid, shutil, tarfile, tempfile
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from .available_models import available_models
|
from .available_models import available_models
|
||||||
from . import dialogs, local_instance, connection_handler
|
from . import dialogs, local_instance, connection_handler, update_history
|
||||||
|
|
||||||
@Gtk.Template(resource_path='/com/jeffser/Alpaca/window.ui')
|
@Gtk.Template(resource_path='/com/jeffser/Alpaca/window.ui')
|
||||||
class AlpacaWindow(Adw.ApplicationWindow):
|
class AlpacaWindow(Adw.ApplicationWindow):
|
||||||
config_dir = os.getenv("XDG_CONFIG_HOME")
|
config_dir = os.getenv("XDG_CONFIG_HOME")
|
||||||
|
data_dir = os.getenv("XDG_DATA_HOME")
|
||||||
app_dir = os.getenv("FLATPAK_DEST")
|
app_dir = os.getenv("FLATPAK_DEST")
|
||||||
|
cache_dir = os.getenv("XDG_CACHE_HOME")
|
||||||
|
|
||||||
__gtype_name__ = 'AlpacaWindow'
|
__gtype_name__ = 'AlpacaWindow'
|
||||||
|
|
||||||
@@ -47,18 +49,33 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
run_on_background = False
|
run_on_background = False
|
||||||
remote_url = ""
|
remote_url = ""
|
||||||
run_remote = False
|
run_remote = False
|
||||||
|
model_tweaks = {"temperature": 0.7, "seed": 0, "keep_alive": 5}
|
||||||
local_models = []
|
local_models = []
|
||||||
pulling_models = {}
|
pulling_models = {}
|
||||||
current_chat_elements = [] #Used for deleting
|
chats = {"chats": {_("New Chat"): {"messages": {}}}, "selected_chat": "New Chat"}
|
||||||
chats = {"chats": {_("New Chat"): {"messages": []}}, "selected_chat": "New Chat"}
|
attachments = {}
|
||||||
attached_image = {"path": None, "base64": None}
|
|
||||||
|
#Override elements
|
||||||
|
override_HSA_OVERRIDE_GFX_VERSION = Gtk.Template.Child()
|
||||||
|
override_CUDA_VISIBLE_DEVICES = Gtk.Template.Child()
|
||||||
|
override_HIP_VISIBLE_DEVICES = Gtk.Template.Child()
|
||||||
|
|
||||||
#Elements
|
#Elements
|
||||||
|
create_model_base = Gtk.Template.Child()
|
||||||
|
create_model_name = Gtk.Template.Child()
|
||||||
|
create_model_system = Gtk.Template.Child()
|
||||||
|
create_model_template = Gtk.Template.Child()
|
||||||
|
create_model_dialog = Gtk.Template.Child()
|
||||||
|
temperature_spin = Gtk.Template.Child()
|
||||||
|
seed_spin = Gtk.Template.Child()
|
||||||
|
keep_alive_spin = Gtk.Template.Child()
|
||||||
preferences_dialog = Gtk.Template.Child()
|
preferences_dialog = Gtk.Template.Child()
|
||||||
shortcut_window : Gtk.ShortcutsWindow = Gtk.Template.Child()
|
shortcut_window : Gtk.ShortcutsWindow = Gtk.Template.Child()
|
||||||
bot_message : Gtk.TextBuffer = None
|
bot_message : Gtk.TextBuffer = None
|
||||||
bot_message_box : Gtk.Box = None
|
bot_message_box : Gtk.Box = None
|
||||||
bot_message_view : Gtk.TextView = None
|
bot_message_view : Gtk.TextView = None
|
||||||
|
file_preview_dialog = Gtk.Template.Child()
|
||||||
|
file_preview_text_view = Gtk.Template.Child()
|
||||||
welcome_dialog = Gtk.Template.Child()
|
welcome_dialog = Gtk.Template.Child()
|
||||||
welcome_carousel = Gtk.Template.Child()
|
welcome_carousel = Gtk.Template.Child()
|
||||||
welcome_previous_button = Gtk.Template.Child()
|
welcome_previous_button = Gtk.Template.Child()
|
||||||
@@ -69,9 +86,14 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
chat_window = Gtk.Template.Child()
|
chat_window = Gtk.Template.Child()
|
||||||
message_text_view = Gtk.Template.Child()
|
message_text_view = Gtk.Template.Child()
|
||||||
send_button = Gtk.Template.Child()
|
send_button = Gtk.Template.Child()
|
||||||
image_button = Gtk.Template.Child()
|
stop_button = Gtk.Template.Child()
|
||||||
|
chats_menu_button = Gtk.Template.Child()
|
||||||
|
attachment_container = Gtk.Template.Child()
|
||||||
|
attachment_box = Gtk.Template.Child()
|
||||||
file_filter_image = Gtk.Template.Child()
|
file_filter_image = Gtk.Template.Child()
|
||||||
file_filter_json = Gtk.Template.Child()
|
file_filter_tar = Gtk.Template.Child()
|
||||||
|
file_filter_gguf = Gtk.Template.Child()
|
||||||
|
file_filter_text = Gtk.Template.Child()
|
||||||
model_drop_down = Gtk.Template.Child()
|
model_drop_down = Gtk.Template.Child()
|
||||||
model_string_list = Gtk.Template.Child()
|
model_string_list = Gtk.Template.Child()
|
||||||
|
|
||||||
@@ -82,8 +104,6 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
chat_list_box = Gtk.Template.Child()
|
chat_list_box = Gtk.Template.Child()
|
||||||
add_chat_button = Gtk.Template.Child()
|
add_chat_button = Gtk.Template.Child()
|
||||||
export_chat_button = Gtk.Template.Child()
|
|
||||||
import_chat_button = Gtk.Template.Child()
|
|
||||||
|
|
||||||
loading_spinner = None
|
loading_spinner = None
|
||||||
|
|
||||||
@@ -100,14 +120,16 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
_("Could not pull model"),
|
_("Could not pull model"),
|
||||||
_("Cannot open image"),
|
_("Cannot open image"),
|
||||||
_("Cannot delete chat because it's the only one left"),
|
_("Cannot delete chat because it's the only one left"),
|
||||||
_("There was an error with the local Ollama instance, so it has been reset")
|
_("There was an error with the local Ollama instance, so it has been reset"),
|
||||||
|
_("Image recognition is only available on specific models")
|
||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
_("Please select a model before chatting"),
|
_("Please select a model before chatting"),
|
||||||
_("Chat cannot be cleared while receiving a message"),
|
_("Chat cannot be cleared while receiving a message"),
|
||||||
_("That tag is already being pulled"),
|
_("That tag is already being pulled"),
|
||||||
_("That tag has been pulled already"),
|
_("That tag has been pulled already"),
|
||||||
_("Code copied to the clipboard")
|
_("Code copied to the clipboard"),
|
||||||
|
_("Message copied to the clipboard")
|
||||||
],
|
],
|
||||||
"good": [
|
"good": [
|
||||||
_("Model deleted successfully"),
|
_("Model deleted successfully"),
|
||||||
@@ -124,65 +146,81 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
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]
|
selected = self.model_drop_down.get_selected_item().get_string().split(":")[0]
|
||||||
if selected in ['llava', 'bakllava', 'moondream', 'llava-llama3']:
|
if selected in ['llava', 'bakllava', 'moondream', 'llava-llama3']:
|
||||||
self.image_button.set_sensitive(True)
|
for name, content in self.attachments.items():
|
||||||
self.image_button.set_tooltip_text(_("Upload image"))
|
if content['type'] == 'image':
|
||||||
|
content['button'].set_css_classes(["flat"])
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.image_button.set_sensitive(False)
|
for name, content in self.attachments.items():
|
||||||
self.image_button.set_tooltip_text(_("Only available on selected models"))
|
if content['type'] == 'image':
|
||||||
self.image_button.set_css_classes(["circular"])
|
content['button'].set_css_classes(["flat", "error"])
|
||||||
self.image_button.get_child().set_icon_name("image-x-generic-symbolic")
|
|
||||||
self.attached_image = {"path": None, "base64": None}
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def stop_message(self, button=None):
|
||||||
|
if self.loading_spinner: self.chat_container.remove(self.loading_spinner)
|
||||||
|
self.toggle_ui_sensitive(True)
|
||||||
|
self.switch_send_stop_button()
|
||||||
|
self.bot_message = None
|
||||||
|
self.bot_message_box = None
|
||||||
|
self.bot_message_view = None
|
||||||
|
|
||||||
@Gtk.Template.Callback()
|
@Gtk.Template.Callback()
|
||||||
def send_message(self, button=None):
|
def send_message(self, button=None):
|
||||||
if button and self.bot_message: #STOP BUTTON
|
if self.bot_message: return
|
||||||
if self.loading_spinner: self.chat_container.remove(self.loading_spinner)
|
if not self.message_text_view.get_buffer().get_text(self.message_text_view.get_buffer().get_start_iter(), self.message_text_view.get_buffer().get_end_iter(), False): return
|
||||||
if self.verify_if_image_can_be_used(): self.image_button.set_sensitive(True)
|
current_model = self.model_drop_down.get_selected_item()
|
||||||
self.image_button.set_css_classes(["circular"])
|
if current_model is None:
|
||||||
self.image_button.get_child().set_icon_name("image-x-generic-symbolic")
|
self.show_toast("info", 0, self.main_overlay)
|
||||||
self.attached_image = {"path": None, "base64": None}
|
return
|
||||||
self.toggle_ui_sensitive(True)
|
id = self.generate_uuid()
|
||||||
self.send_button.set_css_classes(["suggested-action"])
|
|
||||||
self.send_button.get_child().set_label("Send")
|
|
||||||
self.send_button.get_child().set_icon_name("send-to-symbolic")
|
|
||||||
self.bot_message = None
|
|
||||||
self.bot_message_box = None
|
|
||||||
self.bot_message_view = None
|
|
||||||
else:
|
|
||||||
if not self.message_text_view.get_buffer().get_text(self.message_text_view.get_buffer().get_start_iter(), self.message_text_view.get_buffer().get_end_iter(), False): return
|
|
||||||
current_model = self.model_drop_down.get_selected_item()
|
|
||||||
if current_model is None:
|
|
||||||
self.show_toast("info", 0, self.main_overlay)
|
|
||||||
return
|
|
||||||
formated_datetime = datetime.now().strftime("%Y/%m/%d %H:%M")
|
|
||||||
self.chats["chats"][self.chats["selected_chat"]]["messages"].append({
|
|
||||||
"role": "user",
|
|
||||||
"model": "User",
|
|
||||||
"date": formated_datetime,
|
|
||||||
"content": self.message_text_view.get_buffer().get_text(self.message_text_view.get_buffer().get_start_iter(), self.message_text_view.get_buffer().get_end_iter(), False)
|
|
||||||
})
|
|
||||||
data = {
|
|
||||||
"model": current_model.get_string(),
|
|
||||||
"messages": self.chats["chats"][self.chats["selected_chat"]]["messages"]
|
|
||||||
}
|
|
||||||
if self.verify_if_image_can_be_used() and self.attached_image["base64"] is not None:
|
|
||||||
data["messages"][-1]["images"] = [self.attached_image["base64"]]
|
|
||||||
self.send_button.set_css_classes(["destructive-action"])
|
|
||||||
self.send_button.get_child().set_label("Stop")
|
|
||||||
self.send_button.get_child().set_icon_name("edit-delete-symbolic")
|
|
||||||
self.toggle_ui_sensitive(False)
|
|
||||||
self.image_button.set_sensitive(False)
|
|
||||||
|
|
||||||
self.show_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), False, f"\n\n<small>{formated_datetime}</small>", self.attached_image["base64"])
|
attached_images = []
|
||||||
self.message_text_view.get_buffer().set_text("", 0)
|
attached_files = {}
|
||||||
self.loading_spinner = Gtk.Spinner(spinning=True, margin_top=12, margin_bottom=12, hexpand=True)
|
can_use_images = self.verify_if_image_can_be_used()
|
||||||
self.chat_container.append(self.loading_spinner)
|
for name, content in self.attachments.items():
|
||||||
self.show_message("", True)
|
if content["type"] == 'image' and can_use_images: attached_images.append(name)
|
||||||
|
else: attached_files[name] = content['type']
|
||||||
|
if not os.path.exists(os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id)):
|
||||||
|
os.makedirs(os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id))
|
||||||
|
shutil.copy(content['path'], os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id, name))
|
||||||
|
content["button"].get_parent().remove(content["button"])
|
||||||
|
self.attachments = {}
|
||||||
|
self.attachment_box.set_visible(False)
|
||||||
|
|
||||||
thread = threading.Thread(target=self.run_message, args=(data['messages'], data['model']))
|
#{"path": file_path, "type": file_type, "content": content}
|
||||||
thread.start()
|
|
||||||
|
formated_datetime = datetime.now().strftime("%Y/%m/%d %H:%M")
|
||||||
|
|
||||||
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id] = {
|
||||||
|
"role": "user",
|
||||||
|
"model": "User",
|
||||||
|
"date": formated_datetime,
|
||||||
|
"content": self.message_text_view.get_buffer().get_text(self.message_text_view.get_buffer().get_start_iter(), self.message_text_view.get_buffer().get_end_iter(), False)
|
||||||
|
}
|
||||||
|
if len(attached_images) > 0:
|
||||||
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id]['images'] = attached_images
|
||||||
|
if len(attached_files.keys()) > 0:
|
||||||
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id]['files'] = attached_files
|
||||||
|
data = {
|
||||||
|
"model": current_model.get_string(),
|
||||||
|
"messages": self.convert_history_to_ollama(),
|
||||||
|
"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)
|
||||||
|
|
||||||
|
#self.attachments[name] = {"path": file_path, "type": file_type, "content": content}
|
||||||
|
self.show_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), False, f"\n\n<small>{formated_datetime}</small>", attached_images, attached_files, id=id)
|
||||||
|
self.message_text_view.get_buffer().set_text("", 0)
|
||||||
|
self.loading_spinner = Gtk.Spinner(spinning=True, margin_top=12, margin_bottom=12, hexpand=True)
|
||||||
|
self.chat_container.append(self.loading_spinner)
|
||||||
|
bot_id=self.generate_uuid()
|
||||||
|
self.show_message("", True, id=bot_id)
|
||||||
|
|
||||||
|
thread = threading.Thread(target=self.run_message, args=(data['messages'], data['model'], bot_id))
|
||||||
|
thread.start()
|
||||||
|
|
||||||
@Gtk.Template.Callback()
|
@Gtk.Template.Callback()
|
||||||
def manage_models_button_activate(self, button=None):
|
def manage_models_button_activate(self, button=None):
|
||||||
@@ -208,22 +246,14 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
if not self.verify_connection():
|
if not self.verify_connection():
|
||||||
self.connection_error()
|
self.connection_error()
|
||||||
|
|
||||||
@Gtk.Template.Callback()
|
|
||||||
def open_image(self, button):
|
|
||||||
if "destructive-action" in button.get_css_classes():
|
|
||||||
dialogs.remove_image(self)
|
|
||||||
else:
|
|
||||||
file_dialog = Gtk.FileDialog(default_filter=self.file_filter_image)
|
|
||||||
file_dialog.open(self, None, self.load_image)
|
|
||||||
|
|
||||||
@Gtk.Template.Callback()
|
@Gtk.Template.Callback()
|
||||||
def chat_changed(self, listbox, row):
|
def chat_changed(self, listbox, row):
|
||||||
if row and row.get_name() != self.chats["selected_chat"]:
|
if row and row.get_name() != self.chats["selected_chat"]:
|
||||||
self.chats["selected_chat"] = row.get_name()
|
self.chats["selected_chat"] = row.get_name()
|
||||||
self.load_history_into_chat()
|
self.load_history_into_chat()
|
||||||
if len(self.chats["chats"][self.chats["selected_chat"]]["messages"]) > 0:
|
if len(self.chats["chats"][self.chats["selected_chat"]]["messages"].keys()) > 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"][-1]["model"]:
|
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"]:
|
||||||
self.model_drop_down.set_selected(i)
|
self.model_drop_down.set_selected(i)
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -256,6 +286,100 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
print("Closing app...")
|
print("Closing app...")
|
||||||
local_instance.stop()
|
local_instance.stop()
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def model_spin_changed(self, spin):
|
||||||
|
value = spin.get_value()
|
||||||
|
if spin.get_name() != "temperature": value = round(value)
|
||||||
|
else: value = round(value, 1)
|
||||||
|
if self.model_tweaks[spin.get_name()] is not None and self.model_tweaks[spin.get_name()] != value:
|
||||||
|
self.model_tweaks[spin.get_name()] = value
|
||||||
|
self.save_server_config()
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def create_model_start(self, button):
|
||||||
|
base = self.create_model_base.get_subtitle()
|
||||||
|
name = self.create_model_name.get_text()
|
||||||
|
system = self.create_model_system.get_text()
|
||||||
|
template = self.create_model_template.get_text()
|
||||||
|
if "/" in base:
|
||||||
|
modelfile = f"FROM {base}\nSYSTEM {system}\nTEMPLATE {template}"
|
||||||
|
else:
|
||||||
|
modelfile = f"FROM {base}\nSYSTEM {system}"
|
||||||
|
self.pulling_model_list_box.set_visible(True)
|
||||||
|
model_row = Adw.ActionRow(
|
||||||
|
title = name
|
||||||
|
)
|
||||||
|
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": name, "modelfile": modelfile})
|
||||||
|
overlay = Gtk.Overlay()
|
||||||
|
progress_bar = Gtk.ProgressBar(
|
||||||
|
valign = 2,
|
||||||
|
show_text = False,
|
||||||
|
margin_start = 10,
|
||||||
|
margin_end = 10,
|
||||||
|
css_classes = ["osd", "horizontal", "bottom"]
|
||||||
|
)
|
||||||
|
button = Gtk.Button(
|
||||||
|
icon_name = "media-playback-stop-symbolic",
|
||||||
|
vexpand = False,
|
||||||
|
valign = 3,
|
||||||
|
css_classes = ["error"]
|
||||||
|
)
|
||||||
|
button.connect("clicked", lambda button, model_name=name : dialogs.stop_pull_model(self, model_name))
|
||||||
|
model_row.add_suffix(button)
|
||||||
|
self.pulling_models[name] = {"row": model_row, "progress_bar": progress_bar, "overlay": overlay}
|
||||||
|
overlay.set_child(model_row)
|
||||||
|
overlay.add_overlay(progress_bar)
|
||||||
|
self.pulling_model_list_box.append(overlay)
|
||||||
|
self.create_model_dialog.close()
|
||||||
|
self.manage_models_dialog.present(self)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def override_changed(self, entry):
|
||||||
|
name = entry.get_name()
|
||||||
|
value = entry.get_text()
|
||||||
|
if (not value and name not in local_instance.overrides) or (value and value in local_instance.overrides and local_instance.overrides[name] == value): return
|
||||||
|
if not value: del local_instance.overrides[name]
|
||||||
|
else: local_instance.overrides[name] = value
|
||||||
|
self.save_server_config()
|
||||||
|
if not self.run_remote: local_instance.reset()
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def link_button_handler(self, button):
|
||||||
|
webbrowser.open(button.get_name())
|
||||||
|
|
||||||
|
def check_alphanumeric(self, editable, text, length, position):
|
||||||
|
new_text = ''.join([char for char in text if char.isalnum() or char in ['-', '_']])
|
||||||
|
if new_text != text: editable.stop_emission_by_name("insert-text")
|
||||||
|
|
||||||
|
def create_model(self, model:str, file:bool):
|
||||||
|
name = ""
|
||||||
|
system = ""
|
||||||
|
template = ""
|
||||||
|
if not file:
|
||||||
|
response = connection_handler.simple_post(f"{connection_handler.url}/api/show", json.dumps({"name": model}))
|
||||||
|
if 'text' in response:
|
||||||
|
data = json.loads(response['text'])
|
||||||
|
|
||||||
|
for line in data['modelfile'].split('\n'):
|
||||||
|
if line.startswith('SYSTEM'):
|
||||||
|
system = line[len('SYSTEM'):].strip()
|
||||||
|
elif line.startswith('TEMPLATE'):
|
||||||
|
template = line[len('TEMPLATE'):].strip()
|
||||||
|
self.create_model_template.set_sensitive(False)
|
||||||
|
name = model.split(':')[0]
|
||||||
|
else:
|
||||||
|
self.create_model_template.set_sensitive(True)
|
||||||
|
template = '"""{{ if .System }}<|start_header_id|>system<|end_header_id|>\n\n{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>\n{{ .Response }}<|eot_id|>"""'
|
||||||
|
name = model.split("/")[-1].split(".")[0]
|
||||||
|
self.create_model_base.set_subtitle(model)
|
||||||
|
self.create_model_name.set_text(name)
|
||||||
|
self.create_model_system.set_text(system)
|
||||||
|
self.create_model_template.set_text(template)
|
||||||
|
self.manage_models_dialog.close()
|
||||||
|
self.create_model_dialog.present(self)
|
||||||
|
|
||||||
|
|
||||||
def show_toast(self, message_type:str, message_id:int, overlay):
|
def show_toast(self, message_type:str, message_id:int, overlay):
|
||||||
if message_type not in self.toast_messages or message_id > len(self.toast_messages[message_type] or message_id < 0):
|
if message_type not in self.toast_messages or message_id > len(self.toast_messages[message_type] or message_id < 0):
|
||||||
message_type = "error"
|
message_type = "error"
|
||||||
@@ -274,13 +398,49 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.get_application().send_notification(None, notification)
|
self.get_application().send_notification(None, notification)
|
||||||
|
|
||||||
def delete_message(self, message_element):
|
def delete_message(self, message_element):
|
||||||
message_index = self.current_chat_elements.index(message_element)
|
id = message_element.get_name()
|
||||||
del self.chats["chats"][self.chats["selected_chat"]]["messages"][message_index]
|
del self.chats["chats"][self.chats["selected_chat"]]["messages"][id]
|
||||||
self.chat_container.remove(message_element)
|
self.chat_container.remove(message_element)
|
||||||
del self.current_chat_elements[message_index]
|
if os.path.exists(os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id)):
|
||||||
|
print("deleting " + id)
|
||||||
|
shutil.rmtree(os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id))
|
||||||
self.save_history()
|
self.save_history()
|
||||||
|
|
||||||
def show_message(self, msg:str, bot:bool, footer:str=None, image_base64:str=None):
|
def copy_message(self, message_element):
|
||||||
|
id = message_element.get_name()
|
||||||
|
clipboard = Gdk.Display().get_default().get_clipboard()
|
||||||
|
clipboard.set(self.chats["chats"][self.chats["selected_chat"]]["messages"][id]["content"])
|
||||||
|
self.show_toast("info", 5, self.main_overlay)
|
||||||
|
|
||||||
|
def preview_file(self, file_path, file_type):
|
||||||
|
content = self.get_content_of_file(file_path, file_type)
|
||||||
|
buffer = self.file_preview_text_view.get_buffer()
|
||||||
|
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
|
||||||
|
buffer.insert(buffer.get_start_iter(), content, len(content))
|
||||||
|
self.file_preview_dialog.set_title(os.path.basename(file_path))
|
||||||
|
self.file_preview_dialog.present(self)
|
||||||
|
|
||||||
|
def convert_history_to_ollama(self):
|
||||||
|
messages = []
|
||||||
|
for id, message in self.chats["chats"][self.chats["selected_chat"]]["messages"].items():
|
||||||
|
new_message = message.copy()
|
||||||
|
if 'files' in message and len(message['files']) > 0:
|
||||||
|
del new_message['files']
|
||||||
|
new_message['content'] = ''
|
||||||
|
for name, file_type in message['files'].items():
|
||||||
|
file_path = os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id, name)
|
||||||
|
new_message['content'] += f"```[{name}]\n{self.get_content_of_file(file_path, file_type)}\n```"
|
||||||
|
new_message['content'] += message['content']
|
||||||
|
if 'images' in message and len(message['images']) > 0:
|
||||||
|
new_message['images'] = []
|
||||||
|
for name in message['images']:
|
||||||
|
file_path = os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id, name)
|
||||||
|
new_message['images'].append(self.get_content_of_file(file_path, 'image'))
|
||||||
|
messages.append(new_message)
|
||||||
|
return messages
|
||||||
|
|
||||||
|
|
||||||
|
def show_message(self, msg:str, bot:bool, footer:str=None, images:list=None, files:dict=None, id:str=None):
|
||||||
message_text = Gtk.TextView(
|
message_text = Gtk.TextView(
|
||||||
editable=False,
|
editable=False,
|
||||||
focusable=True,
|
focusable=True,
|
||||||
@@ -299,10 +459,20 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
delete_button = Gtk.Button(
|
delete_button = Gtk.Button(
|
||||||
icon_name = "user-trash-symbolic",
|
icon_name = "user-trash-symbolic",
|
||||||
css_classes = ["flat", "circular", "delete-message-button"],
|
css_classes = ["flat", "circular", "delete-message-button"],
|
||||||
valign="end",
|
|
||||||
halign="end",
|
)
|
||||||
|
copy_button = Gtk.Button(
|
||||||
|
icon_name = "edit-copy-symbolic",
|
||||||
|
css_classes = ["flat", "circular", "delete-message-button"],
|
||||||
|
)
|
||||||
|
|
||||||
|
button_container = Gtk.Box(
|
||||||
|
orientation=0,
|
||||||
|
spacing=6,
|
||||||
|
margin_end=6,
|
||||||
margin_bottom=6,
|
margin_bottom=6,
|
||||||
margin_end=6
|
valign="end",
|
||||||
|
halign="end"
|
||||||
)
|
)
|
||||||
|
|
||||||
message_box = Gtk.Box(
|
message_box = Gtk.Box(
|
||||||
@@ -313,31 +483,74 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
)
|
)
|
||||||
message_text.set_valign(Gtk.Align.CENTER)
|
message_text.set_valign(Gtk.Align.CENTER)
|
||||||
|
|
||||||
if image_base64 is not None:
|
if images and len(images) > 0:
|
||||||
image_data = base64.b64decode(image_base64)
|
image_container = Gtk.Box(
|
||||||
loader = GdkPixbuf.PixbufLoader.new()
|
orientation=0,
|
||||||
loader.write(image_data)
|
spacing=12
|
||||||
loader.close()
|
)
|
||||||
|
image_scroller = Gtk.ScrolledWindow(
|
||||||
|
margin_top=10,
|
||||||
|
margin_start=10,
|
||||||
|
margin_end=10,
|
||||||
|
hexpand=True,
|
||||||
|
height_request = 240,
|
||||||
|
child=image_container
|
||||||
|
)
|
||||||
|
for image in images:
|
||||||
|
image_data = base64.b64decode(self.get_content_of_file(os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id, image), "image"))
|
||||||
|
loader = GdkPixbuf.PixbufLoader.new()
|
||||||
|
loader.write(image_data)
|
||||||
|
loader.close()
|
||||||
|
pixbuf = loader.get_pixbuf()
|
||||||
|
texture = Gdk.Texture.new_for_pixbuf(pixbuf)
|
||||||
|
image = Gtk.Image.new_from_paintable(texture)
|
||||||
|
image.set_size_request(240, 240)
|
||||||
|
image.set_hexpand(False)
|
||||||
|
image.set_css_classes(["flat"])
|
||||||
|
image_container.append(image)
|
||||||
|
message_box.append(image_scroller)
|
||||||
|
|
||||||
pixbuf = loader.get_pixbuf()
|
if files and len(files) > 0:
|
||||||
texture = Gdk.Texture.new_for_pixbuf(pixbuf)
|
file_container = Gtk.Box(
|
||||||
|
orientation=0,
|
||||||
|
spacing=12
|
||||||
|
)
|
||||||
|
file_scroller = Gtk.ScrolledWindow(
|
||||||
|
margin_top=10,
|
||||||
|
margin_start=10,
|
||||||
|
margin_end=10,
|
||||||
|
hexpand=True,
|
||||||
|
child=file_container
|
||||||
|
)
|
||||||
|
for name, file_type in files.items():
|
||||||
|
shown_name='.'.join(name.split(".")[:-1])[:20] + (name[20:] and '..') + f".{name.split('.')[-1]}"
|
||||||
|
|
||||||
image = Gtk.Image.new_from_paintable(texture)
|
button_content = Adw.ButtonContent(
|
||||||
image.set_size_request(240, 240)
|
label=shown_name,
|
||||||
image.set_margin_top(10)
|
icon_name="document-text-symbolic"
|
||||||
image.set_margin_start(10)
|
)
|
||||||
image.set_margin_end(10)
|
button = Gtk.Button(
|
||||||
image.set_hexpand(False)
|
vexpand=False,
|
||||||
image.set_css_classes(["flat"])
|
valign=3,
|
||||||
message_box.append(image)
|
name=name,
|
||||||
|
css_classes=["flat"],
|
||||||
|
tooltip_text=name,
|
||||||
|
child=button_content
|
||||||
|
)
|
||||||
|
button.connect("clicked", lambda button, file_path=os.path.join(self.data_dir, "chats", self.chats['selected_chat'], id, name), file_type=file_type: self.preview_file(file_path, file_type))
|
||||||
|
file_container.append(button)
|
||||||
|
message_box.append(file_scroller)
|
||||||
|
|
||||||
message_box.append(message_text)
|
message_box.append(message_text)
|
||||||
self.current_chat_elements.append(Gtk.Overlay(css_classes=["message"]))
|
overlay = Gtk.Overlay(css_classes=["message"], name=id)
|
||||||
self.current_chat_elements[-1].set_child(message_box)
|
overlay.set_child(message_box)
|
||||||
|
|
||||||
delete_button.connect("clicked", lambda button, element=self.current_chat_elements[-1]: self.delete_message(element))
|
delete_button.connect("clicked", lambda button, element=overlay: self.delete_message(element))
|
||||||
self.current_chat_elements[-1].add_overlay(delete_button)
|
copy_button.connect("clicked", lambda button, element=overlay: self.copy_message(element))
|
||||||
self.chat_container.append(self.current_chat_elements[-1])
|
button_container.append(delete_button)
|
||||||
|
button_container.append(copy_button)
|
||||||
|
overlay.add_overlay(button_container)
|
||||||
|
self.chat_container.append(overlay)
|
||||||
|
|
||||||
if bot:
|
if bot:
|
||||||
self.bot_message = message_buffer
|
self.bot_message = message_buffer
|
||||||
@@ -346,7 +559,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
def update_list_local_models(self):
|
def update_list_local_models(self):
|
||||||
self.local_models = []
|
self.local_models = []
|
||||||
response = connection_handler.simple_get(connection_handler.url + "/api/tags")
|
response = connection_handler.simple_get(f"{connection_handler.url}/api/tags")
|
||||||
for i in range(self.model_string_list.get_n_items() -1, -1, -1):
|
for i in range(self.model_string_list.get_n_items() -1, -1, -1):
|
||||||
self.model_string_list.remove(i)
|
self.model_string_list.remove(i)
|
||||||
if response['status'] == 'ok':
|
if response['status'] == 'ok':
|
||||||
@@ -378,12 +591,15 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
else:
|
else:
|
||||||
self.connection_error()
|
self.connection_error()
|
||||||
|
|
||||||
|
def save_server_config(self):
|
||||||
|
with open(os.path.join(self.config_dir, "server.json"), "w+") as f:
|
||||||
|
json.dump({'remote_url': self.remote_url, '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)
|
||||||
|
|
||||||
def verify_connection(self):
|
def verify_connection(self):
|
||||||
response = connection_handler.simple_get(connection_handler.url)
|
response = connection_handler.simple_get(connection_handler.url)
|
||||||
if response['status'] == 'ok':
|
if response['status'] == 'ok':
|
||||||
if "Ollama is running" in response['text']:
|
if "Ollama is running" in response['text']:
|
||||||
with open(os.path.join(self.config_dir, "server.json"), "w+") as f:
|
self.save_server_config()
|
||||||
json.dump({'remote_url': self.remote_url, 'run_remote': self.run_remote, 'local_port': local_instance.port, 'run_on_background': self.run_on_background}, f)
|
|
||||||
self.update_list_local_models()
|
self.update_list_local_models()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -501,12 +717,12 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
clipboard.set(text)
|
clipboard.set(text)
|
||||||
self.show_toast("info", 4, self.main_overlay)
|
self.show_toast("info", 4, self.main_overlay)
|
||||||
|
|
||||||
def update_bot_message(self, data):
|
def update_bot_message(self, data, id):
|
||||||
if self.bot_message is None:
|
if self.bot_message is None:
|
||||||
self.save_history()
|
self.save_history()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
vadjustment = self.chat_window.get_vadjustment()
|
vadjustment = self.chat_window.get_vadjustment()
|
||||||
if self.chats["chats"][self.chats["selected_chat"]]["messages"][-1]['role'] == "user" or vadjustment.get_value() + 50 >= vadjustment.get_upper() - vadjustment.get_page_size():
|
if (id in self.chats["chats"][self.chats["selected_chat"]]["messages"] and self.chats["chats"][self.chats["selected_chat"]]["messages"][id]['role'] == "user") 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_datetime = datetime.now().strftime("%Y/%m/%d %H:%M")
|
formated_datetime = datetime.now().strftime("%Y/%m/%d %H:%M")
|
||||||
@@ -514,33 +730,34 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
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))
|
||||||
self.save_history()
|
self.save_history()
|
||||||
else:
|
else:
|
||||||
if self.chats["chats"][self.chats["selected_chat"]]["messages"][-1]['role'] == "user":
|
if id not in self.chats["chats"][self.chats["selected_chat"]]["messages"]:
|
||||||
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"].append({
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id] = {
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"model": data['model'],
|
"model": data['model'],
|
||||||
"date": datetime.now().strftime("%Y/%m/%d %H:%M"),
|
"date": datetime.now().strftime("%Y/%m/%d %H:%M"),
|
||||||
"content": ''
|
"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"][-1]['content'] += data['message']['content']
|
self.chats["chats"][self.chats["selected_chat"]]["messages"][id]['content'] += data['message']['content']
|
||||||
|
|
||||||
def toggle_ui_sensitive(self, status):
|
def toggle_ui_sensitive(self, status):
|
||||||
for element in [self.chat_list_box, self.export_chat_button, self.import_chat_button, self.add_chat_button]:
|
for element in [self.chat_list_box, self.add_chat_button, self.chats_menu_button]:
|
||||||
element.set_sensitive(status)
|
element.set_sensitive(status)
|
||||||
|
|
||||||
def run_message(self, messages, model):
|
def switch_send_stop_button(self):
|
||||||
response = connection_handler.stream_post(f"{connection_handler.url}/api/chat", data=json.dumps({"model": model, "messages": messages}), callback=self.update_bot_message)
|
self.stop_button.set_visible(self.send_button.get_visible())
|
||||||
|
self.send_button.set_visible(not self.send_button.get_visible())
|
||||||
|
|
||||||
|
def run_message(self, messages, model, 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))
|
||||||
GLib.idle_add(self.add_code_blocks)
|
GLib.idle_add(self.add_code_blocks)
|
||||||
GLib.idle_add(self.send_button.set_css_classes, ["suggested-action"])
|
GLib.idle_add(self.switch_send_stop_button)
|
||||||
GLib.idle_add(self.send_button.get_child().set_label, "Send")
|
|
||||||
GLib.idle_add(self.send_button.get_child().set_icon_name, "send-to-symbolic")
|
|
||||||
GLib.idle_add(self.toggle_ui_sensitive, True)
|
GLib.idle_add(self.toggle_ui_sensitive, True)
|
||||||
if self.verify_if_image_can_be_used(): GLib.idle_add(self.image_button.set_sensitive, True)
|
if self.loading_spinner:
|
||||||
GLib.idle_add(self.image_button.set_css_classes, ["circular"])
|
GLib.idle_add(self.chat_container.remove, self.loading_spinner)
|
||||||
GLib.idle_add(self.image_button.get_child().set_icon_name, "image-x-generic-symbolic")
|
self.loading_spinner = None
|
||||||
self.attached_image = {"path": None, "base64": None}
|
|
||||||
if response['status'] == 'error':
|
if response['status'] == 'error':
|
||||||
GLib.idle_add(self.connection_error)
|
GLib.idle_add(self.connection_error)
|
||||||
|
|
||||||
@@ -553,9 +770,14 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
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_process(self, model):
|
def pull_model_process(self, model, modelfile):
|
||||||
data = {"name":model}
|
response = {}
|
||||||
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))
|
if modelfile:
|
||||||
|
data = {"name": model, "modelfile": modelfile}
|
||||||
|
response = connection_handler.stream_post(f"{connection_handler.url}/api/create", data=json.dumps(data), callback=lambda data, model_name=model: self.pull_model_update(data, model_name))
|
||||||
|
else:
|
||||||
|
data = {"name": model}
|
||||||
|
response = connection_handler.stream_post(f"{connection_handler.url}/api/pull", data=json.dumps(data), callback=lambda data, model_name=model: self.pull_model_update(data, model_name))
|
||||||
GLib.idle_add(self.update_list_local_models)
|
GLib.idle_add(self.update_list_local_models)
|
||||||
|
|
||||||
if response['status'] == 'ok':
|
if response['status'] == 'ok':
|
||||||
@@ -583,7 +805,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
model_row = Adw.ActionRow(
|
model_row = Adw.ActionRow(
|
||||||
title = model
|
title = model
|
||||||
)
|
)
|
||||||
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": model})
|
thread = threading.Thread(target=self.pull_model_process, kwargs={"model": model, "modelfile": None})
|
||||||
overlay = Gtk.Overlay()
|
overlay = Gtk.Overlay()
|
||||||
progress_bar = Gtk.ProgressBar(
|
progress_bar = Gtk.ProgressBar(
|
||||||
valign = 2,
|
valign = 2,
|
||||||
@@ -599,7 +821,6 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
css_classes = ["error"]
|
css_classes = ["error"]
|
||||||
)
|
)
|
||||||
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(progress_bar)
|
|
||||||
model_row.add_suffix(button)
|
model_row.add_suffix(button)
|
||||||
self.pulling_models[model] = {"row": model_row, "progress_bar": progress_bar, "overlay": overlay}
|
self.pulling_models[model] = {"row": model_row, "progress_bar": progress_bar, "overlay": overlay}
|
||||||
overlay.set_child(model_row)
|
overlay.set_child(model_row)
|
||||||
@@ -615,7 +836,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
subtitle = "Image recognition" if model_info["image"] else None
|
subtitle = "Image recognition" if model_info["image"] else None
|
||||||
)
|
)
|
||||||
link_button = Gtk.Button(
|
link_button = Gtk.Button(
|
||||||
icon_name = "web-browser-symbolic",
|
icon_name = "globe-symbolic",
|
||||||
vexpand = False,
|
vexpand = False,
|
||||||
valign = 3,
|
valign = 3,
|
||||||
css_classes = ["success"]
|
css_classes = ["success"]
|
||||||
@@ -633,68 +854,47 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.available_model_list_box.append(model)
|
self.available_model_list_box.append(model)
|
||||||
|
|
||||||
def save_history(self):
|
def save_history(self):
|
||||||
with open(os.path.join(self.config_dir, "chats.json"), "w+") as f:
|
with open(os.path.join(self.data_dir, "chats", "chats.json"), "w+") as f:
|
||||||
json.dump(self.chats, f, indent=4)
|
json.dump(self.chats, f, indent=4)
|
||||||
|
|
||||||
def load_history_into_chat(self):
|
def load_history_into_chat(self):
|
||||||
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 message in self.chats['chats'][self.chats["selected_chat"]]['messages']:
|
for key, message in self.chats['chats'][self.chats["selected_chat"]]['messages'].items():
|
||||||
if message['role'] == 'user':
|
if message:
|
||||||
self.show_message(message['content'], False, f"\n\n<small>{message['date']}</small>", message['images'][0] if 'images' in message and len(message['images']) > 0 else None)
|
if message['role'] == 'user':
|
||||||
else:
|
self.show_message(message['content'], False, f"\n\n<small>{message['date']}</small>", message['images'] if 'images' in message else None, message['files'] if 'files' in message else None, id=key)
|
||||||
self.show_message(message['content'], True, f"\n\n<small>{message['model']}\t|\t{message['date']}</small>")
|
else:
|
||||||
self.add_code_blocks()
|
self.show_message(message['content'], True, f"\n\n<small>{message['model']}\t|\t{message['date']}</small>", id=key)
|
||||||
self.bot_message = None
|
self.add_code_blocks()
|
||||||
|
self.bot_message = None
|
||||||
|
|
||||||
def load_history(self):
|
def load_history(self):
|
||||||
if os.path.exists(os.path.join(self.config_dir, "chats.json")):
|
if os.path.exists(os.path.join(self.data_dir, "chats", "chats.json")):
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(self.config_dir, "chats.json"), "r") as f:
|
with open(os.path.join(self.data_dir, "chats", "chats.json"), "r") as f:
|
||||||
self.chats = json.load(f)
|
self.chats = json.load(f)
|
||||||
if "selected_chat" not in self.chats or self.chats["selected_chat"] not in self.chats["chats"]: self.chats["selected_chat"] = list(self.chats["chats"].keys())[0]
|
if "selected_chat" not in self.chats or self.chats["selected_chat"] not in self.chats["chats"]: self.chats["selected_chat"] = list(self.chats["chats"].keys())[0]
|
||||||
if len(list(self.chats["chats"].keys())) == 0: self.chats["chats"][_("New Chat")] = {"messages": []}
|
if len(list(self.chats["chats"].keys())) == 0: self.chats["chats"][_("New Chat")] = {"messages": {}}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.chats = {"chats": {_("New Chat"): {"messages": []}}, "selected_chat": _("New Chat")}
|
self.chats = {"chats": {_("New Chat"): {"messages": {}}}, "selected_chat": _("New Chat")}
|
||||||
self.load_history_into_chat()
|
self.load_history_into_chat()
|
||||||
|
|
||||||
def load_image(self, file_dialog, result):
|
def generate_numbered_name(self, chat_name:str, compare_list:list) -> str:
|
||||||
try: file = file_dialog.open_finish(result)
|
if chat_name in compare_list:
|
||||||
except: return
|
for i in range(len(compare_list)):
|
||||||
try:
|
if "." in chat_name:
|
||||||
self.attached_image["path"] = file.get_path()
|
if f"{'.'.join(chat_name.split('.')[:-1])} {i+1}.{chat_name.split('.')[-1]}" not in compare_list:
|
||||||
with Image.open(self.attached_image["path"]) as img:
|
chat_name = f"{'.'.join(chat_name.split('.')[:-1])} {i+1}.{chat_name.split('.')[-1]}"
|
||||||
width, height = img.size
|
break
|
||||||
max_size = 240
|
|
||||||
if width > height:
|
|
||||||
new_width = max_size
|
|
||||||
new_height = int((max_size / width) * height)
|
|
||||||
else:
|
else:
|
||||||
new_height = max_size
|
if f"{chat_name} {i+1}" not in compare_list:
|
||||||
new_width = int((max_size / height) * width)
|
chat_name = f"{chat_name} {i+1}"
|
||||||
resized_img = img.resize((new_width, new_height), Image.LANCZOS)
|
break
|
||||||
with BytesIO() as output:
|
|
||||||
resized_img.save(output, format="PNG")
|
|
||||||
image_data = output.getvalue()
|
|
||||||
self.attached_image["base64"] = base64.b64encode(image_data).decode("utf-8")
|
|
||||||
|
|
||||||
self.image_button.set_css_classes(["destructive-action", "circular"])
|
|
||||||
self.image_button.get_child().set_icon_name("edit-delete-symbolic")
|
|
||||||
except Exception as e:
|
|
||||||
self.show_toast("error", 5, self.main_overlay)
|
|
||||||
|
|
||||||
def remove_image(self):
|
|
||||||
self.image_button.set_css_classes(["circular"])
|
|
||||||
self.image_button.get_child().set_icon_name("image-x-generic-symbolic")
|
|
||||||
self.attached_image = {"path": None, "base64": None}
|
|
||||||
|
|
||||||
def generate_numbered_chat_name(self, chat_name) -> str:
|
|
||||||
if chat_name in self.chats["chats"]:
|
|
||||||
for i in range(len(list(self.chats["chats"].keys()))):
|
|
||||||
if chat_name + f" {i+1}" not in self.chats["chats"]:
|
|
||||||
chat_name += f" {i+1}"
|
|
||||||
break
|
|
||||||
return chat_name
|
return chat_name
|
||||||
|
|
||||||
|
def generate_uuid(self) -> str:
|
||||||
|
return f"{datetime.today().strftime('%Y%m%d%H%M%S%f')}{uuid.uuid4().hex}"
|
||||||
|
|
||||||
def clear_chat(self):
|
def clear_chat(self):
|
||||||
for widget in list(self.chat_container): self.chat_container.remove(widget)
|
for widget in list(self.chat_container): self.chat_container.remove(widget)
|
||||||
self.chats["chats"][self.chats["selected_chat"]]["messages"] = []
|
self.chats["chats"][self.chats["selected_chat"]]["messages"] = []
|
||||||
@@ -702,22 +902,26 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
def delete_chat(self, chat_name):
|
def delete_chat(self, chat_name):
|
||||||
del self.chats['chats'][chat_name]
|
del self.chats['chats'][chat_name]
|
||||||
|
if os.path.exists(os.path.join(self.data_dir, "chats", self.chats['selected_chat'])):
|
||||||
|
shutil.rmtree(os.path.join(self.data_dir, "chats", self.chats['selected_chat']))
|
||||||
self.save_history()
|
self.save_history()
|
||||||
self.update_chat_list()
|
self.update_chat_list()
|
||||||
if len(self.chats['chats'])==0:
|
if len(self.chats['chats'])==0:
|
||||||
self.new_chat()
|
self.new_chat()
|
||||||
|
|
||||||
def rename_chat(self, old_chat_name, new_chat_name, label_element):
|
def rename_chat(self, old_chat_name, new_chat_name, label_element):
|
||||||
new_chat_name = self.generate_numbered_chat_name(new_chat_name)
|
new_chat_name = self.generate_numbered_name(new_chat_name, self.chats["chats"].keys())
|
||||||
self.chats["chats"][new_chat_name] = self.chats["chats"][old_chat_name]
|
self.chats["chats"][new_chat_name] = self.chats["chats"][old_chat_name]
|
||||||
del self.chats["chats"][old_chat_name]
|
del self.chats["chats"][old_chat_name]
|
||||||
|
if os.path.exists(os.path.join(self.data_dir, "chats", old_chat_name)):
|
||||||
|
shutil.move(os.path.join(self.data_dir, "chats", old_chat_name), os.path.join(self.data_dir, "chats", new_chat_name))
|
||||||
label_element.set_label(new_chat_name)
|
label_element.set_label(new_chat_name)
|
||||||
label_element.get_parent().set_name(new_chat_name)
|
label_element.get_parent().set_name(new_chat_name)
|
||||||
self.save_history()
|
self.save_history()
|
||||||
|
|
||||||
def new_chat(self):
|
def new_chat(self):
|
||||||
chat_name = self.generate_numbered_chat_name(_("New Chat"))
|
chat_name = self.generate_numbered_name(_("New Chat"), self.chats["chats"].keys())
|
||||||
self.chats["chats"][chat_name] = {"messages": []}
|
self.chats["chats"][chat_name] = {"messages": {}}
|
||||||
self.save_history()
|
self.save_history()
|
||||||
self.new_chat_element(chat_name, True)
|
self.new_chat_element(chat_name, True)
|
||||||
|
|
||||||
@@ -726,7 +930,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
del self.pulling_models[model_name]
|
del self.pulling_models[model_name]
|
||||||
|
|
||||||
def delete_model(self, model_name):
|
def delete_model(self, model_name):
|
||||||
response = connection_handler.simple_delete(connection_handler.url + "/api/delete", data={"name": model_name})
|
response = connection_handler.simple_delete(f"{connection_handler.url}/api/delete", data={"name": model_name})
|
||||||
self.update_list_local_models()
|
self.update_list_local_models()
|
||||||
if response['status'] == 'ok':
|
if response['status'] == 'ok':
|
||||||
self.show_toast("good", 0, self.manage_models_overlay)
|
self.show_toast("good", 0, self.manage_models_overlay)
|
||||||
@@ -823,35 +1027,74 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
def on_export_current_chat(self, file_dialog, result):
|
def on_export_current_chat(self, file_dialog, result):
|
||||||
file = file_dialog.save_finish(result)
|
file = file_dialog.save_finish(result)
|
||||||
data_to_export = {self.chats["selected_chat"]: self.chats["chats"][self.chats["selected_chat"]]}
|
if not file: return
|
||||||
file.replace_contents_async(
|
json_data = json.dumps({self.chats["selected_chat"]: self.chats["chats"][self.chats["selected_chat"]]}, indent=4).encode("UTF-8")
|
||||||
json.dumps(data_to_export, indent=4).encode("UTF-8"),
|
|
||||||
etag=None,
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
make_backup=False,
|
json_path = os.path.join(temp_dir, "data.json")
|
||||||
flags=Gio.FileCreateFlags.NONE,
|
with open(json_path, "wb") as json_file:
|
||||||
cancellable=None,
|
json_file.write(json_data)
|
||||||
callback=self.on_replace_contents
|
|
||||||
)
|
tar_path = os.path.join(temp_dir, f"{self.chats['selected_chat']}")
|
||||||
|
with tarfile.open(tar_path, "w") as tar:
|
||||||
|
tar.add(json_path, arcname="data.json")
|
||||||
|
directory = os.path.join(self.data_dir, "chats", self.chats['selected_chat'])
|
||||||
|
if os.path.exists(directory) and os.path.isdir(directory):
|
||||||
|
tar.add(directory, arcname=os.path.basename(directory))
|
||||||
|
|
||||||
|
with open(tar_path, "rb") as tar:
|
||||||
|
tar_content = tar.read()
|
||||||
|
|
||||||
|
file.replace_contents_async(
|
||||||
|
tar_content,
|
||||||
|
etag=None,
|
||||||
|
make_backup=False,
|
||||||
|
flags=Gio.FileCreateFlags.NONE,
|
||||||
|
cancellable=None,
|
||||||
|
callback=self.on_replace_contents
|
||||||
|
)
|
||||||
|
|
||||||
def export_current_chat(self):
|
def export_current_chat(self):
|
||||||
file_dialog = Gtk.FileDialog(initial_name=f"{self.chats['selected_chat']}.json")
|
file_dialog = Gtk.FileDialog(initial_name=f"{self.chats['selected_chat']}.tar")
|
||||||
file_dialog.save(parent=self, cancellable=None, callback=self.on_export_current_chat)
|
file_dialog.save(parent=self, cancellable=None, callback=self.on_export_current_chat)
|
||||||
|
|
||||||
def on_chat_imported(self, file_dialog, result):
|
def on_chat_imported(self, file_dialog, result):
|
||||||
file = file_dialog.open_finish(result)
|
file = file_dialog.open_finish(result)
|
||||||
|
if not file: return
|
||||||
stream = file.read(None)
|
stream = file.read(None)
|
||||||
data_stream = Gio.DataInputStream.new(stream)
|
data_stream = Gio.DataInputStream.new(stream)
|
||||||
data, _ = data_stream.read_until('\0', None)
|
tar_content = data_stream.read_bytes(1024 * 1024, None)
|
||||||
data = json.loads(data)
|
|
||||||
chat_name = list(data.keys())[0]
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
chat_content = data[chat_name]
|
tar_filename = os.path.join(temp_dir, "imported_chat.tar")
|
||||||
self.chats['chats'][chat_name] = chat_content
|
|
||||||
|
with open(tar_filename, "wb") as tar_file:
|
||||||
|
tar_file.write(tar_content.get_data())
|
||||||
|
|
||||||
|
with tarfile.open(tar_filename, "r") as tar:
|
||||||
|
tar.extractall(path=temp_dir)
|
||||||
|
chat_name = None
|
||||||
|
chat_content = None
|
||||||
|
for member in tar.getmembers():
|
||||||
|
if member.name == "data.json":
|
||||||
|
json_filepath = os.path.join(temp_dir, member.name)
|
||||||
|
with open(json_filepath, "r") as json_file:
|
||||||
|
data = json.load(json_file)
|
||||||
|
for chat_name, chat_content in data.items():
|
||||||
|
new_chat_name = self.generate_numbered_name(chat_name, list(self.chats['chats'].keys()))
|
||||||
|
self.chats['chats'][new_chat_name] = chat_content
|
||||||
|
src_path = os.path.join(temp_dir, chat_name)
|
||||||
|
if os.path.exists(src_path) and os.path.isdir(src_path):
|
||||||
|
dest_path = os.path.join(self.data_dir, "chats", new_chat_name)
|
||||||
|
shutil.copytree(src_path, dest_path)
|
||||||
|
|
||||||
|
|
||||||
self.update_chat_list()
|
self.update_chat_list()
|
||||||
self.save_history()
|
self.save_history()
|
||||||
self.show_toast("good", 3, self.main_overlay)
|
self.show_toast("good", 3, self.main_overlay)
|
||||||
|
|
||||||
def import_chat(self):
|
def import_chat(self):
|
||||||
file_dialog = Gtk.FileDialog(default_filter=self.file_filter_json)
|
file_dialog = Gtk.FileDialog(default_filter=self.file_filter_tar)
|
||||||
file_dialog.open(self, None, self.on_chat_imported)
|
file_dialog.open(self, None, self.on_chat_imported)
|
||||||
|
|
||||||
def switch_run_on_background(self):
|
def switch_run_on_background(self):
|
||||||
@@ -859,19 +1102,78 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.set_hide_on_close(self.run_on_background)
|
self.set_hide_on_close(self.run_on_background)
|
||||||
self.verify_connection()
|
self.verify_connection()
|
||||||
|
|
||||||
|
def get_content_of_file(self, file_path, file_type):
|
||||||
|
if file_type == 'image':
|
||||||
|
try:
|
||||||
|
with Image.open(file_path) as img:
|
||||||
|
width, height = img.size
|
||||||
|
max_size = 240
|
||||||
|
if width > height:
|
||||||
|
new_width = max_size
|
||||||
|
new_height = int((max_size / width) * height)
|
||||||
|
else:
|
||||||
|
new_height = max_size
|
||||||
|
new_width = int((max_size / height) * width)
|
||||||
|
resized_img = img.resize((new_width, new_height), Image.LANCZOS)
|
||||||
|
with BytesIO() as output:
|
||||||
|
resized_img.save(output, format="PNG")
|
||||||
|
image_data = output.getvalue()
|
||||||
|
return base64.b64encode(image_data).decode("utf-8")
|
||||||
|
except Exception as e:
|
||||||
|
self.show_toast("error", 5, self.main_overlay)
|
||||||
|
elif file_type == 'plain_text':
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def remove_attached_file(self, button):
|
||||||
|
del self.attachments[button.get_name()]
|
||||||
|
button.get_parent().remove(button)
|
||||||
|
if len(self.attachments) == 0: self.attachment_box.set_visible(False)
|
||||||
|
|
||||||
|
def attach_file(self, file_path, file_type):
|
||||||
|
name = self.generate_numbered_name(os.path.basename(file_path), self.attachments.keys())
|
||||||
|
content = self.get_content_of_file(file_path, file_type)
|
||||||
|
|
||||||
|
shown_name='.'.join(name.split(".")[:-1])[:20] + (name[20:] and '..') + f".{name.split('.')[-1]}"
|
||||||
|
|
||||||
|
button_content = Adw.ButtonContent(
|
||||||
|
label=shown_name,
|
||||||
|
icon_name={"image": "image-x-generic-symbolic", "plain_text": "document-text-symbolic"}[file_type]
|
||||||
|
)
|
||||||
|
button = Gtk.Button(
|
||||||
|
vexpand=True,
|
||||||
|
valign=3,
|
||||||
|
name=name,
|
||||||
|
css_classes=["flat"],
|
||||||
|
tooltip_text=name,
|
||||||
|
child=button_content
|
||||||
|
)
|
||||||
|
|
||||||
|
self.attachments[name] = {"path": file_path, "type": file_type, "content": content, "button": button}
|
||||||
|
button.connect("clicked", lambda button: dialogs.remove_attached_file(self, button))
|
||||||
|
self.attachment_container.append(button)
|
||||||
|
self.attachment_box.set_visible(True)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
GtkSource.init()
|
GtkSource.init()
|
||||||
|
if os.path.exists(os.path.join(self.config_dir, "chats.json")) and not os.path.exists(os.path.join(self.data_dir, "chats", "chats.json")):
|
||||||
|
update_history.update(self)
|
||||||
self.set_help_overlay(self.shortcut_window)
|
self.set_help_overlay(self.shortcut_window)
|
||||||
self.get_application().set_accels_for_action("win.show-help-overlay", ['<primary>slash'])
|
self.get_application().set_accels_for_action("win.show-help-overlay", ['<primary>slash'])
|
||||||
self.get_application().create_action('new_chat', lambda *_: self.new_chat(), ['<primary>n'])
|
self.get_application().create_action('new_chat', lambda *_: self.new_chat(), ['<primary>n'])
|
||||||
self.get_application().create_action('clear', lambda *_: dialogs.clear_chat(self), ['<primary>e'])
|
self.get_application().create_action('clear', lambda *_: dialogs.clear_chat(self), ['<primary>e'])
|
||||||
self.get_application().create_action('send', lambda *_: self.send_message(self), ['Return'])
|
self.get_application().create_action('send', lambda *_: self.send_message(self), ['Return'])
|
||||||
|
self.get_application().create_action('export_current_chat', lambda *_: self.export_current_chat())
|
||||||
|
self.get_application().create_action('import_chat', lambda *_: self.import_chat())
|
||||||
|
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('attach_image', lambda *_: dialogs.attach_file(self, self.file_filter_image, "image"))
|
||||||
|
self.get_application().create_action('attach_plain_text', lambda *_: dialogs.attach_file(self, self.file_filter_text, "plain_text"))
|
||||||
self.add_chat_button.connect("clicked", lambda button : self.new_chat())
|
self.add_chat_button.connect("clicked", lambda button : self.new_chat())
|
||||||
|
|
||||||
self.export_chat_button.connect("clicked", lambda button : self.export_current_chat())
|
self.create_model_name.get_delegate().connect("insert-text", self.check_alphanumeric)
|
||||||
self.import_chat_button.connect("clicked", lambda button : self.import_chat())
|
|
||||||
|
|
||||||
self.remote_connection_entry.connect("entry-activated", lambda entry : entry.set_css_classes([]))
|
self.remote_connection_entry.connect("entry-activated", lambda entry : entry.set_css_classes([]))
|
||||||
self.remote_connection_switch.connect("notify", lambda pspec, user_data : self.connection_switched())
|
self.remote_connection_switch.connect("notify", lambda pspec, user_data : self.connection_switched())
|
||||||
self.background_switch.connect("notify", lambda pspec, user_data : self.switch_run_on_background())
|
self.background_switch.connect("notify", lambda pspec, user_data : self.switch_run_on_background())
|
||||||
@@ -882,6 +1184,22 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
local_instance.port = data['local_port']
|
local_instance.port = data['local_port']
|
||||||
self.remote_url = data['remote_url']
|
self.remote_url = data['remote_url']
|
||||||
self.run_on_background = data['run_on_background']
|
self.run_on_background = data['run_on_background']
|
||||||
|
#Model Tweaks
|
||||||
|
if "model_tweaks" in data: self.model_tweaks = data['model_tweaks']
|
||||||
|
self.temperature_spin.set_value(self.model_tweaks['temperature'])
|
||||||
|
self.seed_spin.set_value(self.model_tweaks['seed'])
|
||||||
|
self.keep_alive_spin.set_value(self.model_tweaks['keep_alive'])
|
||||||
|
#Overrides
|
||||||
|
if "ollama_overrides" in data: local_instance.overrides = data['ollama_overrides']
|
||||||
|
for element in [
|
||||||
|
self.override_HSA_OVERRIDE_GFX_VERSION,
|
||||||
|
self.override_CUDA_VISIBLE_DEVICES,
|
||||||
|
self.override_HIP_VISIBLE_DEVICES]:
|
||||||
|
override = element.get_name()
|
||||||
|
if override in local_instance.overrides:
|
||||||
|
element.set_text(local_instance.overrides[override])
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -896,7 +1214,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
local_instance.start()
|
local_instance.start()
|
||||||
connection_handler.url = f"http://127.0.0.1:{local_instance.port}"
|
connection_handler.url = f"http://127.0.0.1:{local_instance.port}"
|
||||||
self.welcome_dialog.present(self)
|
self.welcome_dialog.present(self)
|
||||||
if self.verify_connection() is False and self.run_remote == False: self.connection_error()
|
if self.verify_connection() is False: self.connection_error()
|
||||||
self.update_list_available_models()
|
self.update_list_available_models()
|
||||||
self.load_history()
|
self.load_history()
|
||||||
self.update_chat_list()
|
self.update_chat_list()
|
||||||
|
|||||||
534
src/window.ui
@@ -24,7 +24,9 @@
|
|||||||
<setter object="split_view_overlay" property="collapsed">true</setter>
|
<setter object="split_view_overlay" property="collapsed">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>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<property name="content">
|
<property name="content">
|
||||||
@@ -44,21 +46,11 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="end">
|
<child type="end">
|
||||||
<object class="GtkButton" id="import_chat_button">
|
<object class="GtkMenuButton" id="chats_menu_button">
|
||||||
<property name="tooltip-text" translatable="yes">Import chat</property>
|
<property name="direction">1</property>
|
||||||
<property name="icon-name">document-open-symbolic</property>
|
<property name="halign">3</property>
|
||||||
<style>
|
<property name="menu-model">chats_menu</property>
|
||||||
<class name="flat"/>
|
<property name="icon-name">view-more-symbolic</property>
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="end">
|
|
||||||
<object class="GtkButton" id="export_chat_button">
|
|
||||||
<property name="tooltip-text" translatable="yes">Export chat</property>
|
|
||||||
<property name="icon-name">folder-download-symbolic</property>
|
|
||||||
<style>
|
|
||||||
<class name="flat"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -113,7 +105,7 @@
|
|||||||
<property name="tooltip-text" translatable="yes">Manage models</property>
|
<property name="tooltip-text" translatable="yes">Manage models</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwButtonContent">
|
<object class="AdwButtonContent">
|
||||||
<property name="icon-name">package-x-generic-symbolic</property>
|
<property name="icon-name">library-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -172,75 +164,115 @@
|
|||||||
<object class="AdwClamp">
|
<object class="AdwClamp">
|
||||||
<property name="maximum-size">1000</property>
|
<property name="maximum-size">1000</property>
|
||||||
<property name="tightening-threshold">800</property>
|
<property name="tightening-threshold">800</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="orientation">0</property>
|
<property name="orientation">1</property>
|
||||||
<property name="spacing">12</property>
|
<property name="spacing">12</property>
|
||||||
<property name="margin-top">12</property>
|
<property name="margin-top">12</property>
|
||||||
<property name="margin-bottom">12</property>
|
<property name="margin-bottom">12</property>
|
||||||
<property name="margin-start">12</property>
|
<property name="margin-start">12</property>
|
||||||
<property name="margin-end">12</property>
|
<property name="margin-end">12</property>
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="image_button">
|
|
||||||
<signal name="clicked" handler="open_image"/>
|
|
||||||
<property name="vexpand">false</property>
|
|
||||||
<property name="valign">3</property>
|
|
||||||
<property name="sensitive">false</property>
|
|
||||||
<property name="tooltip-text" translatable="yes">Only available on selected models</property>
|
|
||||||
<child>
|
|
||||||
<object class="AdwButtonContent">
|
|
||||||
<property name="icon-name">image-x-generic-symbolic</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<style>
|
|
||||||
<class name="card"/>
|
|
||||||
<class name="message-input"/>
|
|
||||||
</style>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow">
|
|
||||||
<property name="margin-start">12</property>
|
|
||||||
<property name="margin-end">12</property>
|
|
||||||
<style>
|
|
||||||
<class name="message-input"/>
|
|
||||||
<class name="undershoot-bottom"/>
|
|
||||||
<class name="undershoot-top"/>
|
|
||||||
</style>
|
|
||||||
<child>
|
|
||||||
<object class="GtkTextView" id="message_text_view">
|
|
||||||
<property name="wrap-mode">word</property>
|
|
||||||
<property name="top-margin">6</property>
|
|
||||||
<property name="bottom-margin">6</property>
|
|
||||||
<property name="hexpand">true</property>
|
|
||||||
<style>
|
|
||||||
<class name="message-input"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="send_button">
|
<object class="GtkScrolledWindow" id="attachment_box">
|
||||||
<signal name="clicked" handler="send_message"/>
|
<property name="visible">false</property>
|
||||||
<property name="vexpand">false</property>
|
|
||||||
<property name="valign">3</property>
|
|
||||||
<style>
|
|
||||||
<class name="suggested-action"/>
|
|
||||||
<class name="circular"/>
|
|
||||||
</style>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwButtonContent">
|
<object class="GtkBox" id="attachment_container">
|
||||||
<property name="icon-name">send-to-symbolic</property>
|
<property name="orientation">0</property>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="orientation">0</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuButton" id="attachment_button">
|
||||||
|
<property name="menu-model">attachment_menu</property>
|
||||||
|
<property name="direction">0</property>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="valign">3</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Attach file</property>
|
||||||
|
<style>
|
||||||
|
<class name="circular"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="AdwButtonContent">
|
||||||
|
<property name="icon-name">chain-link-loose-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<style>
|
||||||
|
<class name="card"/>
|
||||||
|
<class name="message-input"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="margin-start">12</property>
|
||||||
|
<property name="margin-end">12</property>
|
||||||
|
<style>
|
||||||
|
<class name="message-input"/>
|
||||||
|
<class name="undershoot-bottom"/>
|
||||||
|
<class name="undershoot-top"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="message_text_view">
|
||||||
|
<property name="wrap-mode">word</property>
|
||||||
|
<property name="top-margin">6</property>
|
||||||
|
<property name="bottom-margin">6</property>
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<style>
|
||||||
|
<class name="message-input"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="send_button">
|
||||||
|
<signal name="clicked" handler="send_message"/>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="valign">3</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Send message</property>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
<class name="circular"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="AdwButtonContent">
|
||||||
|
<property name="icon-name">paper-plane-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="stop_button">
|
||||||
|
<signal name="clicked" handler="stop_message"/>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="valign">3</property>
|
||||||
|
<property name="visible">false</property>
|
||||||
|
<style>
|
||||||
|
<class name="destructive-action"/>
|
||||||
|
<class name="circular"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="AdwButtonContent">
|
||||||
|
<property name="icon-name">media-playback-stop-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@@ -260,7 +292,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesPage" id="general_page">
|
<object class="AdwPreferencesPage" id="general_page">
|
||||||
<property name="title" translatable="yes">General</property>
|
<property name="title" translatable="yes">General</property>
|
||||||
<property name="icon-name">insert-link-symbolic</property>
|
<property name="icon-name">preferences-system-symbolic</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesGroup">
|
<object class="AdwPreferencesGroup">
|
||||||
<property name="title" translatable="yes">Remote Connection</property>
|
<property name="title" translatable="yes">Remote Connection</property>
|
||||||
@@ -292,18 +324,257 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<!--<child>
|
<child>
|
||||||
<object class="AdwPreferencesPage" id="model_page">
|
<object class="AdwPreferencesPage" id="model_page">
|
||||||
<property name="title" translatable="yes">Advanced Model Settings</property>
|
<property name="title" translatable="yes">Model</property>
|
||||||
<property name="icon-name">preferences-system-symbolic</property>
|
<property name="icon-name">preferences-other-symbolic</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesGroup">
|
<object class="AdwPreferencesGroup">
|
||||||
<property name="title" translatable="yes">Model Tweaks</property>
|
<property name="title" translatable="yes">Model Tweaks</property>
|
||||||
<property name="description" translatable="yes">Manage the behavior of the AI models</property>
|
<property name="description" translatable="yes">Manage the behavior of the AI models</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwSpinRow" id="temperature_spin">
|
||||||
|
<signal name="changed" handler="model_spin_changed"/>
|
||||||
|
<property name="name">temperature</property>
|
||||||
|
<property name="title" translatable="yes">Temperature</property>
|
||||||
|
<property name="subtitle" translatable="yes">The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)</property>
|
||||||
|
<property name="digits">1</property>
|
||||||
|
<property name="adjustment">
|
||||||
|
<object class="GtkAdjustment">
|
||||||
|
<property name="lower">0</property>
|
||||||
|
<property name="upper">1</property>
|
||||||
|
<property name="step-increment">0.1</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwSpinRow" id="seed_spin">
|
||||||
|
<signal name="changed" handler="model_spin_changed"/>
|
||||||
|
<property name="name">seed</property>
|
||||||
|
<property name="title" translatable="yes">Seed</property>
|
||||||
|
<property name="subtitle" translatable="yes">Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0 (random))</property>
|
||||||
|
<property name="adjustment">
|
||||||
|
<object class="GtkAdjustment">
|
||||||
|
<property name="lower">0</property>
|
||||||
|
<property name="upper">999999</property>
|
||||||
|
<property name="step-increment">1</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwSpinRow" id="keep_alive_spin">
|
||||||
|
<signal name="changed" handler="model_spin_changed"/>
|
||||||
|
<property name="name">keep_alive</property>
|
||||||
|
<property name="title" translatable="yes">Keep Alive Time</property>
|
||||||
|
<property name="subtitle" translatable="yes">Controls how long the model will stay loaded into memory following the request in minutes (default: 5)</property>
|
||||||
|
<property name="adjustment">
|
||||||
|
<object class="GtkAdjustment">
|
||||||
|
<property name="lower">-1</property>
|
||||||
|
<property name="upper">999999</property>
|
||||||
|
<property name="step-increment">1</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>-->
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesPage" id="instance_page">
|
||||||
|
<property name="title" translatable="yes">Ollama Instance</property>
|
||||||
|
<property name="icon-name">brain-augemnted-symbolic</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<property name="title" translatable="yes">Ollama Overrides</property>
|
||||||
|
<property name="description" translatable="yes">Manage the arguments used on Ollama, any changes on this page only applies to the integrated instance, the instance will restart if you make changes</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="override_HSA_OVERRIDE_GFX_VERSION">
|
||||||
|
<signal name="apply" handler="override_changed"/>
|
||||||
|
<property name="name">HSA_OVERRIDE_GFX_VERSION</property>
|
||||||
|
<property name="title" translatable="no">HSA_OVERRIDE_GFX_VERSION</property>
|
||||||
|
<property name="show-apply-button">true</property>
|
||||||
|
<child type="prefix">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<signal name="clicked" handler="link_button_handler"/>
|
||||||
|
<property name="name">https://github.com/ollama/ollama/blob/main/docs/gpu.md#overrides</property>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="valign">3</property>
|
||||||
|
<property name="icon-name">globe-symbolic</property>
|
||||||
|
<style>
|
||||||
|
<class name="success"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="override_CUDA_VISIBLE_DEVICES">
|
||||||
|
<signal name="apply" handler="override_changed"/>
|
||||||
|
<property name="name">CUDA_VISIBLE_DEVICES</property>
|
||||||
|
<property name="title" translatable="no">CUDA_VISIBLE_DEVICES</property>
|
||||||
|
<property name="show-apply-button">true</property>
|
||||||
|
<child type="prefix">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<signal name="clicked" handler="link_button_handler"/>
|
||||||
|
<property name="name">https://github.com/ollama/ollama/blob/main/docs/gpu.md#gpu-selection</property>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="valign">3</property>
|
||||||
|
<property name="icon-name">globe-symbolic</property>
|
||||||
|
<style>
|
||||||
|
<class name="success"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="override_HIP_VISIBLE_DEVICES">
|
||||||
|
<signal name="apply" handler="override_changed"/>
|
||||||
|
<property name="name">HIP_VISIBLE_DEVICES</property>
|
||||||
|
<property name="title" translatable="no">HIP_VISIBLE_DEVICES</property>
|
||||||
|
<property name="show-apply-button">true</property>
|
||||||
|
<child type="prefix">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<signal name="clicked" handler="link_button_handler"/>
|
||||||
|
<property name="name">https://github.com/ollama/ollama/blob/main/docs/gpu.md#gpu-selection-1</property>
|
||||||
|
<property name="vexpand">false</property>
|
||||||
|
<property name="valign">3</property>
|
||||||
|
<property name="icon-name">globe-symbolic</property>
|
||||||
|
<style>
|
||||||
|
<class name="success"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</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"></property>
|
||||||
|
<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>
|
||||||
|
|
||||||
<object class="AdwDialog" id="manage_models_dialog">
|
<object class="AdwDialog" id="manage_models_dialog">
|
||||||
@@ -316,6 +587,14 @@
|
|||||||
<object class="AdwToolbarView">
|
<object class="AdwToolbarView">
|
||||||
<child type="top">
|
<child type="top">
|
||||||
<object class="AdwHeaderBar">
|
<object class="AdwHeaderBar">
|
||||||
|
<child type="start">
|
||||||
|
<object class="GtkMenuButton">
|
||||||
|
<property name="primary">True</property>
|
||||||
|
<property name="icon-name">list-add-symbolic</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Create model</property>
|
||||||
|
<property name="menu-model">create_model_menu</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<property name="title-widget">
|
<property name="title-widget">
|
||||||
<object class="AdwWindowTitle">
|
<object class="AdwWindowTitle">
|
||||||
<property name="title" translatable="yes">Manage models</property>
|
<property name="title" translatable="yes">Manage models</property>
|
||||||
@@ -372,6 +651,37 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
|
<object class="AdwDialog" id="file_preview_dialog">
|
||||||
|
<property name="can-close">true</property>
|
||||||
|
<property name="width-request">450</property>
|
||||||
|
<property name="height-request">450</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwToolbarView">
|
||||||
|
<child type="top">
|
||||||
|
<object class="AdwHeaderBar">
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="margin-bottom">12</property>
|
||||||
|
<property name="margin-start">12</property>
|
||||||
|
<property name="margin-end">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="file_preview_text_view">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<property name="editable">false</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
|
||||||
<object class="AdwDialog" id="welcome_dialog">
|
<object class="AdwDialog" id="welcome_dialog">
|
||||||
<property name="can-close">false</property>
|
<property name="can-close">false</property>
|
||||||
<property name="width-request">450</property>
|
<property name="width-request">450</property>
|
||||||
@@ -569,13 +879,56 @@
|
|||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
</menu>
|
</menu>
|
||||||
<menu id="chat_context_menu">
|
<menu id="chats_menu">
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Remove</attribute>
|
<attribute name="label" translatable="yes">Export current chat</attribute>
|
||||||
|
<attribute name="action">app.export_current_chat</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Import chat</attribute>
|
||||||
|
<attribute name="action">app.import_chat</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
</menu>
|
</menu>
|
||||||
|
<menu id="create_model_menu">
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">From existing model</attribute>
|
||||||
|
<attribute name="action">app.create_model_from_existing</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">From GGUF file (Testing)</attribute>
|
||||||
|
<attribute name="action">app.create_model_from_file</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</menu>
|
||||||
|
<menu id="attachment_menu">
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Plain text file</attribute>
|
||||||
|
<attribute name="action">app.attach_plain_text</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Image</attribute>
|
||||||
|
<attribute name="action">app.attach_image</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</menu>
|
||||||
|
<object class="GtkFileFilter" id="file_filter_text">
|
||||||
|
<suffixes>
|
||||||
|
<suffix></suffix>
|
||||||
|
<suffix>txt</suffix>
|
||||||
|
<suffix>md</suffix>
|
||||||
|
<suffix>html</suffix>
|
||||||
|
<suffix>css</suffix>
|
||||||
|
<suffix>js</suffix>
|
||||||
|
<suffix>py</suffix>
|
||||||
|
<suffix>java</suffix>
|
||||||
|
<suffix>json</suffix>
|
||||||
|
<suffix>xml</suffix>
|
||||||
|
</suffixes>
|
||||||
|
</object>
|
||||||
<object class="GtkFileFilter" id="file_filter_image">
|
<object class="GtkFileFilter" id="file_filter_image">
|
||||||
<mime-types>
|
<mime-types>
|
||||||
<mime-type>image/svg+xml</mime-type>
|
<mime-type>image/svg+xml</mime-type>
|
||||||
@@ -585,11 +938,16 @@
|
|||||||
<mime-type>image/gif</mime-type>
|
<mime-type>image/gif</mime-type>
|
||||||
</mime-types>
|
</mime-types>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkFileFilter" id="file_filter_json">
|
<object class="GtkFileFilter" id="file_filter_tar">
|
||||||
<mime-types>
|
<mime-types>
|
||||||
<mime-type>application/json</mime-type>
|
<mime-type>application/x-tar</mime-type>
|
||||||
</mime-types>
|
</mime-types>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkFileFilter" id="file_filter_gguf">
|
||||||
|
<suffixes>
|
||||||
|
<suffix>gguf</suffix>
|
||||||
|
</suffixes>
|
||||||
|
</object>
|
||||||
<object class="GtkShortcutsWindow" id="shortcut_window">
|
<object class="GtkShortcutsWindow" id="shortcut_window">
|
||||||
<property name="modal">1</property>
|
<property name="modal">1</property>
|
||||||
<child>
|
<child>
|
||||||
@@ -664,5 +1022,3 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||