New big feature: Attach website (experimental)

This commit is contained in:
jeffser 2024-06-30 23:24:29 -06:00
parent 2c2a1ac2bc
commit 2ded3c72fd
3 changed files with 71 additions and 6 deletions

View File

@ -99,6 +99,20 @@
} }
] ]
}, },
{
"name": "python3-html2text",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"html2text\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/1a/43/e1d53588561e533212117750ee79ad0ba02a41f52a08c1df3396bd466c05/html2text-2024.2.26.tar.gz",
"sha256": "05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32"
}
]
},
{ {
"name": "ollama", "name": "ollama",
"buildsystem": "simple", "buildsystem": "simple",

View File

@ -3,6 +3,8 @@
from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf
import os import os
from pytube import YouTube from pytube import YouTube
from html2text import html2text
from . import connection_handler
# CLEAR CHAT | WORKS # CLEAR CHAT | WORKS
@ -312,3 +314,35 @@ def youtube_caption(self, video_url):
callback = lambda dialog, task, video_url = video_url, caption_drop_down = caption_drop_down: youtube_caption_response(self, dialog, task, video_url, caption_drop_down) callback = lambda dialog, task, video_url = video_url, caption_drop_down = caption_drop_down: youtube_caption_response(self, dialog, task, video_url, caption_drop_down)
) )
# Website extraction |
def attach_website_response(self, dialog, task, url):
if dialog.choose_finish(task) == "accept":
html = connection_handler.simple_get(url)['text']
md = html2text(html)
buffer = self.message_text_view.get_buffer()
textview_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False).replace(url, "")
buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
buffer.insert(buffer.get_start_iter(), textview_text, len(textview_text))
if not os.path.exists('/tmp/alpaca/websites/'):
os.makedirs('/tmp/alpaca/websites/')
md_name = self.generate_numbered_name('website.md', os.listdir('/tmp/alpaca/websites'))
file_path = os.path.join('/tmp/alpaca/websites/', md_name)
with open(file_path, 'w+') as f:
f.write('{}\n\n{}'.format(url, md))
self.attach_file(file_path, 'website')
def attach_website(self, url):
dialog = Adw.AlertDialog(
heading=_("Attach Website (Experimental)"),
body=_("Are you sure you want to attach\n'{}'?").format(url),
close_response="cancel"
)
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, url=url: attach_website_response(self, dialog, task, url)
)

View File

