my-gradio-app / ui /layout.py
Nguyen Trong Lap
Recreate history without binary blobs
eeb0f9c
import gradio as gr
from .components import (
create_header,
create_chatbot,
create_input_row,
create_clear_button,
create_top_header,
create_login_form,
)
from .handlers import (
setup_handlers,
handle_login,
handle_register,
handle_logout,
handle_load_history
)
from utils.helpers import convert_chatbot_messages_to_list
from auth.db import init_db
import modelscope_studio.components.antdx as antdx
import modelscope_studio.components.base as ms
import modelscope_studio.components.antd as antd
init_db()
theme = gr.themes.Soft()
def create_auth(session, chat_column, chatbot):
with gr.Column() as auth_block:
with gr.Row():
with gr.Column(scale=8):
create_header()
with gr.Column(scale=2):
top_row, welcome_text, login_btn, logout_btn = create_top_header()
login_form, username, password, login_submit, register_btn, back_btn, status = create_login_form()
# Sự kiện: bấm nút đăng nhập → hiện form, ẩn khu vực chat
login_btn.click(
lambda: (
gr.update(visible=False), # Ẩn nút đăng nhập
gr.update(visible=True), # Hiện form đăng nhập
gr.update(visible=False), # Ẩn khu vực
gr.update(value="", visible=False) # Ẩn message
),
None,
[login_btn, login_form, chat_column, welcome_text]
)
# Sự kiện: bấm nút đăng xuất → xóa session, ẩn lời chào
def logout_handler():
msg = handle_logout(session)
return (
msg,
gr.update(visible=True), # Hiện nút đăng nhập
gr.update(visible=False), # Ẩn nút đăng xuất
gr.update(value="", visible=True), # Hiện message
gr.update(visible=True), # Hiện khu vực chat
gr.update(value=[]) # Reset chatbot
)
logout_btn.click(
logout_handler,
None,
[status, login_btn, logout_btn, welcome_text, chat_column, chatbot]
)
# Sự kiện: bấm nút trở lại → ẩn lời chào
def return_handler():
return (
"",
gr.update(visible=True), # Hiện nút đăng nhập
gr.update(visible=False), # Ẩn login form
gr.update(visible=True), # Hiện khu vực chat
gr.update(value="", visible=True) # Hiện message
)
back_btn.click(
return_handler,
None,
[status, login_btn, login_form, chat_column, welcome_text]
)
# Sự kiện: xác thực đăng nhập
def login_submit_handler(u, p):
success, msg, username = handle_login(u, p, session)
if success:
welcome = f"👋 Xin chào, {username.title()}!"
history = handle_load_history(username)
return (
msg,
gr.update(visible=True), # Hiện chat_column
gr.update(visible=False), # Ẩn form đăng nhập
gr.update(visible=True), # Hiện nút đăng xuất
gr.update(visible=False), # Ẩn nút đăng nhập
gr.update(value=welcome, visible=True), # Hiện lời chào
gr.update(value=history)
)
else:
return (
msg,
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
gr.update(value="", visible=True),
gr.update(value=[])
)
login_submit.click(
login_submit_handler,
inputs=[username, password],
outputs=[status, chat_column, login_form,
logout_btn, login_btn, welcome_text, chatbot]
)
# Sự kiện: đăng ký tài khoản
def register_handler(u, p):
success, msg = handle_register(u, p, session)
return msg
register_btn.click(
register_handler,
inputs=[username, password],
outputs=[status]
)
return {
"auth_block": auth_block,
"top_row": top_row,
"login_form": login_form,
"welcome_text": welcome_text,
"login_btn": login_btn,
"logout_btn": logout_btn,
"username": username,
"password": password,
"login_submit": login_submit,
"register_btn": register_btn,
"status": status
}
def create_chat_column(session):
"""Tạo giao diện chatbot"""
with gr.Column(visible=True) as chat_column:
with gr.Column(elem_classes=["tab-container"]):
username = session.value.get("user")
# Chatbot with clear badge overlay
with gr.Group(elem_classes=["chatbot-wrapper"]):
chatbot = create_chatbot()
clear_btn = create_clear_button()
# File display (1 line above input)
file_display = gr.Markdown(
"", elem_classes=["file-display-compact"], visible=False)
# Input row with textbox and submit button
row, message, submit_btn = create_input_row()
# Audio input component for speech-to-text
audio_input = gr.Audio(
sources=["microphone"],
type="filepath",
label="🎤 Nhập bằng giọng nói",
waveform_options=gr.WaveformOptions(
show_recording_waveform=False,
),
)
clear_btn = create_clear_button()
setup_handlers(message, chatbot, submit_btn,
clear_btn, session, audio_input=audio_input)
return chat_column, chatbot
def build_layout():
"""Build the Gradio UI layout for the healthcare AI assistant with modelscope_studio components."""
with gr.Blocks(
theme=theme,
css="""
.tab-title { font-size: 1.2rem; font-weight: 600; }
.tab-subtitle { font-size: 0.9rem; color: #555; margin-top: 0.3rem; }
.tab-container { border-radius: 10px; }
.general-tab { background-color: #e6f7ff; border: 1px solid #91caff; }
.nutrition-tab { background-color: #f6ffed; border: 1px solid #b7eb8f; }
.exercise-tab { background-color: #fff7e6; border: 1px solid #ffd591; }
.mental-tab { background-color: #f9f0ff; border: 1px solid #d3adf7; }
.featured-tip {
background-color: rgba(255, 255, 255, 0.7);
padding: 10px;
border-radius: 8px;
margin-bottom: 15px;
border-left: 4px solid #4b8bf4;
}
.tab-selected { border-bottom: 3px solid #1890ff !important; font-weight: bold; }
.tab-header { margin-bottom: 12px; }
.tab-container button::after { bottom: -20px; }
.action-buttons {
display: flex;
gap: 10px;
}
.build-layout-container {
display: flex;
flex-direction: column;
}
.auth-block {
order: 1;
}
.chat-block {
order: 2;
}
.login-form-container {
max-width: 70%;
margin: auto;
}
.login-input label span {
background-color: unset
}
.login-buttons {
display: flex;
justify-content: space-between;
margin-top: 15px;
}
.login-btn, .register-btn {
width: 48%;
padding: 10px;
font-weight: bold;
border-radius: 8px;
}
.login-status textarea {
margin-top: 15px;
background-color: #fffbe6;
border-left: 4px solid #faad14;
border-radius: 8px;
padding: 10px;
font-size: 0.95rem;
color: #333;
}
.file-display-compact {
font-size: 0.85rem;
color: #666;
padding: 4px 8px;
background-color: #f5f5f5;
border-radius: 4px;
margin: 0 0 8px 0;
}
.input-container {
position: relative;
}
.message-input textarea {
padding-right: 45px !important;
}
.small-button {
max-width: 200px;
}
/* Speech input styling */
#speech-input-panel {
background-color: #f0f7ff;
border: 2px solid #1890ff;
border-radius: 8px;
padding: 15px;
margin-top: 10px;
}
.speech-audio-input {
border: 2px dashed #1890ff;
border-radius: 8px;
padding: 10px;
}
.speech-status {
background-color: #e6f7ff;
border-left: 4px solid #1890ff;
}
.speech-transcription {
background-color: #fafafa;
border: 1px solid #d9d9d9;
border-radius: 4px;
}
/* Chatbot wrapper with badge overlay */
.chatbot-wrapper {
position: relative !important;
background: transparent !important;
border: none !important;
padding: 0 !important;
}
.clear-badge {
position: absolute !important;
top: 8px !important;
right: 8px !important;
z-index: 10 !important;
font-size: 0.75rem !important;
padding: 4px 8px !important;
min-width: auto !important;
height: auto !important;
background-color: rgba(239, 68, 68, 0.9) !important;
border: none !important;
border-radius: 4px !important;
color: white !important;
cursor: pointer !important;
}
.clear-badge:hover {
background-color: rgba(220, 38, 38, 1) !important;
}
/* Input block with inline button */
.input-block {
background-color: #27272A;
border-radius: 8px;
padding: 12px;
}
.input-block > div {
background: transparent !important;
}
.input-label {
font-size: 0.75rem !important;
color: #a1a1aa !important;
margin-bottom: 5px !important;
background-color: #27272A !important;
}
.input-label p {
margin: 0 !important;
font-size: 0.75rem !important;
}
.input-row {
gap: 0 !important;
align-items: center !important;;
}
.input-row button {
min-width: 60px !important;
height: 40px !important;
margin: 0 !important;
border-radius: 0 6px 6px 0 !important;
font-size: 1rem;
}
.message-input {
height: 40px !important;
background: transparent !important;
}
.message-input textarea {
height: 40px !important;
min-height: 40px !important;
max-height: 40px !important;
border-radius: 6px 0 0 6px !important;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3) !important;
background-color: #1a1a1c !important;
border: none !important;
color: #ffffff !important;
padding: 10px 12px !important;
}
.message-input textarea::placeholder {
color: #a1a1aa !important;
}
/* Hide Gradio processing time display */
.progress-text {
display: none !important;
}
footer {
display: none !important;
}
"""
) as demo, ms.Application(), antdx.XProvider():
session = gr.State({"user": None, "history": []})
with antd.Flex(vertical=True, gap="middle"):
chat_column, chatbot = create_chat_column(session)
chat_column.elem_classes = ["chat-block"]
auth_ui = create_auth(session, chat_column, chatbot)
auth_ui["auth_block"].elem_classes = ["auth-block"]
return demo