diff --git a/ragna/ui/api_wrapper.py b/ragna/ui/api_wrapper.py index 36927d68..3baca20b 100644 --- a/ragna/ui/api_wrapper.py +++ b/ragna/ui/api_wrapper.py @@ -3,7 +3,8 @@ import emoji -import requests +# import requests +import httpx # The goal is this class is to provide ready-to-use functions to interact with the API @@ -11,10 +12,11 @@ class ApiWrapper: def __init__(self, api_url, user): self.api_url = api_url self.user = user + self.client = httpx.Client() def get_chats(self): # Make a GET request to the API endpoint - response = requests.get(self.api_url + "/chats?user=" + self.user) + response = self.client.get(f"{self.api_url}/chats", params={"user": self.user}) if response.status_code == 200: json_data = response.json() @@ -38,20 +40,23 @@ def get_chats(self): ) def answer(self, chat_id, prompt): - requests.post( + self.client.post( f"{self.api_url}/chats/{chat_id}/start", params={"user": self.user}, ) - raw_result = requests.post( + raw_result = self.client.post( f"{self.api_url}/chats/{chat_id}/answer", params={"user": self.user, "prompt": prompt}, ) return raw_result.json()["message"]["content"] - def get_components(self): - response = requests.get(self.api_url + "/components?user=" + self.user) - return response.json() + async def get_components_async(self): + async with httpx.AsyncClient() as async_client: + response = await async_client.get( + self.api_url + "/components?user=" + self.user + ) + return response.json() def replace_emoji_shortcodes_with_emoji(self, markdown_string): # Define a regular expression pattern to find emoji shortcodes diff --git a/ragna/ui/central_view.py b/ragna/ui/central_view.py index 9c0d8544..90f51275 100644 --- a/ragna/ui/central_view.py +++ b/ragna/ui/central_view.py @@ -2,7 +2,7 @@ import param -pn.widgets.ChatEntry._stylesheets = pn.widgets.ChatEntry._stylesheets + [ +chat_entry_stylesheets = [ """ :host .right, :host .center, :host .chat-entry { width:100% !important; } @@ -29,7 +29,25 @@ border: 1px solid rgb(234, 234, 234); } """, + """ + :host .avatar { + margin-top:0px; + } + """, ] +pn.widgets.ChatEntry._stylesheets = ( + pn.widgets.ChatEntry._stylesheets + chat_entry_stylesheets +) + + +def chat_entry_value_renderer(txt, role): + return pn.pane.Markdown( + txt, + css_classes=["chat-entry-user" if role == "user" else "chat-entry-ragna"], + stylesheets=[ + " \n table {\n margin-top:10px;\n margin-bottom:10px;\n \n }\n " + ], + ) def build_chat_entry(role, txt, timestamp=None): @@ -38,23 +56,7 @@ def build_chat_entry(role, txt, timestamp=None): # user="User" if m["role"] == "user" else "Ragna (Chat GPT 3.5)", # actually looking better with empty user name than show_user=False # show_user=False, - renderers=[ - lambda txt: pn.pane.Markdown( - txt, - css_classes=[ - "chat-entry-user" if role == "user" else "chat-entry-ragna" - ], - stylesheets=[ - """ - table { - margin-top:10px; - margin-bottom:10px; - - } - """ - ], - ) - ], + renderers=[lambda txt: chat_entry_value_renderer(txt, role=role)], css_classes=[ "chat-entry", "chat-entry-user" if role == "user" else "chat-entry-ragna", diff --git a/ragna/ui/chat_config.py b/ragna/ui/chat_config.py deleted file mode 100644 index ba9f52d3..00000000 --- a/ragna/ui/chat_config.py +++ /dev/null @@ -1,50 +0,0 @@ -from typing import Any - -import panel as pn -import param - -# @dataclasses.dataclass -# class AppComponents: -# page_extractors: dict[str, PageExtractor] -# source_storages: dict[str, SourceStorage] -# llms: dict[str, Llm] - - -class ChatConfig(param.Parameterized): - source_storage_name = param.Selector() - llm_name = param.Selector() - extra = param.Dict(default={}) - # components = param.ClassSelector(AppComponents) - - def __init__( - self, - *, - # components, - source_storage_names, - llm_names, - extra, - ): - super().__init__( - extra=extra, - ) - self.param.source_storage_name.objects = source_storage_names - self.source_storage_name = source_storage_names[0] - self.param.llm_name.objects = llm_names - self.llm_name = llm_names[0] - - def __panel__(self): - """Could be left empty to provide no input from users""" - return pn.Column( - pn.widgets.Select.from_param(self.param.source_storage_name), - pn.widgets.Select.from_param(self.param.llm_name), - ) - - def get_config(self) -> tuple[str, str, dict[str, Any]]: - return { - "source_storage_name": self.source_storage_name, - "llm_name": self.llm_name, - "extra": {}, - } - - def __repr__(self) -> str: - return "DemoConfig" diff --git a/ragna/ui/js.py b/ragna/ui/js.py index f37d997e..e8e9f8c3 100644 --- a/ragna/ui/js.py +++ b/ragna/ui/js.py @@ -6,7 +6,8 @@ document.getElementById("pn-Modal").style.setProperty( "--dialog-height", "{MODAL_MIN_HEIGHT}ex", - "important") + "important"); + """ TOGGLE_CARD = f""" diff --git a/ragna/ui/main_page.py b/ragna/ui/main_page.py index 8f5d629b..fbc9c7b6 100644 --- a/ragna/ui/main_page.py +++ b/ragna/ui/main_page.py @@ -5,10 +5,7 @@ import ragna.ui.styles as ui from ragna.ui.central_view import CentralView - -from ragna.ui.chat_config import ChatConfig from ragna.ui.left_sidebar import LeftSidebar - from ragna.ui.modal import ModalConfiguration from ragna.ui.right_sidebar import RightSidebar @@ -20,21 +17,24 @@ def __init__(self, api_wrapper): super().__init__() self.api_wrapper = api_wrapper + self.modal = None self.central_view = None self.left_sidebar = None self.right_sidebar = None # Modal and callbacks def open_modal(self, template): - self.modal = ModalConfiguration( - chat_configs=[], # self.chat_configs, - start_button_callback=lambda event, template=template: self.on_click_start_conv_button( - event, template - ), - cancel_button_callback=lambda event, template=template: self.on_click_cancel_button( - event, template - ), - ) + if self.modal is None: + self.modal = ModalConfiguration( + api_wrapper=self.api_wrapper, + start_button_callback=lambda event, template=template: self.on_click_start_conv_button( + event, template + ), + cancel_button_callback=lambda event, template=template: self.on_click_cancel_button( + event, template + ), + ) + template.modal.objects[0].objects = [self.modal] template.open_modal() @@ -66,17 +66,6 @@ def page(self): on_error=lambda x: print(f"error sync on {x}"), ) - # TODO : retrieve this from the API - print(self.api_wrapper.get_components()) - self.chat_configs = [ - ChatConfig( - # components=self.components, - source_storage_names=["source", "source2"], - llm_names=["gpt"], - extra={"some config": "value", "other_config": 42}, - ) - ] - 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 diff --git a/ragna/ui/modal.py b/ragna/ui/modal.py index c3ca1e72..1984c03f 100644 --- a/ragna/ui/modal.py +++ b/ragna/ui/modal.py @@ -4,6 +4,7 @@ import param import ragna.ui.js as js +import ragna.ui.styles as ui def get_default_chat_name(timezone_offset=None): @@ -15,14 +16,18 @@ def get_default_chat_name(timezone_offset=None): class ModalConfiguration(pn.viewable.Viewer): - chat_configs = param.List() chat_name = param.String() start_button_callback = param.Callable() cancel_button_callback = param.Callable() # + source_storage_name = param.Selector() + assistant_name = param.Selector() - def __init__(self, **params): + def __init__(self, api_wrapper, **params): super().__init__(chat_name=get_default_chat_name(), **params) + + self.api_wrapper = api_wrapper + # TODO: sort out documents within this class self.document_uploader = pn.widgets.FileInput( @@ -62,6 +67,29 @@ def __init__(self, **params): self.got_timezone = False + async def model_section(self): + components = await self.api_wrapper.get_components_async() + + self.param.assistant_name.objects = components["assistants"] + self.param.source_storage_name.objects = components["source_storages"] + + if len(components["assistants"]) > 0: + self.assistant_name = components["assistants"][0] + + if len(components["source_storages"]) > 0: + self.source_storage_name = components["source_storages"][0] + + return pn.Row( + pn.Column( + pn.pane.HTML("Model"), + pn.widgets.Select.from_param(self.param.assistant_name, name=""), + ), + pn.Column( + pn.pane.HTML("Source storage"), + pn.widgets.Select.from_param(self.param.source_storage_name, name=""), + ), + ) + def __panel__(self): def divider(): return pn.layout.Divider(styles={"padding": "0em 2em 0em 2em"}) @@ -69,7 +97,7 @@ def divider(): return pn.Column( pn.pane.HTML( f"""

Start a new chat

- Setup the configurations for your new chat.
+ Let's set up the configurations for your new chat !
""", ), @@ -84,12 +112,12 @@ def divider(): show_name=False, ), divider(), - *self.chat_configs, + self.model_section, divider(), self.upload_files_label, self.upload_row, pn.Row(self.cancel_button, self.start_chat_button), - min_width=800, + min_width=ui.MODAL_WIDTH, sizing_mode="stretch_both", height_policy="max", ) diff --git a/ragna/ui/styles.py b/ragna/ui/styles.py index aefd0dbf..4fd3f339 100644 --- a/ragna/ui/styles.py +++ b/ragna/ui/styles.py @@ -13,6 +13,7 @@ # set modal height (in x heights) MODAL_MIN_HEIGHT = 70 MODAL_MAX_HEIGHT = 300 +MODAL_WIDTH = 800 APP_RAW = """ @@ -46,4 +47,17 @@ div#content { height: calc(100vh); } + +/* Fix the size of the modal */ +#pn-Modal { + --dialog-width: 800px !important; + --dialog-height:500px !important; +} + +/* Hide the default close button of the modal */ +.pn-modal-close { + display: none !important; +} + + """