mirror of
https://github.com/Cinnamon/kotaemon.git
synced 2025-06-26 23:19:56 +00:00
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:
parent
a203fc0f7c
commit
8001c86b16
@ -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",
|
||||
|
@ -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",
|
||||
},
|
||||
]
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"""
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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"""
|
||||
|
@ -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"""
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -21,6 +21,7 @@ dependencies = [
|
||||
"sqlalchemy",
|
||||
"sqlmodel",
|
||||
"tiktoken",
|
||||
"gradio>=4.0.0,<=4.22.0",
|
||||
]
|
||||
readme = "README.md"
|
||||
license = { text = "MIT License" }
|
||||
|
Loading…
x
Reference in New Issue
Block a user