Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file selector widget #285

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions ragna/deploy/_ui/app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from pathlib import Path
from urllib.parse import urlsplit

Expand All @@ -24,8 +25,8 @@

HERE = Path(__file__).parent
# CSS = HERE / "css"
IMGS = HERE / "imgs"
RES = HERE / "resources"
IMGS = Path(os.environ.get("RAGNA_UI_IMG_DIR", HERE / "imgs"))
RES = Path(os.environ.get("RAGNA_UI_RES_DIR", HERE / "resources"))


class App(param.Parameterized):
Expand All @@ -40,14 +41,26 @@ def get_template(self):
template = pn.template.FastListTemplate(
# We need to set a title to have it appearing on the browser's tab
# but it means we need to hide it from the header bar
title="Ragna",
title="",
accent_base_color=ui.MAIN_COLOR,
theme_toggle=False,
collapsed_sidebar=True,
header=[
pn.pane.PNG(IMGS / "test_logo.png", width=100),
pn.pane.HTML(
"BX AI",
style={
"font-size": "25px",
"margin-top": "20px",
"font-weight": "300",
},
),
],
# main_layout=None
raw_css=[ui.APP_RAW],
favicon="imgs/ragna_logo.svg",
css_files=["https://rsms.me/", "https://rsms.me/inter/inter.css"],
busy_indicator=None,
css_files=["https://fonts.cdnfonts.com/css/guardian-sans"],
)