@ -196,7 +196,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
self.save_history() self.save_history()
self.show_toast("info", 6, self.main_overlay) self.show_toast("info", 6, self.main_overlay)
if self.bot_message: return if self.bot_message or self.get_focus() not in (self.message_text_view, self.send_button): return
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 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_chat_row = self.chat_list_box.get_selected_row() current_chat_row = self.chat_list_box.get_selected_row()
self.chat_list_box.unselect_all() self.chat_list_box.unselect_all()
@ -514,6 +514,8 @@ class AlpacaWindow(Adw.ApplicationWindow):
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])
elif file_type == 'website':
self.file_preview_open_button.set_name(content.split('\n')[0])
else: else:
self.file_preview_dialog.set_title(os.path.basename(file_path)) self.file_preview_dialog.set_title(os.path.basename(file_path))
self.file_preview_open_button.set_name(file_path) self.file_preview_open_button.set_name(file_path)
@ -545,7 +547,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
Generate a title following these rules: Generate a title following these rules:
- The title should be based on the prompt at the end - The title should be based on the prompt at the end
- Keep it in the same language as the prompt - Keep it in the same language as the prompt
- The title needs to be less than 4 words - The title needs to be less than 30 characters
- Use only alphanumeric characters and spaces - Use only alphanumeric characters and spaces
- Just write the title, NOTHING ELSE - Just write the title, NOTHING ELSE
@ -558,6 +560,7 @@ Generate a title following these rules:
if 'images' in message: data["images"] = message['images'] if 'images' in message: data["images"] = message['images']
response = connection_handler.simple_post(f"{connection_handler.url}/api/generate", data=json.dumps(data)) response = connection_handler.simple_post(f"{connection_handler.url}/api/generate", data=json.dumps(data))
new_chat_name = json.loads(response['text'])["response"].lstrip().rstrip().replace('"', '').replace("'", "").title() new_chat_name = json.loads(response['text'])["response"].lstrip().rstrip().replace('"', '').replace("'", "").title()
new_chat_name = new_chat_name[:30] + (new_chat_name[30:] and '...')
self.rename_chat(label_element.get_name(), new_chat_name, label_element) self.rename_chat(label_element.get_name(), new_chat_name, label_element)
def show_message(self, msg:str, bot:bool, footer:str=None, images:list=None, files:dict=None, id:str=None): def show_message(self, msg:str, bot:bool, footer:str=None, images:list=None, files:dict=None, id:str=None):
@ -660,7 +663,12 @@ Generate a title following these rules:
for name, file_type in files.items(): for name, file_type in files.items():
button_content = Adw.ButtonContent( button_content = Adw.ButtonContent(
label=name, label=name,
icon_name="play-symbolic" if file_type=='youtube' else "document-text-symbolic" icon_name={
"plain_text": "document-text-symbolic",
"pdf": "document-text-symbolic",
"youtube": "play-symbolic",
"website": "globe-symbolic"
}[file_type]
) )
button = Gtk.Button( button = Gtk.Button(
vexpand=False, vexpand=False,
@ -671,7 +679,6 @@ Generate a title following these rules:
child=button_content child=button_content
) )
file_path = os.path.join(self.data_dir, "chats", "{selected_chat}", id, name) file_path = os.path.join(self.data_dir, "chats", "{selected_chat}", id, name)
print(file_path)
button.connect("clicked", lambda button, file_path=file_path, file_type=file_type: self.preview_file(file_path, file_type)) button.connect("clicked", lambda button, file_path=file_path, file_type=file_type: self.preview_file(file_path, file_type))
file_container.append(button) file_container.append(button)
message_box.append(file_scroller) message_box.append(file_scroller)
@ -1308,7 +1315,7 @@ Generate a title following these rules:
return base64.b64encode(image_data).decode("utf-8") return base64.b64encode(image_data).decode("utf-8")
except Exception as e: except Exception as e:
self.show_toast("error", 5, self.main_overlay) self.show_toast("error", 5, self.main_overlay)
elif file_type == 'plain_text' or file_type == 'youtube': elif file_type == 'plain_text' or file_type == 'youtube' or file_type == 'website':
with open(file_path, 'r') as f: with open(file_path, 'r') as f:
return f.read() return f.read()
elif file_type == 'pdf': elif file_type == 'pdf':
@ -1334,7 +1341,8 @@ Generate a title following these rules:
"image": "image-x-generic-symbolic", "image": "image-x-generic-symbolic",
"plain_text": "document-text-symbolic", "plain_text": "document-text-symbolic",
"pdf": "document-text-symbolic", "pdf": "document-text-symbolic",
"youtube": "play-symbolic" "youtube": "play-symbolic",
"website": "globe-symbolic"
}[file_type] }[file_type]
) )
button = Gtk.Button( button = Gtk.Button(
@ -1372,11 +1380,20 @@ Generate a title following these rules:
youtube_regex = re.compile( youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/' r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})') r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})')
url_regex = re.compile(
r'http[s]?://'
r'(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|'
r'(?:%[0-9a-fA-F][0-9a-fA-F]))+'
r'(?:\\:[0-9]{1,5})?'
r'(?:/[^\\s]*)?'
)
if youtube_regex.match(text): if youtube_regex.match(text):
try: try:
dialogs.youtube_caption(self, text) dialogs.youtube_caption(self, text)
except Exception as e: except Exception as e:
self.show_toast("error", 10, self.main_overlay) self.show_toast("error", 10, self.main_overlay)
elif url_regex.match(text):
dialogs.attach_website(self, text)
except Exception as e: 'huh' except Exception as e: 'huh'
def cb_image_received(self, clipboard, result): def cb_image_received(self, clipboard, result):