Feat/new UI (#13)

* new custom theme

* improve css: scrollbar, header, tabs and buttons

* update settings tab

* open file index selector by default

* update chat control panel

* update chat panel

* update file index page

* cap gradio<=4.22.0

* rename admin page

* adjust UI

* update flowsettings

* auto start in browser

* change colour for edit LLM page's button
This commit is contained in:
ian_Cin 2024-04-08 22:23:00 +07:00 committed by GitHub
parent a203fc0f7c
commit 8001c86b16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 239 additions and 145 deletions

View File

@ -22,7 +22,7 @@ dependencies = [
"theflow",
"llama-index>=0.9.0,<0.10.0",
"llama-hub",
"gradio>=4.0.0",
"gradio>=4.0.0,<=4.22.0",
"openpyxl",
"cookiecutter",
"click",

View File

@ -73,30 +73,29 @@ if config("AZURE_OPENAI_API_KEY", default="") and config(
if config("OPENAI_API_KEY", default=""):
KH_LLMS["openai"] = {
"def": {
"spec": {
"__type__": "kotaemon.llms.ChatOpenAI",
"temperature": 0,
"openai_api_base": config("OPENAI_API_BASE", default="")
"base_url": config("OPENAI_API_BASE", default="")
or "https://api.openai.com/v1",
"openai_api_key": config("OPENAI_API_KEY", default=""),
"api_key": config("OPENAI_API_KEY", default=""),
"model": config("OPENAI_CHAT_MODEL", default="") or "gpt-3.5-turbo",
"request_timeout": 10,
"stream": False,
"timeout": 10,
},
"default": False,
}
if len(KH_EMBEDDINGS) < 1:
KH_EMBEDDINGS["openai"] = {
"def": {
"spec": {
"__type__": "kotaemon.embeddings.OpenAIEmbeddings",
"openai_api_base": config("OPENAI_API_BASE", default="")
"base_url": config("OPENAI_API_BASE", default="")
or "https://api.openai.com/v1",
"openai_api_key": config("OPENAI_API_KEY", default=""),
"api_key": config("OPENAI_API_KEY", default=""),
"model": config(
"OPENAI_EMBEDDINGS_MODEL", default="text-embedding-ada-002"
)
or "text-embedding-ada-002",
"request_timeout": 10,
"timeout": 10,
"chunk_size": 16,
},
"default": False,
@ -104,7 +103,7 @@ if config("OPENAI_API_KEY", default=""):
if config("LOCAL_MODEL", default=""):
KH_LLMS["local"] = {
"def": {
"spec": {
"__type__": "kotaemon.llms.EndpointChatLLM",
"endpoint_url": "http://localhost:31415/v1/chat/completions",
},
@ -113,7 +112,7 @@ if config("LOCAL_MODEL", default=""):
}
if len(KH_EMBEDDINGS) < 1:
KH_EMBEDDINGS["local"] = {
"def": {
"spec": {
"__type__": "kotaemon.embeddings.EndpointEmbeddings",
"endpoint_url": "http://localhost:31415/v1/embeddings",
},
@ -164,10 +163,4 @@ KH_INDICES = [
"config": {},
"index_type": "ktem.index.file.FileIndex",
},
{
"id": 2,
"name": "Sample",
"config": {},
"index_type": "ktem.index.file.FileIndex",
},
]

View File

@ -36,10 +36,7 @@ class BaseApp:
def __init__(self):
self.dev_mode = getattr(settings, "KH_MODE", "") == "dev"
self.f_user_management = getattr(settings, "KH_FEATURE_USER_MANAGEMENT", False)
self._theme = gr.themes.Base(
font=("ui-sans-serif", "system-ui", "sans-serif"),
font_mono=("ui-monospace", "Consolas", "monospace"),
)
self._theme = gr.Theme.from_hub("lone17/kotaemon")
dir_assets = Path(__file__).parent / "assets"
with (dir_assets / "css" / "main.css").open() as fi:

View File

@ -1,55 +1,91 @@
/* no footer */
footer {
display: none !important;
display: none !important;
}
/* customize scrollbar */
::-webkit-scrollbar {
background: var(--background-fill-primary);
}
::-webkit-scrollbar-thumb {
background-color: var(--border-color-primary);
border: 4px solid transparent;
border-radius: 100px;
background-clip: content-box;
}
::-webkit-scrollbar-corner {
background: var(--border-color-primary);
}
.gradio-container {
max-width: 100% !important;
padding: 0 !important;
max-width: 100% !important;
}
/* styling for header bar */
.header-bar {
background-color: #f7f7f7;
margin: 0px 0px 20px;
overflow-x: scroll;
display: block !important;
text-wrap: nowrap;
background-color: transparent;
margin: 0px 0px 20px;
overflow-x: scroll;
display: block !important;
text-wrap: nowrap;
border: none;
}
.dark .header-bar {
border: none !important;
background-color: #8080802b !important;
}
.header-bar button.selected {
border-radius: 0;
border: none;
/* an alternative header bar style with rounded background */
/* background-color: var(--background-fill-primary);
border: 4px solid transparent;
border-radius: var(--radius-lg);
background-clip: padding-box; */
}
.indices-tab {
border: none !important;
/* selected buttons have highlighted text */
button.selected {
color: var(--block-label-text-color);
font-weight: bold;
}
#chat-tab, #settings-tab, #help-tab, #admin-tab, #login-tab {
border: none !important;
#chat-tab,
#settings-tab,
#help-tab,
#resources-tab,
#login-tab {
border: none !important;
}
#help-tab,
#settings-tab {
/* text-dense view should not be wide for readability */
max-width: max(56vw, 900px) !important;
margin: 0 auto !important;
}
.indices-tab,
#resources-tab {
/* Other view should not be too wide */
border: none !important;
max-width: max(70vw, 1200px) !important;
margin: 0 auto !important;
}
#main-chat-bot {
height: calc(100vh - 140px) !important;
/* span the chat area to occupy full height minus space for chat input */
height: calc(100vh - 180px) !important;
}
#chat-info-panel {
max-height: calc(100vh - 140px) !important;
overflow-y: scroll !important;
max-height: calc(100vh - 180px) !important;
overflow-y: scroll !important;
}
.setting-answer-mode-description {
margin: 5px 5px 2px !important
margin: 5px 5px 2px !important;
}
mark {
*/ mark {
background-color: #1496bb;
}
/* clpse */
.clpse {
background-color: var(--background-fill-secondary);
@ -61,3 +97,33 @@ mark {
text-align: left;
outline: none;
}
/* for setting transparent background for elements */
.no-background {
background-color: transparent;
border: none;
}
/* for setting bold text for elements */
.bold-text {
font-weight: bold;
}
/* for setting highlighted text for elements */
.body-text-color {
color: var(--body-text-color);
}
/* for setting right-aligned buttons */
.right-button {
min-width: 200px !important;
width: fit-content;
padding-left: 20px;
padding-right: 20px;
margin: 0px 0px 0px auto;
}
/* for setting height limit for buttons */
.cap-height {
max-height: 42px;
}

View File

@ -97,49 +97,66 @@ class FileIndexPage(BasePage):
def on_building_ui(self):
"""Build the UI of the app"""
with gr.Accordion(label="File upload", open=True) as self.upload:
msg = self.upload_instruction()
if msg:
gr.Markdown(msg)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("## File Upload")
with gr.Column() as self.upload:
msg = self.upload_instruction()
if msg:
gr.Markdown(msg)
self.files = File(
file_types=self._supported_file_types,
file_count="multiple",
container=False,
)
with gr.Accordion("Advanced indexing options", open=False):
with gr.Row():
self.reindex = gr.Checkbox(
value=False, label="Force reindex file", container=False
self.files = File(
file_types=self._supported_file_types,
file_count="multiple",
container=True,
)
with gr.Accordion("Advanced indexing options", open=True):
with gr.Row():
self.reindex = gr.Checkbox(
value=False, label="Force reindex file", container=False
)
self.upload_button = gr.Button(
"Upload and Index", variant="primary"
)
self.file_output = gr.File(
visible=False, label="Output files (debug purpose)"
)
self.upload_button = gr.Button("Upload and Index")
self.file_output = gr.File(
visible=False, label="Output files (debug purpose)"
)
with gr.Column(scale=4):
gr.Markdown("## File List")
self.file_list_state = gr.State(value=None)
self.file_list = gr.DataFrame(
headers=["id", "name", "size", "text_length", "date_created"],
interactive=False,
)
gr.Markdown("## File list")
self.file_list_state = gr.State(value=None)
self.file_list = gr.DataFrame(
headers=["id", "name", "size", "text_length", "date_created"],
interactive=False,
)
with gr.Row() as self.selection_info:
self.selected_file_id = gr.State(value=None)
with gr.Column(scale=2):
self.selected_panel = gr.Markdown(self.selected_panel_false)
with gr.Column(scale=1):
self.deselect_button = gr.Button(
"Deselect",
scale=1,
visible=False,
elem_classes=["right-button"],
)
with gr.Row() as self.selection_info:
self.selected_file_id = gr.State(value=None)
self.selected_panel = gr.Markdown(self.selected_panel_false)
self.deselect_button = gr.Button("Deselect", visible=False)
with gr.Row() as self.tools:
with gr.Column():
self.view_button = gr.Button("View Text (WIP)")
with gr.Column():
self.delete_button = gr.Button("Delete")
with gr.Row():
self.delete_yes = gr.Button(
"Confirm Delete", variant="primary", visible=False
)
self.delete_no = gr.Button("Cancel", visible=False)
self.delete_button = gr.Button(
"Delete", variant="stop", elem_classes=["right-button"]
)
self.delete_yes = gr.Button(
"Confirm Delete",
variant="stop",
visible=False,
elem_classes=["right-button"],
)
self.delete_no = gr.Button(
"Cancel",
visible=False,
elem_classes=["right-button"],
)
def on_subscribe_public_events(self):
"""Subscribe to the declared public event of the app"""

View File

@ -52,13 +52,17 @@ class LLMManagement(BasePage):
with gr.Row(visible=False) as self._selected_panel_btn:
with gr.Column():
self.btn_edit_save = gr.Button("Save", min_width=10)
self.btn_edit_save = gr.Button(
"Save", min_width=10, variant="primary"
)
with gr.Column():
self.btn_delete = gr.Button("Delete", min_width=10)
self.btn_delete = gr.Button(
"Delete", min_width=10, variant="stop"
)
with gr.Row():
self.btn_delete_yes = gr.Button(
"Confirm delete",
variant="primary",
"Confirm Delete",
variant="stop",
visible=False,
min_width=10,
)
@ -98,7 +102,7 @@ class LLMManagement(BasePage):
"by default across the application."
),
)
self.btn_new = gr.Button("Create LLM")
self.btn_new = gr.Button("Add LLM", variant="primary")
with gr.Column(scale=3):
self.spec_desc = gr.Markdown(self.spec_desc_default)

View File

@ -53,11 +53,11 @@ class App(BaseApp):
setattr(self, f"_index_{index.id}", page)
with gr.Tab(
"Admin",
elem_id="admin-tab",
id="admin-tab",
"Resources",
elem_id="resources-tab",
id="resources-tab",
visible=not self.f_user_management,
) as self._tabs["admin-tab"]:
) as self._tabs["resources-tab"]:
self.admin_page = AdminPage(self)
with gr.Tab(
@ -111,7 +111,7 @@ class App(BaseApp):
for k in self._tabs.keys():
if k == "login-tab":
tabs_update.append(gr.update(visible=False))
elif k == "admin-tab":
elif k == "resources-tab":
tabs_update.append(gr.update(visible=is_admin))
else:
tabs_update.append(gr.update(visible=True))

View File

@ -41,7 +41,7 @@ class ChatPage(BasePage):
continue
index_ui.unrender() # need to rerender later within Accordion
with gr.Accordion(label=f"{index.name} Index", open=False):
with gr.Accordion(label=f"{index.name} Index", open=True):
index_ui.render()
gr_index = index_ui.as_gradio_component()
if gr_index:

View File

@ -9,17 +9,33 @@ class ChatPanel(BasePage):
def on_building_ui(self):
self.chatbot = gr.Chatbot(
label="Kotaemon",
# placeholder="This is the beginning of a new conversation.",
show_label=True,
elem_id="main-chat-bot",
show_copy_button=True,
likeable=True,
show_label=False,
bubble_full_width=False,
)
with gr.Row():
self.text_input = gr.Text(
placeholder="Chat input", scale=15, container=False
placeholder="Chat input",
scale=15,
container=False,
)
self.submit_btn = gr.Button(
value="Send",
scale=1,
min_width=10,
variant="primary",
elem_classes=["cap-height"],
)
self.regen_btn = gr.Button(
value="Regen",
scale=1,
min_width=10,
elem_classes=["cap-height"],
)
self.submit_btn = gr.Button(value="Send", scale=1, min_width=10)
self.regen_btn = gr.Button(value="Regen", scale=1, min_width=10)
def submit_msg(self, chat_input, chat_history):
"""Submit a message to the chatbot"""

View File

@ -29,47 +29,42 @@ class ConversationControl(BasePage):
self.on_building_ui()
def on_building_ui(self):
with gr.Accordion(label="Conversation control", open=True):
self.conversation_id = gr.State(value="")
self.conversation = gr.Dropdown(
label="Chat sessions",
choices=[],
gr.Markdown("## Conversations")
self.conversation_id = gr.State(value="")
self.conversation = gr.Dropdown(
label="Chat sessions",
choices=[],
container=False,
filterable=False,
interactive=True,
)
with gr.Row() as self._new_delete:
self.btn_new = gr.Button(value="New", min_width=10, variant="primary")
self.btn_del = gr.Button(value="Delete", min_width=10, variant="stop")
with gr.Row(visible=False) as self._delete_confirm:
self.btn_del_conf = gr.Button(
value="Delete",
variant="stop",
min_width=10,
)
self.btn_del_cnl = gr.Button(value="Cancel", min_width=10)
with gr.Row():
self.conversation_rn = gr.Text(
placeholder="Conversation name",
container=False,
filterable=False,
scale=5,
min_width=10,
interactive=True,
)
with gr.Row() as self._new_delete:
self.btn_new = gr.Button(value="New", min_width=10)
self.btn_del = gr.Button(value="Delete", min_width=10)
with gr.Row(visible=False) as self._delete_confirm:
self.btn_del_conf = gr.Button(
value="Delete",
variant="primary",
min_width=10,
)
self.btn_del_cnl = gr.Button(value="Cancel", min_width=10)
with gr.Row():
self.conversation_rn = gr.Text(
placeholder="Conversation name",
container=False,
scale=5,
min_width=10,
interactive=True,
)
self.conversation_rn_btn = gr.Button(
value="Rename", scale=1, min_width=10
)
# current_state = gr.Text()
# show_current_state = gr.Button(value="Current")
# show_current_state.click(
# lambda a, b: "\n".join([a, b]),
# inputs=[cid, self.conversation],
# outputs=[current_state],
# )
self.conversation_rn_btn = gr.Button(
value="Rename",
scale=1,
min_width=10,
elem_classes=["no-background", "body-text-color", "bold-text"],
)
def load_chat_history(self, user_id):
"""Reload chat history"""

View File

@ -100,13 +100,18 @@ class SettingsPage(BasePage):
self.on_building_ui()
def on_building_ui(self):
self.setting_save_btn = gr.Button("Save settings")
if self._app.f_user_management:
with gr.Tab("User settings"):
with gr.Tab("Users"):
self.user_tab()
self.app_tab()
self.index_tab()
self.reasoning_tab()
with gr.Tab("General"):
self.app_tab()
with gr.Tab("Document Indices"):
self.index_tab()
with gr.Tab("Reasoning Pipelines"):
self.reasoning_tab()
self.setting_save_btn = gr.Button(
"Save changes", variant="primary", scale=1, elem_classes=["right-button"]
)
def on_subscribe_public_events(self):
"""

View File

@ -2,4 +2,4 @@ from ktem.main import App
app = App()
demo = app.make()
demo.queue().launch(favicon_path=app._favicon)
demo.queue().launch(favicon_path=app._favicon, inbrowser=True)

View File

@ -21,6 +21,7 @@ dependencies = [
"sqlalchemy",
"sqlmodel",
"tiktoken",
"gradio>=4.0.0,<=4.22.0",
]
readme = "README.md"
license = { text = "MIT License" }