template.modal.objects = [
Expand Down
2 changes: 1 addition & 1 deletion ragna/deploy/_ui/central_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ def __panel__(self):
""" :host {
background-color: #F9F9F9;

height:100%;
height:95%;
max-width: 100%;
margin-left: min(15px, 2%);
border-left: 1px solid #EEEEEE;
Expand Down
33 changes: 32 additions & 1 deletion ragna/deploy/_ui/left_sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def __init__(self, api_wrapper, **params):
self.on_click_chat = None
self.on_click_new_chat = None

self.clicked_on_upload_files = False

self.chat_buttons = []

pn.state.location.sync(
Expand All @@ -30,6 +32,15 @@ def trigger_on_click_new_chat(self, event):
if self.on_click_new_chat is not None:
self.on_click_new_chat(event)

def trigger_on_click_upload_files(self, event):
if event.old > event.new:
return

self.clicked_on_upload_files = True

if self.on_click_new_chat is not None:
self.on_click_new_chat(event)

def on_click_chat_wrapper(self, event, chat):
# This is a hack to avoid the event being triggered twice in a row
if event.old > event.new:
Expand Down Expand Up @@ -154,6 +165,26 @@ def __panel__(self):
],
)

upload_files_button = pn.widgets.Button(
name="Upload Files",
button_type="default",
icon="plus",
stylesheets=[
"""
:host {
width: 90%;
margin-left: 10px;
margin-top: 10px;
}
:host div button {
text-align: left;
}
"""
],
)

upload_files_button.on_click(self.trigger_on_click_upload_files)

new_chat_button = pn.widgets.Button(
name="New Chat",
button_type="primary",
Expand All @@ -176,7 +207,7 @@ def __panel__(self):
new_chat_button.on_click(self.trigger_on_click_new_chat)

objects = (
[header, new_chat_button]
[header, upload_files_button, new_chat_button]
+ self.chat_buttons
+ [
pn.layout.VSpacer(),
Expand Down
3 changes: 3 additions & 0 deletions ragna/deploy/_ui/main_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def after_update_chats(self):
def open_modal(self):
self.modal = ModalConfiguration(
api_wrapper=self.api_wrapper,
clicked_on_upload_files=self.left_sidebar.clicked_on_upload_files,
new_chat_ready_callback=self.open_new_chat,
cancel_button_callback=self.on_click_cancel_button,
)
Expand All @@ -83,9 +84,11 @@ async def open_new_chat(self, new_chat_id):
self.current_chat_id = new_chat_id
await self.refresh_data()

self.left_sidebar.clicked_on_upload_files = False
self.template.close_modal()

def on_click_cancel_button(self, event):
self.left_sidebar.clicked_on_upload_files = False
self.template.close_modal()

# Left sidebar callbacks
Expand Down
165 changes: 105 additions & 60 deletions ragna/deploy/_ui/modal_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@ class ModalConfiguration(pn.viewable.Viewer):

advanced_config_collapsed = param.Boolean(default=True)

def __init__(self, api_wrapper, **params):
def __init__(self, api_wrapper, clicked_on_upload_files, **params):
super().__init__(chat_name=get_default_chat_name(), **params)

self.api_wrapper = api_wrapper

self.clicked_on_upload_files = clicked_on_upload_files

upload_endpoints = self.api_wrapper.upload_endpoints()

self.chat_name_input = pn.widgets.TextInput.from_param(
Expand All @@ -94,6 +96,8 @@ def __init__(self, api_wrapper, **params):
upload_endpoints["informations_endpoint"],
)

self.file_widget = pn.widgets.FileSelector()

# Most widgets (including those that use from_param) should be placed after the super init call
self.cancel_button = pn.widgets.Button(
name="Cancel", button_type="default", min_width=375
Expand All @@ -108,15 +112,18 @@ def __init__(self, api_wrapper, **params):
self.upload_files_label = pn.pane.HTML()
self.change_upload_files_label()

self.upload_row = pn.Row(
self.document_uploader,
sizing_mode="stretch_width",
stylesheets=[""" :host { margin-bottom: 20px; } """],
)

self.got_timezone = False

def did_click_on_start_chat_button(self, event):
if self.clicked_on_upload_files:
# close your eyes and pray
files = {}
files["name"] = self.file_widget.value
files["size"] = "0"
files["type"] = "text/plain"
files["lastModified"] = "1519211809934"
self.document_uploader.file_list = [files]

if not self.document_uploader.can_proceed_to_upload():
self.change_upload_files_label("missing_file")
else:
Expand All @@ -127,24 +134,28 @@ async def did_finish_upload(self, uploaded_documents):
# at this point, the UI has uploaded the files to the API.
# We can now start the chat

try:
new_chat_id = await self.api_wrapper.start_and_prepare(
name=self.chat_name,
documents=uploaded_documents,
source_storage=self.config.source_storage_name,
assistant=self.config.assistant_name,
params=self.config.to_params_dict(),
)

self.start_chat_button.disabled = False
if not self.clicked_on_upload_files:
try:
new_chat_id = await self.api_wrapper.start_and_prepare(
name=self.chat_name,
documents=uploaded_documents,
source_storage=self.config.source_storage_name,
assistant=self.config.assistant_name,
params=self.config.to_params_dict(),
)
self.start_chat_button.disabled = False

if self.new_chat_ready_callback is not None:
await self.new_chat_ready_callback(new_chat_id)

except Exception:
self.change_upload_files_label("upload_error")
self.document_uploader.loading = False
except Exception:
if not self.clicked_on_upload_files:
self.change_upload_files_label("upload_error")
self.document_uploader.loading = False
self.start_chat_button.disabled = False

else:
self.start_chat_button.disabled = False
await self.new_chat_ready_callback("")

def change_upload_files_label(self, mode="normal"):
if mode == "upload_error":
Expand Down Expand Up @@ -184,24 +195,29 @@ async def model_section(self):
self.config = config
self.document_uploader.allowed_documents = config.allowed_documents

return pn.Row(
pn.Column(
pn.pane.HTML("<b>Assistants</b>"),
pn.widgets.Select.from_param(
self.config.param.assistant_name,
name="",
stylesheets=[ui.BK_INPUT_GRAY_BORDER],
# the config is currently coupled to this function, so we need to call it
# for the upload file modal even though we don't need a model section there
if self.clicked_on_upload_files:
return
else:
return pn.Row(
pn.Column(
pn.pane.HTML("<b>Assistants</b>"),
pn.widgets.Select.from_param(
self.config.param.assistant_name,
name="",
stylesheets=[ui.BK_INPUT_GRAY_BORDER],
),
),
),
pn.Column(
pn.pane.HTML("<b>Source storage</b>"),
pn.widgets.Select.from_param(
self.config.param.source_storage_name,
name="",
stylesheets=[ui.BK_INPUT_GRAY_BORDER],
pn.Column(
pn.pane.HTML("<b>Source storage</b>"),
pn.widgets.Select.from_param(
self.config.param.source_storage_name,
name="",
stylesheets=[ui.BK_INPUT_GRAY_BORDER],
),
),
),
)
)

@pn.depends("config", "config.assistant_name", "config.source_storage_name")
def advanced_config_ui(self):
Expand Down Expand Up @@ -318,26 +334,55 @@ def toggle_card(event):
return pn.Column(toggle_button, card)

def __panel__(self):
return pn.Column(
pn.pane.HTML(
f"""<h2>Start a new chat</h2>
Let's set up the configurations for your new chat !<br />
<script>{js.reset_modal_size(ui.CONFIG_MODAL_WIDTH, ui.CONFIG_MODAL_MIN_HEIGHT)}</script>
""",
),
ui.divider(),
pn.pane.HTML("<b>Chat name</b>"),
self.chat_name_input,
ui.divider(),
self.model_section,
ui.divider(),
self.advanced_config_ui,
ui.divider(),
self.upload_files_label,
self.upload_row,
pn.Row(self.cancel_button, self.start_chat_button),
min_height=ui.CONFIG_MODAL_MIN_HEIGHT,
min_width=ui.CONFIG_MODAL_WIDTH,
sizing_mode="stretch_both",
height_policy="max",
)
# side-effect nirvana
not_visible = pn.Row(self.document_uploader)
not_visible.visible = False

if self.clicked_on_upload_files:
self.start_chat_button.name = "Select Files"
return pn.Column(
pn.pane.HTML(
f"""<h2>Select files</h2>
Select local files to be used for future chats. <br />
<script>{js.reset_modal_size(ui.FILE_MODAL_WIDTH, ui.FILE_MODAL_MIN_HEIGHT)}</script>
""",
),
ui.divider(),
self.model_section,
self.upload_files_label,
self.file_widget,
pn.Row(self.cancel_button, self.start_chat_button),
not_visible,
min_height=ui.FILE_MODAL_MIN_HEIGHT,
min_width=ui.FILE_MODAL_WIDTH,
sizing_mode="stretch_both",
height_policy="max",
)
else:
return pn.Column(
pn.pane.HTML(
f"""<h2>Start a new chat</h2>
Let's set up the configurations for your new chat !<br />
<script>{js.reset_modal_size(ui.CONFIG_MODAL_WIDTH, ui.CONFIG_MODAL_MIN_HEIGHT)}</script>
""",
),
ui.divider(),
pn.pane.HTML("<b>Chat name</b>"),
self.chat_name_input,
ui.divider(),
self.model_section,
ui.divider(),
self.advanced_config_ui,
ui.divider(),
self.upload_files_label,
pn.Row(
self.document_uploader,
sizing_mode="stretch_width",
stylesheets=[""" :host { margin-bottom: 20px; } """],
),
pn.Row(self.cancel_button, self.start_chat_button),
min_height=ui.CONFIG_MODAL_MIN_HEIGHT,
min_width=ui.CONFIG_MODAL_WIDTH,
sizing_mode="stretch_both",
height_policy="max",
)
Loading
Loading