Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13d1572dd5 | ||
|
|
f2fa417194 | ||
|
|
4bf64c98e0 | ||
|
|
6fb36b1cc4 | ||
|
|
89b600f964 | ||
|
|
91c54a4565 | ||
|
|
c3b105c30b | ||
|
|
7c4c1e0997 | ||
|
|
f50c98befc | ||
|
|
98a0f60be9 | ||
|
|
d9e6b08fd7 | ||
|
|
d36f6b6644 | ||
|
|
e67d0bea83 | ||
|
|
218c10f4ad | ||
|
|
134a907eff | ||
|
|
97ee2e7a24 | ||
|
|
ed62aed6a4 | ||
|
|
b6ab989ac8 | ||
|
|
cefd758846 | ||
|
|
c114ae67ba | ||
|
|
0a7f7e5ac2 | ||
|
|
22db4a43d9 | ||
|
|
70e4d8f407 | ||
|
|
3da5207f53 | ||
|
|
f00122d789 | ||
|
|
6a19ca266e | ||
|
|
4f9aebf7a3 | ||
|
|
61f9e187bd | ||
|
|
27126736a4 | ||
|
|
c9cf2bfefc | ||
|
|
e03ea42be3 | ||
|
|
3253e67680 | ||
|
|
e189769f3f | ||
|
|
f6637493db | ||
|
|
063da38597 | ||
|
|
7587b03828 | ||
|
|
8fffb64f79 | ||
|
|
735eae0d0e | ||
|
|
8c98be6ef6 | ||
|
|
115e22e52c | ||
|
|
792a81ad03 | ||
|
|
2ea0ff6870 | ||
|
|
6242087152 | ||
|
|
1da6e31de1 | ||
|
|
cb4979ab7c | ||
|
|
da653c754d | ||
|
|
c4907b81fd | ||
|
|
4c104560d5 | ||
|
|
2253e378ac | ||
|
|
ba66ac40a3 | ||
|
|
40d0d92498 | ||
|
|
4ed6cf8e18 | ||
|
|
3fc1c74f51 | ||
|
|
150e8779c7 | ||
|
|
5462248565 |
@@ -33,7 +33,7 @@ Alpaca is an [Ollama](https://github.com/ollama/ollama) client where you can man
|
|||||||
|
|
||||||
Normal conversation | Image recognition | Code highlighting | YouTube transcription | Model management
|
Normal conversation | Image recognition | Code highlighting | YouTube transcription | Model management
|
||||||
:------------------:|:-----------------:|:-----------------:|:---------------------:|:----------------:
|
:------------------:|:-----------------:|:-----------------:|:---------------------:|:----------------:
|
||||||
 |  |  |  | 
|
 |  |  |  | 
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ Language | Contributors
|
|||||||
🇷🇺 Russian | [Alex K](https://github.com/alexkdeveloper)
|
🇷🇺 Russian | [Alex K](https://github.com/alexkdeveloper)
|
||||||
🇪🇸 Spanish | [Jeffry Samuel](https://github.com/jeffser)
|
🇪🇸 Spanish | [Jeffry Samuel](https://github.com/jeffser)
|
||||||
🇫🇷 French | [Louis Chauvet-Villaret](https://github.com/loulou64490) , [Théo FORTIN](https://github.com/topiga)
|
🇫🇷 French | [Louis Chauvet-Villaret](https://github.com/loulou64490) , [Théo FORTIN](https://github.com/topiga)
|
||||||
🇧🇷 Brazilian Portuguese | [Daimar Stein](https://github.com/not-a-dev-stein)
|
🇧🇷 Brazilian Portuguese | [Daimar Stein](https://github.com/not-a-dev-stein) , [Bruno Antunes](https://github.com/antun3s)
|
||||||
🇳🇴 Norwegian | [CounterFlow64](https://github.com/CounterFlow64)
|
🇳🇴 Norwegian | [CounterFlow64](https://github.com/CounterFlow64)
|
||||||
🇮🇳 Bengali | [Aritra Saha](https://github.com/olumolu)
|
🇮🇳 Bengali | [Aritra Saha](https://github.com/olumolu)
|
||||||
🇨🇳 Simplified Chinese | [Yuehao Sui](https://github.com/8ar10der) , [Aleksana](https://github.com/Aleksanaa)
|
🇨🇳 Simplified Chinese | [Yuehao Sui](https://github.com/8ar10der) , [Aleksana](https://github.com/Aleksanaa)
|
||||||
|
|||||||
@@ -111,6 +111,45 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "python3-youtube-transcript-api",
|
||||||
|
"buildsystem": "simple",
|
||||||
|
"build-commands": [
|
||||||
|
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"youtube-transcript-api\" --no-build-isolation"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"url": "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl",
|
||||||
|
"sha256": "922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"url": "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz",
|
||||||
|
"sha256": "223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl",
|
||||||
|
"sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"url": "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl",
|
||||||
|
"sha256": "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"url": "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl",
|
||||||
|
"sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"url": "https://files.pythonhosted.org/packages/52/42/5f57d37d56bdb09722f226ed81cc1bec63942da745aa27266b16b0e16a5d/youtube_transcript_api-0.6.2-py3-none-any.whl",
|
||||||
|
"sha256": "019dbf265c6a68a0591c513fff25ed5a116ce6525832aefdfb34d4df5567121c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "python3-html2text",
|
"name": "python3-html2text",
|
||||||
"buildsystem": "simple",
|
"buildsystem": "simple",
|
||||||
|
|||||||
@@ -82,6 +82,32 @@
|
|||||||
<url type="contribute">https://github.com/Jeffser/Alpaca/discussions/154</url>
|
<url type="contribute">https://github.com/Jeffser/Alpaca/discussions/154</url>
|
||||||
<url type="vcs-browser">https://github.com/Jeffser/Alpaca</url>
|
<url type="vcs-browser">https://github.com/Jeffser/Alpaca</url>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.7.0" date="2024-10-15">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/2.7.0</url>
|
||||||
|
<description>
|
||||||
|
<p>New</p>
|
||||||
|
<ul>
|
||||||
|
<li>User messages are now compacted into bubbles</li>
|
||||||
|
</ul>
|
||||||
|
<p>Fixes</p>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed re connection dialog not working when 'use local instance' is selected</li>
|
||||||
|
<li>Fixed model manager not adapting to large system fonts</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<release version="2.6.5" date="2024-10-13">
|
||||||
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/2.6.5</url>
|
||||||
|
<description>
|
||||||
|
<p>New</p>
|
||||||
|
<ul>
|
||||||
|
<li>Details page for models</li>
|
||||||
|
<li>Model selector gets replaced with 'manage models' button when there are no models downloaded</li>
|
||||||
|
<li>Added warning when model is too big for the device</li>
|
||||||
|
<li>Added AMD GPU indicator in preferences</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="2.6.0" date="2024-10-11">
|
<release version="2.6.0" date="2024-10-11">
|
||||||
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/2.6.0</url>
|
<url type="details">https://github.com/Jeffser/Alpaca/releases/tag/2.6.0</url>
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
project('Alpaca', 'c',
|
project('Alpaca', 'c',
|
||||||
version: '2.6.0',
|
version: '2.7.0',
|
||||||
meson_version: '>= 0.62.0',
|
meson_version: '>= 0.62.0',
|
||||||
default_options: [ 'warning_level=2', 'werror=false', ],
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
)
|
)
|
||||||
|
|||||||
1093
po/alpaca.pot
1093
po/alpaca.pot
File diff suppressed because it is too large
Load Diff
1093
po/nb_NO.po
1093
po/nb_NO.po
File diff suppressed because it is too large
Load Diff
2071
po/pt_BR.po
2071
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
1096
po/zh_Hans.po
1096
po/zh_Hans.po
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
name: alpaca
|
name: jeffser-alpaca
|
||||||
base: core24
|
base: core24
|
||||||
adopt-info: alpaca
|
adopt-info: alpaca
|
||||||
|
|
||||||
@@ -63,14 +63,15 @@ parts:
|
|||||||
ollama:
|
ollama:
|
||||||
plugin: dump
|
plugin: dump
|
||||||
source:
|
source:
|
||||||
- on amd64: https://github.com/ollama/ollama/releases/download/v0.3.10/ollama-linux-amd64.tgz
|
- on amd64: https://github.com/ollama/ollama/releases/download/v0.3.12/ollama-linux-amd64.tgz
|
||||||
- on arm64: https://github.com/ollama/ollama/releases/download/v0.3.10/ollama-linux-arm64.tgz
|
- on arm64: https://github.com/ollama/ollama/releases/download/v0.3.12/ollama-linux-arm64.tgz
|
||||||
|
|
||||||
# Alpaca app
|
# Alpaca app
|
||||||
alpaca:
|
alpaca:
|
||||||
plugin: meson
|
plugin: meson
|
||||||
source-type: git
|
source-type: git
|
||||||
source: https://github.com/Jeffser/Alpaca.git
|
source: https://github.com/Jeffser/Alpaca.git
|
||||||
|
source-tag: 2.6.5
|
||||||
source-depth: 1
|
source-depth: 1
|
||||||
meson-parameters:
|
meson-parameters:
|
||||||
- --prefix=/snap/alpaca/current/usr
|
- --prefix=/snap/alpaca/current/usr
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<file alias="icons/scalable/status/chat-bubble-text-symbolic.svg">icons/chat-bubble-text-symbolic.svg</file>
|
<file alias="icons/scalable/status/chat-bubble-text-symbolic.svg">icons/chat-bubble-text-symbolic.svg</file>
|
||||||
<file alias="icons/scalable/status/execute-from-symbolic.svg">icons/execute-from-symbolic.svg</file>
|
<file alias="icons/scalable/status/execute-from-symbolic.svg">icons/execute-from-symbolic.svg</file>
|
||||||
<file alias="icons/scalable/status/cross-large-symbolic.svg">icons/cross-large-symbolic.svg</file>
|
<file alias="icons/scalable/status/cross-large-symbolic.svg">icons/cross-large-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/info-outline-symbolic.svg">icons/info-outline-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>
|
||||||
|
|||||||
@@ -11,15 +11,28 @@ logger = getLogger(__name__)
|
|||||||
|
|
||||||
window = None
|
window = None
|
||||||
|
|
||||||
|
AMD_support_label = "\n<a href='https://github.com/Jeffser/Alpaca/wiki/AMD-Support'>{}</a>".format(_('Alpaca Support'))
|
||||||
|
|
||||||
def log_output(pipe):
|
def log_output(pipe):
|
||||||
with open(os.path.join(data_dir, 'tmp.log'), 'a') as f:
|
with open(os.path.join(data_dir, 'tmp.log'), 'a') as f:
|
||||||
with pipe:
|
with pipe:
|
||||||
try:
|
try:
|
||||||
for line in iter(pipe.readline, ''):
|
for line in iter(pipe.readline, ''):
|
||||||
#print(line, end='')
|
print(line, end='')
|
||||||
f.write(line)
|
f.write(line)
|
||||||
f.flush()
|
f.flush()
|
||||||
except:
|
if 'msg="model request too large for system"' in line:
|
||||||
|
window.show_toast(_("Model request too large for system"), window.main_overlay)
|
||||||
|
elif 'msg="amdgpu detected, but no compatible rocm library found.' in line:
|
||||||
|
if bool(os.getenv("FLATPAK_ID")):
|
||||||
|
window.ollama_information_label.set_label(_("AMD GPU detected but the extension is missing, Ollama will use CPU.") + AMD_support_label)
|
||||||
|
else:
|
||||||
|
window.ollama_information_label.set_label(_("AMD GPU detected but ROCm is missing, Ollama will use CPU.") + AMD_support_label)
|
||||||
|
window.ollama_information_label.set_css_classes(['dim-label', 'error'])
|
||||||
|
elif 'msg="amdgpu is supported"' in line:
|
||||||
|
window.ollama_information_label.set_label(_("Using AMD GPU type '{}'").format(line.split('=')[-1]))
|
||||||
|
window.ollama_information_label.set_css_classes(['dim-label', 'success'])
|
||||||
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class instance():
|
class instance():
|
||||||
@@ -116,6 +129,8 @@ class instance():
|
|||||||
self.instance = instance
|
self.instance = instance
|
||||||
if not self.idle_timer:
|
if not self.idle_timer:
|
||||||
self.start_timer()
|
self.start_timer()
|
||||||
|
window.ollama_information_label.set_label(_("Integrated Ollama instance is running"))
|
||||||
|
window.ollama_information_label.set_css_classes(['dim-label', 'success'])
|
||||||
else:
|
else:
|
||||||
self.remote = True
|
self.remote = True
|
||||||
window.remote_connection_switch.set_sensitive(True)
|
window.remote_connection_switch.set_sensitive(True)
|
||||||
@@ -130,6 +145,8 @@ class instance():
|
|||||||
self.instance.terminate()
|
self.instance.terminate()
|
||||||
self.instance.wait()
|
self.instance.wait()
|
||||||
self.instance = None
|
self.instance = None
|
||||||
|
window.ollama_information_label.set_label(_("Integrated Ollama instance is not running"))
|
||||||
|
window.ollama_information_label.set_css_classes(['dim-label'])
|
||||||
logger.info("Stopped Alpaca's Ollama instance")
|
logger.info("Stopped Alpaca's Ollama instance")
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ class chat(Gtk.ScrolledWindow):
|
|||||||
vexpand=True,
|
vexpand=True,
|
||||||
hexpand=True,
|
hexpand=True,
|
||||||
css_classes=["undershoot-bottom"],
|
css_classes=["undershoot-bottom"],
|
||||||
name=name
|
name=name,
|
||||||
|
hscrollbar_policy=2
|
||||||
)
|
)
|
||||||
self.messages = {}
|
self.messages = {}
|
||||||
self.welcome_screen = None
|
self.welcome_screen = None
|
||||||
|
|||||||
@@ -230,7 +230,8 @@ class attachment_container(Gtk.ScrolledWindow):
|
|||||||
|
|
||||||
self.container = Gtk.Box(
|
self.container = Gtk.Box(
|
||||||
orientation=0,
|
orientation=0,
|
||||||
spacing=12
|
spacing=10,
|
||||||
|
valign=1
|
||||||
)
|
)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@@ -238,7 +239,8 @@ class attachment_container(Gtk.ScrolledWindow):
|
|||||||
margin_start=10,
|
margin_start=10,
|
||||||
margin_end=10,
|
margin_end=10,
|
||||||
hexpand=True,
|
hexpand=True,
|
||||||
child=self.container
|
child=self.container,
|
||||||
|
vscrollbar_policy=2
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_file(self, file:attachment):
|
def add_file(self, file:attachment):
|
||||||
@@ -467,10 +469,15 @@ class message(Gtk.Overlay):
|
|||||||
orientation=1,
|
orientation=1,
|
||||||
halign='fill',
|
halign='fill',
|
||||||
css_classes=["response_message"] if self.bot else ["card", "user_message"],
|
css_classes=["response_message"] if self.bot else ["card", "user_message"],
|
||||||
spacing=12
|
spacing=5,
|
||||||
|
width_request=-1 if self.bot else 375
|
||||||
)
|
)
|
||||||
|
|
||||||
super().__init__(css_classes=["message"], name=message_id)
|
super().__init__(
|
||||||
|
css_classes=["message"],
|
||||||
|
name=message_id,
|
||||||
|
halign=0 if self.bot else 2
|
||||||
|
)
|
||||||
self.set_child(self.container)
|
self.set_child(self.container)
|
||||||
|
|
||||||
def add_attachments(self, attachments:dict):
|
def add_attachments(self, attachments:dict):
|
||||||
@@ -619,7 +626,7 @@ class message(Gtk.Overlay):
|
|||||||
if self.spinner:
|
if self.spinner:
|
||||||
self.container.remove(self.spinner)
|
self.container.remove(self.spinner)
|
||||||
self.spinner = None
|
self.spinner = None
|
||||||
self.spinner = Gtk.Spinner(spinning=True, margin_top=12, margin_bottom=12, hexpand=True)
|
self.spinner = Gtk.Spinner(spinning=True, margin_top=10, margin_bottom=10, hexpand=True)
|
||||||
self.container.append(self.spinner)
|
self.container.append(self.spinner)
|
||||||
self.container.append(text_b)
|
self.container.append(text_b)
|
||||||
self.container.queue_draw()
|
self.container.queue_draw()
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class model_selector_popup(Gtk.Popover):
|
|||||||
class model_selector_row(Gtk.ListBoxRow):
|
class model_selector_row(Gtk.ListBoxRow):
|
||||||
__gtype_name__ = 'AlpacaModelSelectorRow'
|
__gtype_name__ = 'AlpacaModelSelectorRow'
|
||||||
|
|
||||||
def __init__(self, model_name:str, image_recognition:bool):
|
def __init__(self, model_name:str, data:dict):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
child = Gtk.Label(
|
child = Gtk.Label(
|
||||||
label=window.convert_model_name(model_name, 0),
|
label=window.convert_model_name(model_name, 0),
|
||||||
@@ -68,7 +68,8 @@ class model_selector_row(Gtk.ListBoxRow):
|
|||||||
name=model_name,
|
name=model_name,
|
||||||
tooltip_text=window.convert_model_name(model_name, 0)
|
tooltip_text=window.convert_model_name(model_name, 0)
|
||||||
)
|
)
|
||||||
self.image_recognition = image_recognition
|
self.data = data
|
||||||
|
self.image_recognition = 'projector_info' in self.data
|
||||||
|
|
||||||
class model_selector_button(Gtk.MenuButton):
|
class model_selector_button(Gtk.MenuButton):
|
||||||
__gtype_name__ = 'AlpacaModelSelectorButton'
|
__gtype_name__ = 'AlpacaModelSelectorButton'
|
||||||
@@ -81,11 +82,10 @@ class model_selector_button(Gtk.MenuButton):
|
|||||||
orientation=0,
|
orientation=0,
|
||||||
spacing=5
|
spacing=5
|
||||||
)
|
)
|
||||||
self.label = Gtk.Label(label=_('Select a Model'))
|
self.label = Gtk.Label()
|
||||||
container.append(self.label)
|
container.append(self.label)
|
||||||
container.append(Gtk.Image.new_from_icon_name("down-symbolic"))
|
container.append(Gtk.Image.new_from_icon_name("down-symbolic"))
|
||||||
super().__init__(
|
super().__init__(
|
||||||
tooltip_text=_('Select a Model'),
|
|
||||||
child=container,
|
child=container,
|
||||||
popover=self.popover,
|
popover=self.popover,
|
||||||
halign=3
|
halign=3
|
||||||
@@ -104,27 +104,28 @@ class model_selector_button(Gtk.MenuButton):
|
|||||||
self.label.set_label(window.convert_model_name(model_name, 0))
|
self.label.set_label(window.convert_model_name(model_name, 0))
|
||||||
self.set_tooltip_text(window.convert_model_name(model_name, 0))
|
self.set_tooltip_text(window.convert_model_name(model_name, 0))
|
||||||
elif len(list(listbox)) == 0:
|
elif len(list(listbox)) == 0:
|
||||||
self.label.set_label(_("Select a Model"))
|
window.title_stack.set_visible_child_name('no_models')
|
||||||
self.set_tooltip_text(_("Select a Model"))
|
|
||||||
window.model_manager.verify_if_image_can_be_used()
|
window.model_manager.verify_if_image_can_be_used()
|
||||||
|
|
||||||
def add_model(self, model_name:str):
|
def add_model(self, model_name:str):
|
||||||
vision = False
|
data = None
|
||||||
response = window.ollama_instance.request("POST", "api/show", json.dumps({"name": model_name}))
|
response = window.ollama_instance.request("POST", "api/show", json.dumps({"name": model_name}))
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
logger.error(f"Status code was {response.status_code}")
|
logger.error(f"Status code was {response.status_code}")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
vision = 'projector_info' in json.loads(response.text)
|
data = json.loads(response.text)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error fetching vision info: {str(e)}")
|
logger.error(f"Error fetching 'api - show' info: {str(e)}")
|
||||||
model_row = model_selector_row(model_name, vision)
|
model_row = model_selector_row(model_name, data)
|
||||||
GLib.idle_add(self.get_popover().model_list_box.append, model_row)
|
GLib.idle_add(self.get_popover().model_list_box.append, model_row)
|
||||||
GLib.idle_add(self.change_model, model_name)
|
GLib.idle_add(self.change_model, model_name)
|
||||||
|
GLib.idle_add(window.title_stack.set_visible_child_name, 'model_selector')
|
||||||
|
|
||||||
def remove_model(self, model_name:str):
|
def remove_model(self, model_name:str):
|
||||||
self.get_popover().model_list_box.remove(next((model for model in list(self.get_popover().model_list_box) if model.get_name() == model_name), None))
|
self.get_popover().model_list_box.remove(next((model for model in list(self.get_popover().model_list_box) if model.get_name() == model_name), None))
|
||||||
self.model_changed(self.get_popover().model_list_box)
|
self.model_changed(self.get_popover().model_list_box)
|
||||||
|
window.title_stack.set_visible_child_name('model_selector' if len(window.model_manager.get_model_list()) > 0 else 'no_models')
|
||||||
|
|
||||||
def clear_list(self):
|
def clear_list(self):
|
||||||
self.get_popover().model_list_box.remove_all()
|
self.get_popover().model_list_box.remove_all()
|
||||||
@@ -248,6 +249,37 @@ class pulling_model_list(Gtk.ListBox):
|
|||||||
visible=False
|
visible=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class information_bow(Gtk.Box):
|
||||||
|
__gtype_name__ = 'AlpacaModelInformationBow'
|
||||||
|
|
||||||
|
def __init__(self, title:str, subtitle:str):
|
||||||
|
self.title = title
|
||||||
|
self.subtitle = subtitle
|
||||||
|
title_label = Gtk.Label(
|
||||||
|
label=self.title,
|
||||||
|
css_classes=['subtitle', 'caption', 'dim-label'],
|
||||||
|
hexpand=True,
|
||||||
|
margin_top=10,
|
||||||
|
margin_start=0,
|
||||||
|
margin_end=0
|
||||||
|
)
|
||||||
|
subtitle_label = Gtk.Label(
|
||||||
|
label=self.subtitle if self.subtitle else '(none)',
|
||||||
|
css_classes=['heading'],
|
||||||
|
hexpand=True,
|
||||||
|
margin_bottom=10,
|
||||||
|
margin_start=0,
|
||||||
|
margin_end=0
|
||||||
|
)
|
||||||
|
super().__init__(
|
||||||
|
spacing=5,
|
||||||
|
orientation=1,
|
||||||
|
css_classes=['card']
|
||||||
|
)
|
||||||
|
self.append(title_label)
|
||||||
|
self.append(subtitle_label)
|
||||||
|
|
||||||
|
|
||||||
class local_model(Gtk.ListBoxRow):
|
class local_model(Gtk.ListBoxRow):
|
||||||
__gtype_name__ = 'AlpacaLocalModel'
|
__gtype_name__ = 'AlpacaLocalModel'
|
||||||
|
|
||||||
@@ -275,6 +307,16 @@ class local_model(Gtk.ListBoxRow):
|
|||||||
description_box.append(model_label)
|
description_box.append(model_label)
|
||||||
description_box.append(tag_label)
|
description_box.append(tag_label)
|
||||||
|
|
||||||
|
info_button = Gtk.Button(
|
||||||
|
icon_name = "info-outline-symbolic",
|
||||||
|
vexpand = False,
|
||||||
|
valign = 3,
|
||||||
|
css_classes = ["circular"],
|
||||||
|
tooltip_text = _("Details")
|
||||||
|
)
|
||||||
|
|
||||||
|
info_button.connect('clicked', self.show_information)
|
||||||
|
|
||||||
delete_button = Gtk.Button(
|
delete_button = Gtk.Button(
|
||||||
icon_name = "user-trash-symbolic",
|
icon_name = "user-trash-symbolic",
|
||||||
vexpand = False,
|
vexpand = False,
|
||||||
@@ -302,6 +344,7 @@ class local_model(Gtk.ListBoxRow):
|
|||||||
margin_end=10
|
margin_end=10
|
||||||
)
|
)
|
||||||
container_box.append(description_box)
|
container_box.append(description_box)
|
||||||
|
container_box.append(info_button)
|
||||||
container_box.append(delete_button)
|
container_box.append(delete_button)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@@ -309,6 +352,53 @@ class local_model(Gtk.ListBoxRow):
|
|||||||
name=model_name
|
name=model_name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def show_information(self, button):
|
||||||
|
model = next((element for element in list(window.model_manager.model_selector.get_popover().model_list_box) if element.get_name() == self.get_name()), None)
|
||||||
|
model_name = model.get_child().get_label()
|
||||||
|
|
||||||
|
window.model_detail_page.set_title(' ('.join(model_name.split(' (')[:-1]))
|
||||||
|
window.model_detail_page.set_description(' ('.join(model_name.split(' (')[-1:])[:-1])
|
||||||
|
window.model_detail_create_button.set_name(model_name)
|
||||||
|
window.model_detail_create_button.set_tooltip_text(_("Create Model Based on '{}'").format(model_name))
|
||||||
|
|
||||||
|
details_flow_box = Gtk.FlowBox(
|
||||||
|
valign=1,
|
||||||
|
hexpand=True,
|
||||||
|
vexpand=False,
|
||||||
|
selection_mode=0,
|
||||||
|
max_children_per_line=2,
|
||||||
|
min_children_per_line=1,
|
||||||
|
margin_top=12,
|
||||||
|
margin_bottom=12,
|
||||||
|
margin_start=12,
|
||||||
|
margin_end=12
|
||||||
|
)
|
||||||
|
|
||||||
|
translation_strings={
|
||||||
|
'modified_at': _('Modified At'),
|
||||||
|
'parent_model': _('Parent Model'),
|
||||||
|
'format': _('Format'),
|
||||||
|
'family': _('Family'),
|
||||||
|
'parameter_size': _('Parameter Size'),
|
||||||
|
'quantization_level': _('Quantization Level')
|
||||||
|
}
|
||||||
|
|
||||||
|
if 'modified_at' in model.data and model.data['modified_at']:
|
||||||
|
details_flow_box.append(information_bow(
|
||||||
|
title=translation_strings['modified_at'],
|
||||||
|
subtitle=datetime.datetime.strptime(':'.join(model.data['modified_at'].split(':')[:2]), '%Y-%m-%dT%H:%M').strftime('%Y-%m-%d %H:%M')
|
||||||
|
))
|
||||||
|
|
||||||
|
for name, value in model.data['details'].items():
|
||||||
|
if isinstance(value, str):
|
||||||
|
details_flow_box.append(information_bow(
|
||||||
|
title=translation_strings[name] if name in translation_strings else name.replace('_', ' ').title(),
|
||||||
|
subtitle=value
|
||||||
|
))
|
||||||
|
|
||||||
|
window.model_detail_page.set_child(details_flow_box)
|
||||||
|
window.navigation_view_manage_models.push_by_tag('model_information')
|
||||||
|
|
||||||
class local_model_list(Gtk.ListBox):
|
class local_model_list(Gtk.ListBox):
|
||||||
__gtype_name__ = 'AlpacaLocalModelList'
|
__gtype_name__ = 'AlpacaLocalModelList'
|
||||||
|
|
||||||
@@ -341,7 +431,9 @@ class available_model(Gtk.ListBoxRow):
|
|||||||
label="<b>{}</b> <small>by {}</small>".format(self.model_title, self.model_author),
|
label="<b>{}</b> <small>by {}</small>".format(self.model_title, self.model_author),
|
||||||
hexpand=True,
|
hexpand=True,
|
||||||
halign=1,
|
halign=1,
|
||||||
use_markup=True
|
use_markup=True,
|
||||||
|
wrap=True,
|
||||||
|
wrap_mode=0
|
||||||
)
|
)
|
||||||
description_label = Gtk.Label(
|
description_label = Gtk.Label(
|
||||||
css_classes=["subtitle"],
|
css_classes=["subtitle"],
|
||||||
@@ -521,7 +613,8 @@ class model_manager_container(Gtk.Box):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
window.connection_error()
|
window.connection_error()
|
||||||
window.title_stack.set_visible_child_name('model_selector')
|
window.title_stack.set_visible_child_name('model_selector' if len(window.model_manager.get_model_list()) > 0 else 'no_models')
|
||||||
|
#window.title_stack.set_visible_child_name('model_selector')
|
||||||
window.chat_list_box.update_welcome_screens(len(self.get_model_list()) > 0)
|
window.chat_list_box.update_welcome_screens(len(self.get_model_list()) > 0)
|
||||||
|
|
||||||
#Should only be called when the app starts
|
#Should only be called when the app starts
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Working on organizing the code
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os, requests
|
import os, requests
|
||||||
from pytube import YouTube
|
from youtube_transcript_api import YouTubeTranscriptApi
|
||||||
from html2text import html2text
|
from html2text import html2text
|
||||||
from .internal import cache_dir
|
from .internal import cache_dir
|
||||||
|
|
||||||
@@ -18,25 +18,36 @@ def connect_remote(remote_url:str, bearer_token:str):
|
|||||||
window.model_manager.update_local_list()
|
window.model_manager.update_local_list()
|
||||||
window.save_server_config()
|
window.save_server_config()
|
||||||
|
|
||||||
def attach_youtube(video_url:str, caption_name:str):
|
def attach_youtube(video_title:str, video_author:str, watch_url:str, video_url:str, video_id:str, caption_name:str):
|
||||||
buffer = window.message_text_view.get_buffer()
|
buffer = window.message_text_view.get_buffer()
|
||||||
text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False).replace(video_url, "")
|
text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False).replace(video_url, "")
|
||||||
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
|
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
|
||||||
buffer.insert(buffer.get_start_iter(), text, len(text))
|
buffer.insert(buffer.get_start_iter(), text, len(text))
|
||||||
|
|
||||||
yt = YouTube(video_url)
|
result_text = "{}\n{}\n{}\n\n".format(video_title, video_author, watch_url)
|
||||||
text = "{}\n{}\n{}\n\n".format(yt.title, yt.author, yt.watch_url)
|
caption_name = caption_name.split(' (')[-1][:-1]
|
||||||
|
|
||||||
|
if caption_name.startswith('Translate:'):
|
||||||
|
available_captions = get_youtube_transcripts(video_id)
|
||||||
|
original_caption_name = available_captions[0].split(' (')[-1][:-1]
|
||||||
|
transcript = YouTubeTranscriptApi.list_transcripts(video_id).find_transcript([original_caption_name]).translate(caption_name.split(':')[-1]).fetch()
|
||||||
|
result_text += '(Auto translated from {})\n'.format(available_captions[0])
|
||||||
|
else:
|
||||||
|
transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[caption_name])
|
||||||
|
|
||||||
|
result_text += '\n'.join([t['text'] for t in transcript])
|
||||||
|
|
||||||
for event in yt.captions[caption_name.split('(')[-1][:-1]].json_captions['events']:
|
|
||||||
text += "{}\n".format(event['segs'][0]['utf8'].replace('\n', '\\n'))
|
|
||||||
if not os.path.exists(os.path.join(cache_dir, 'tmp/youtube')):
|
if not os.path.exists(os.path.join(cache_dir, 'tmp/youtube')):
|
||||||
os.makedirs(os.path.join(cache_dir, 'tmp/youtube'))
|
os.makedirs(os.path.join(cache_dir, 'tmp/youtube'))
|
||||||
file_path = os.path.join(os.path.join(cache_dir, 'tmp/youtube'), f'{yt.title} ({caption_name.split(" (")[0]})')
|
file_path = os.path.join(os.path.join(cache_dir, 'tmp/youtube'), '{} ({})'.format(video_title.replace('/', ' '), caption_name))
|
||||||
with open(file_path, 'w+', encoding="utf-8") as f:
|
with open(file_path, 'w+', encoding="utf-8") as f:
|
||||||
f.write(text)
|
f.write(result_text)
|
||||||
|
|
||||||
window.attach_file(file_path, 'youtube')
|
window.attach_file(file_path, 'youtube')
|
||||||
|
|
||||||
|
def get_youtube_transcripts(video_id:str):
|
||||||
|
return ['{} ({})'.format(t.language, t.language_code) for t in YouTubeTranscriptApi.list_transcripts(video_id)]
|
||||||
|
|
||||||
def attach_website(url:str):
|
def attach_website(url:str):
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
|
|||||||
2
src/icons/info-outline-symbolic.svg
Normal file
2
src/icons/info-outline-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 8 0 c -4.410156 0 -8 3.589844 -8 8 s 3.589844 8 8 8 s 8 -3.589844 8 -8 s -3.589844 -8 -8 -8 z m 0 2 c 3.332031 0 6 2.667969 6 6 s -2.667969 6 -6 6 s -6 -2.667969 -6 -6 s 2.667969 -6 6 -6 z m 0 1.875 c -0.621094 0 -1.125 0.503906 -1.125 1.125 s 0.503906 1.125 1.125 1.125 s 1.125 -0.503906 1.125 -1.125 s -0.503906 -1.125 -1.125 -1.125 z m -1.523438 3.125 c -0.265624 0.011719 -0.476562 0.230469 -0.476562 0.5 c 0 0.277344 0.222656 0.5 0.5 0.5 h 0.5 v 3 h -0.5 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 h -0.5 v -4 h -2.5 c -0.007812 0 -0.015625 0 -0.023438 0 z m 0 0" fill="#222222"/></svg>
|
||||||
|
After Width: | Height: | Size: 813 B |
@@ -40,6 +40,7 @@ translators = [
|
|||||||
'Louis Chauvet-Villaret (French) https://github.com/loulou64490',
|
'Louis Chauvet-Villaret (French) https://github.com/loulou64490',
|
||||||
'Théo FORTIN (French) https://github.com/topiga',
|
'Théo FORTIN (French) https://github.com/topiga',
|
||||||
'Daimar Stein (Brazilian Portuguese) https://github.com/not-a-dev-stein',
|
'Daimar Stein (Brazilian Portuguese) https://github.com/not-a-dev-stein',
|
||||||
|
'Bruno Antunes (Brazilian Portuguese) https://github.com/antun3s',
|
||||||
'CounterFlow64 (Norwegian) https://github.com/CounterFlow64',
|
'CounterFlow64 (Norwegian) https://github.com/CounterFlow64',
|
||||||
'Aritra Saha (Bengali) https://github.com/olumolu',
|
'Aritra Saha (Bengali) https://github.com/olumolu',
|
||||||
'Yuehao Sui (Simplified Chinese) https://github.com/8ar10der',
|
'Yuehao Sui (Simplified Chinese) https://github.com/8ar10der',
|
||||||
@@ -56,7 +57,8 @@ class AlpacaApplication(Adw.Application):
|
|||||||
def __init__(self, version):
|
def __init__(self, version):
|
||||||
super().__init__(application_id='com.jeffser.Alpaca',
|
super().__init__(application_id='com.jeffser.Alpaca',
|
||||||
flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
|
flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
|
||||||
self.create_action('quit', lambda *_: self.props.active_window.closing_app(None), ['<primary>w', '<primary>q'])
|
self.create_action('quit', lambda *_: self.props.active_window.closing_app(None), ['<primary>q'])
|
||||||
|
self.set_accels_for_action('app.delete_current_chat', ['<primary>w'])
|
||||||
self.create_action('preferences', lambda *_: self.props.active_window.preferences_dialog.present(self.props.active_window), ['<primary>comma'])
|
self.create_action('preferences', lambda *_: self.props.active_window.preferences_dialog.present(self.props.active_window), ['<primary>comma'])
|
||||||
self.create_action('about', self.on_about_action)
|
self.create_action('about', self.on_about_action)
|
||||||
self.set_accels_for_action("win.show-help-overlay", ['<primary>slash'])
|
self.set_accels_for_action("win.show-help-overlay", ['<primary>slash'])
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
preferences_dialog = Gtk.Template.Child()
|
preferences_dialog = Gtk.Template.Child()
|
||||||
shortcut_window : Gtk.ShortcutsWindow = Gtk.Template.Child()
|
shortcut_window : Gtk.ShortcutsWindow = Gtk.Template.Child()
|
||||||
file_preview_dialog = Gtk.Template.Child()
|
file_preview_dialog = Gtk.Template.Child()
|
||||||
file_preview_text_view = Gtk.Template.Child()
|
file_preview_text_label = Gtk.Template.Child()
|
||||||
file_preview_image = Gtk.Template.Child()
|
file_preview_image = Gtk.Template.Child()
|
||||||
welcome_dialog = Gtk.Template.Child()
|
welcome_dialog = Gtk.Template.Child()
|
||||||
welcome_carousel = Gtk.Template.Child()
|
welcome_carousel = Gtk.Template.Child()
|
||||||
@@ -102,6 +102,9 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
title_stack = Gtk.Template.Child()
|
title_stack = Gtk.Template.Child()
|
||||||
manage_models_dialog = Gtk.Template.Child()
|
manage_models_dialog = Gtk.Template.Child()
|
||||||
model_scroller = Gtk.Template.Child()
|
model_scroller = Gtk.Template.Child()
|
||||||
|
model_detail_page = Gtk.Template.Child()
|
||||||
|
model_detail_create_button = Gtk.Template.Child()
|
||||||
|
ollama_information_label = Gtk.Template.Child()
|
||||||
|
|
||||||
chat_list_container = Gtk.Template.Child()
|
chat_list_container = Gtk.Template.Child()
|
||||||
chat_list_box = None
|
chat_list_box = None
|
||||||
@@ -332,6 +335,10 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
clipboard.read_text_async(None, self.cb_text_received)
|
clipboard.read_text_async(None, self.cb_text_received)
|
||||||
clipboard.read_texture_async(None, self.cb_image_received)
|
clipboard.read_texture_async(None, self.cb_image_received)
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def model_detail_create_button_clicked(self, button):
|
||||||
|
self.create_model(button.get_name(), False)
|
||||||
|
|
||||||
def convert_model_name(self, name:str, mode:int) -> str: # mode=0 name:tag -> Name (tag) | mode=1 Name (tag) -> name:tag
|
def convert_model_name(self, name:str, mode:int) -> str: # mode=0 name:tag -> Name (tag) | mode=1 Name (tag) -> name:tag
|
||||||
try:
|
try:
|
||||||
if mode == 0:
|
if mode == 0:
|
||||||
@@ -351,20 +358,15 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
modelfile_buffer.delete(modelfile_buffer.get_start_iter(), modelfile_buffer.get_end_iter())
|
modelfile_buffer.delete(modelfile_buffer.get_start_iter(), modelfile_buffer.get_end_iter())
|
||||||
self.create_model_system.set_text('')
|
self.create_model_system.set_text('')
|
||||||
if not file:
|
if not file:
|
||||||
response = self.ollama_instance.request("POST", "api/show", json.dumps({"name": self.convert_model_name(model, 1)}))
|
data = next((element for element in list(self.model_manager.model_selector.get_popover().model_list_box) if element.get_name() == self.convert_model_name(model, 1)), None).data
|
||||||
if response.status_code == 200:
|
modelfile = []
|
||||||
data = json.loads(response.text)
|
for line in data['modelfile'].split('\n'):
|
||||||
modelfile = []
|
if line.startswith('SYSTEM'):
|
||||||
for line in data['modelfile'].split('\n'):
|
self.create_model_system.set_text(line[len('SYSTEM'):].strip())
|
||||||
if line.startswith('SYSTEM'):
|
if not line.startswith('SYSTEM') and not line.startswith('FROM') and not line.startswith('#'):
|
||||||
self.create_model_system.set_text(line[len('SYSTEM'):].strip())
|
modelfile.append(line)
|
||||||
if not line.startswith('SYSTEM') and not line.startswith('FROM') and not line.startswith('#'):
|
self.create_model_name.set_text(self.convert_model_name(model, 1).split(':')[0] + "-custom")
|
||||||
modelfile.append(line)
|
modelfile_buffer.insert(modelfile_buffer.get_start_iter(), '\n'.join(modelfile), len('\n'.join(modelfile).encode('utf-8')))
|
||||||
self.create_model_name.set_text(self.convert_model_name(model, 1).split(':')[0] + "-custom")
|
|
||||||
modelfile_buffer.insert(modelfile_buffer.get_start_iter(), '\n'.join(modelfile), len('\n'.join(modelfile).encode('utf-8')))
|
|
||||||
else:
|
|
||||||
##TODO ERROR MESSAGE
|
|
||||||
return
|
|
||||||
self.create_model_base.set_subtitle(self.convert_model_name(model, 1))
|
self.create_model_base.set_subtitle(self.convert_model_name(model, 1))
|
||||||
else:
|
else:
|
||||||
self.create_model_name.set_text(os.path.splitext(os.path.basename(model))[0])
|
self.create_model_name.set_text(os.path.splitext(os.path.basename(model))[0])
|
||||||
@@ -403,7 +405,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
if content:
|
if content:
|
||||||
if file_type == 'image':
|
if file_type == 'image':
|
||||||
self.file_preview_image.set_visible(True)
|
self.file_preview_image.set_visible(True)
|
||||||
self.file_preview_text_view.set_visible(False)
|
self.file_preview_text_label.set_visible(False)
|
||||||
image_data = base64.b64decode(content)
|
image_data = base64.b64decode(content)
|
||||||
loader = GdkPixbuf.PixbufLoader.new()
|
loader = GdkPixbuf.PixbufLoader.new()
|
||||||
loader.write(image_data)
|
loader.write(image_data)
|
||||||
@@ -416,10 +418,8 @@ class AlpacaWindow(Adw.ApplicationWindow):
|
|||||||
self.file_preview_open_button.set_name(file_path)
|
self.file_preview_open_button.set_name(file_path)
|
||||||
else:
|
else:
|
||||||
self.file_preview_image.set_visible(False)
|
self.file_preview_image.set_visible(False)
|
||||||
self.file_preview_text_view.set_visible(True)
|
self.file_preview_text_label.set_visible(True)
|
||||||
buffer = self.file_preview_text_view.get_buffer()
|
buffer = self.file_preview_text_label.set_label(content)
|
||||||
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
|
|
||||||
buffer.insert(buffer.get_start_iter(), content, len(content.encode('utf-8')))
|
|
||||||
if file_type == 'youtube':
|
if file_type == 'youtube':
|
||||||
self.file_preview_dialog.set_title(content.split('\n')[0])
|
self.file_preview_dialog.set_title(content.split('\n')[0])
|
||||||
self.file_preview_open_button.set_name(content.split('\n')[2])
|
self.file_preview_open_button.set_name(content.split('\n')[2])
|
||||||
@@ -607,6 +607,7 @@ Generate a title following these rules:
|
|||||||
self.chat_list_box.prepend_chat(_("New Chat"))
|
self.chat_list_box.prepend_chat(_("New Chat"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_numbered_name(self, chat_name:str, compare_list:list) -> str:
|
def generate_numbered_name(self, chat_name:str, compare_list:list) -> str:
|
||||||
if chat_name in compare_list:
|
if chat_name in compare_list:
|
||||||
for i in range(len(compare_list)):
|
for i in range(len(compare_list)):
|
||||||
@@ -628,7 +629,7 @@ Generate a title following these rules:
|
|||||||
if self.ollama_instance.remote:
|
if self.ollama_instance.remote:
|
||||||
options = {
|
options = {
|
||||||
_("Close Alpaca"): {"callback": lambda *_: self.get_application().quit(), "appearance": "destructive"},
|
_("Close Alpaca"): {"callback": lambda *_: self.get_application().quit(), "appearance": "destructive"},
|
||||||
_("Use Local Instance"): {"callback": lambda *_: window.remote_connection_switch.set_active(False)},
|
_("Use Local Instance"): {"callback": lambda *_: self.remote_connection_switch.set_active(False)},
|
||||||
_("Connect"): {"callback": lambda url, bearer: generic_actions.connect_remote(url,bearer), "appearance": "suggested"}
|
_("Connect"): {"callback": lambda url, bearer: generic_actions.connect_remote(url,bearer), "appearance": "suggested"}
|
||||||
}
|
}
|
||||||
entries = [
|
entries = [
|
||||||
@@ -740,6 +741,36 @@ Generate a title following these rules:
|
|||||||
self.selected_chat_row = self.chat_list_box.get_selected_row()
|
self.selected_chat_row = self.chat_list_box.get_selected_row()
|
||||||
self.chat_actions(action, user_data)
|
self.chat_actions(action, user_data)
|
||||||
|
|
||||||
|
def youtube_detected(self, video_url):
|
||||||
|
try:
|
||||||
|
tries=0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
yt = YouTube(video_url)
|
||||||
|
video_title = yt.title
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
tries+=1
|
||||||
|
if tries == 4:
|
||||||
|
raise Exception(e)
|
||||||
|
transcriptions = generic_actions.get_youtube_transcripts(yt.video_id)
|
||||||
|
if len(transcriptions) == 0:
|
||||||
|
self.show_toast(_("This video does not have any transcriptions"), self.main_overlay)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not any(filter(lambda x: '(en' in x and 'auto-generated' not in x and len(transcriptions) > 1, transcriptions)):
|
||||||
|
transcriptions.insert(1, 'English (translate:en)')
|
||||||
|
|
||||||
|
dialog_widget.simple_dropdown(
|
||||||
|
_('Attach YouTube Video?'),
|
||||||
|
_('{}\n\nPlease select a transcript to include').format(video_title),
|
||||||
|
lambda caption_name, yt=yt, video_url=video_url: generic_actions.attach_youtube(yt.title, yt.author, yt.watch_url, video_url, yt.video_id, caption_name),
|
||||||
|
transcriptions
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
self.show_toast(_("Error attaching video, please try again"), self.main_overlay)
|
||||||
|
|
||||||
def cb_text_received(self, clipboard, result):
|
def cb_text_received(self, clipboard, result):
|
||||||
try:
|
try:
|
||||||
text = clipboard.read_text_finish(result)
|
text = clipboard.read_text_finish(result)
|
||||||
@@ -755,22 +786,7 @@ Generate a title following these rules:
|
|||||||
r'(?:/[^\\s]*)?'
|
r'(?:/[^\\s]*)?'
|
||||||
)
|
)
|
||||||
if youtube_regex.match(text):
|
if youtube_regex.match(text):
|
||||||
try:
|
self.youtube_detected(text)
|
||||||
yt = YouTube(text)
|
|
||||||
captions = yt.captions
|
|
||||||
if len(captions) == 0:
|
|
||||||
self.show_toast(_("This video does not have any transcriptions"), self.main_overlay)
|
|
||||||
return
|
|
||||||
video_title = yt.title
|
|
||||||
dialog_widget.simple_dropdown(
|
|
||||||
_('Attach YouTube Video?'),
|
|
||||||
_('{}\n\nPlease select a transcript to include').format(video_title),
|
|
||||||
lambda caption_name, video_url=text: generic_actions.attach_youtube(video_url, caption_name),
|
|
||||||
["{} ({})".format(caption.name.title(), caption.code) for caption in captions]
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(e)
|
|
||||||
self.show_toast(_("This video is not available"), self.main_overlay)
|
|
||||||
elif url_regex.match(text):
|
elif url_regex.match(text):
|
||||||
dialog_widget.simple(
|
dialog_widget.simple(
|
||||||
_('Attach Website? (Experimental)'),
|
_('Attach Website? (Experimental)'),
|
||||||
@@ -829,7 +845,7 @@ Generate a title following these rules:
|
|||||||
|
|
||||||
[element.set_sensitive(True) for element in sensitive_elements]
|
[element.set_sensitive(True) for element in sensitive_elements]
|
||||||
self.get_application().lookup_action('manage_models').set_enabled(True)
|
self.get_application().lookup_action('manage_models').set_enabled(True)
|
||||||
self.title_stack.set_visible_child_name('model_selector')
|
self.title_stack.set_visible_child_name('model_selector' if len(self.model_manager.get_model_list()) > 0 else 'no_models')
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
options = {
|
options = {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<signal name="close-request" handler="closing_app"/>
|
<signal name="close-request" handler="closing_app"/>
|
||||||
<property name="resizable">True</property>
|
<property name="resizable">True</property>
|
||||||
<property name="width-request">400</property>
|
<property name="width-request">400</property>
|
||||||
<property name="height-request">400</property>
|
<property name="height-request">600</property>
|
||||||
<property name="default-width">1300</property>
|
<property name="default-width">1300</property>
|
||||||
<property name="default-height">800</property>
|
<property name="default-height">800</property>
|
||||||
<property name="title">Alpaca</property>
|
<property name="title">Alpaca</property>
|
||||||
@@ -97,6 +97,18 @@
|
|||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStackPage">
|
||||||
|
<property name="name">no_models</property>
|
||||||
|
<property name="child">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label" translatable="yes">Manage Models</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Manage Models</property>
|
||||||
|
<property name="action-name">app.manage_models</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="end">
|
<child type="end">
|
||||||
@@ -212,6 +224,7 @@
|
|||||||
<signal name="paste-clipboard" handler="on_clipboard_paste"/>
|
<signal name="paste-clipboard" handler="on_clipboard_paste"/>
|
||||||
<style>
|
<style>
|
||||||
<class name="message_text_view"/>
|
<class name="message_text_view"/>
|
||||||
|
<class name="undershoot-bottom"/>
|
||||||
</style>
|
</style>
|
||||||
<property name="wrap-mode">word</property>
|
<property name="wrap-mode">word</property>
|
||||||
<property name="top-margin">10</property>
|
<property name="top-margin">10</property>
|
||||||
@@ -457,6 +470,21 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="ollama_information_label">
|
||||||
|
<property name="wrap">true</property>
|
||||||
|
<property name="use-markup">true</property>
|
||||||
|
<property name="label" translatable="yes">Integrated Ollama instance is not running</property>
|
||||||
|
<property name="justify">2</property>
|
||||||
|
<style>
|
||||||
|
<class name="dim-label"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -639,20 +667,48 @@
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwNavigationPage">
|
<object class="AdwNavigationPage">
|
||||||
<property name="title" translatable="yes">Create Model</property>
|
<property name="title" translatable="yes">Model Details</property>
|
||||||
<property name="tag">model_create_page</property>
|
<property name="tag">model_information</property>
|
||||||
<property name="child">
|
<property name="child">
|
||||||
<object class="AdwToolbarView">
|
<object class="AdwToolbarView">
|
||||||
<child type="top">
|
<child type="top">
|
||||||
<object class="AdwHeaderBar">
|
<object class="AdwHeaderBar">
|
||||||
<child type="start">
|
<child type="start">
|
||||||
<object class="GtkButton">
|
<object class="GtkButton" id="model_detail_create_button">
|
||||||
<signal name="clicked" handler="link_button_handler"/>
|
<signal name="clicked" handler="model_detail_create_button_clicked"/>
|
||||||
<property name="icon-name">globe-symbolic</property>
|
<property name="icon-name">edit-copy-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<property name="content">
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwStatusPage" id="model_detail_page">
|
||||||
|
<property name="icon-name">brain-augemnted-symbolic</property>
|
||||||
|
<property name="description">text</property>
|
||||||
|
<style>
|
||||||
|
<class name="compact"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwNavigationPage">
|
||||||
|
<property name="title" translatable="yes">Create Model</property>
|
||||||
|
<property name="tag">model_create_page</property>
|
||||||
|
<property name="child">
|
||||||
|
<object class="AdwToolbarView">
|
||||||
|
<child type="top">
|
||||||
|
<object class="AdwHeaderBar"/>
|
||||||
|
</child>
|
||||||
<property name="content">
|
<property name="content">
|
||||||
<object class="GtkScrolledWindow">
|
<object class="GtkScrolledWindow">
|
||||||
<property name="vexpand">true</property>
|
<property name="vexpand">true</property>
|
||||||
@@ -726,6 +782,9 @@
|
|||||||
<object class="GtkScrolledWindow">
|
<object class="GtkScrolledWindow">
|
||||||
<property name="margin-start">10</property>
|
<property name="margin-start">10</property>
|
||||||
<property name="margin-end">10</property>
|
<property name="margin-end">10</property>
|
||||||
|
<style>
|
||||||
|
<class name="undershoot-bottom"/>
|
||||||
|
</style>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTextView" id="create_model_modelfile">
|
<object class="GtkTextView" id="create_model_modelfile">
|
||||||
<style>
|
<style>
|
||||||
@@ -825,14 +884,12 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTextView" id="file_preview_text_view">
|
<object class="GtkLabel" id="file_preview_text_label">
|
||||||
<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>
|
||||||
<property name="hexpand">true</property>
|
<property name="selectable">true</property>
|
||||||
<property name="vexpand">true</property>
|
|
||||||
<property name="editable">false</property>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@@ -1068,10 +1125,16 @@
|
|||||||
<property name="title" translatable="yes">General</property>
|
<property name="title" translatable="yes">General</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkShortcutsShortcut">
|
<object class="GtkShortcutsShortcut">
|
||||||
<property name="accelerator"><ctrl>W</property>
|
<property name="accelerator"><ctrl>Q</property>
|
||||||
<property name="title" translatable="yes">Close application</property>
|
<property name="title" translatable="yes">Close application</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkShortcutsShortcut">
|
||||||
|
<property name="accelerator"><ctrl>W</property>
|
||||||
|
<property name="title" translatable="yes">Delete current chat</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkShortcutsShortcut">
|
<object class="GtkShortcutsShortcut">
|
||||||
<property name="accelerator"><ctrl>I</property>
|
<property name="accelerator"><ctrl>I</property>
|
||||||
|
|||||||
Reference in New Issue
Block a user