AnatoliiG commited on
Commit ·
05f0041
1
Parent(s): 5ef649a
ui update
Browse files- src/ui/components.py +71 -23
- src/ui/styles.py +109 -43
src/ui/components.py
CHANGED
|
@@ -9,62 +9,110 @@ from src.ui.styles import CSS
|
|
| 9 |
|
| 10 |
|
| 11 |
def get_system_status():
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
|
| 17 |
def create_ui():
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
sys_pt = gr.Textbox(
|
| 32 |
-
label="System Prompt",
|
|
|
|
|
|
|
| 33 |
)
|
| 34 |
-
temp = gr.Slider(0, 1, value=settings.DEFAULT_TEMP, label="
|
| 35 |
tokens = gr.Slider(
|
| 36 |
-
512,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
)
|
| 38 |
-
clear = gr.Button("🗑️ Clear")
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
with gr.Column(elem_id="col-chat-main"):
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
with gr.Row(elem_id="input-area"):
|
| 43 |
-
msg = gr.Textbox(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
submit = gr.Button("Run ➤", variant="primary", scale=1)
|
| 45 |
|
|
|
|
| 46 |
timer.tick(
|
| 47 |
get_system_status,
|
| 48 |
-
outputs=[cpu_display,
|
| 49 |
show_progress="hidden",
|
| 50 |
)
|
| 51 |
|
|
|
|
| 52 |
input_args = [msg, chatbot]
|
| 53 |
output_args = [msg, chatbot]
|
| 54 |
gen_args = [chatbot, sys_pt, temp, tokens]
|
| 55 |
|
|
|
|
| 56 |
msg.submit(user_input, input_args, output_args, queue=False).then(
|
| 57 |
lambda: set_interactive(False), None, [msg, submit]
|
| 58 |
).then(bot_response, gen_args, chatbot).then(
|
| 59 |
lambda: set_interactive(True), None, [msg, submit]
|
| 60 |
)
|
| 61 |
|
|
|
|
| 62 |
submit.click(user_input, input_args, output_args, queue=False).then(
|
| 63 |
lambda: set_interactive(False), None, [msg, submit]
|
| 64 |
).then(bot_response, gen_args, chatbot).then(
|
| 65 |
lambda: set_interactive(True), None, [msg, submit]
|
| 66 |
)
|
| 67 |
|
|
|
|
| 68 |
clear.click(lambda: [], None, chatbot)
|
| 69 |
|
| 70 |
return demo
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
def get_system_status():
|
| 12 |
+
"""
|
| 13 |
+
Получает данные о состоянии системы для отображения в блоке Health.
|
| 14 |
+
Возвращает значения для CPU, общей RAM и RAM процесса.
|
| 15 |
+
"""
|
| 16 |
+
# CPU usage (%)
|
| 17 |
+
cpu = psutil.cpu_percent(interval=None)
|
| 18 |
+
# Total system RAM (%)
|
| 19 |
+
memory = psutil.virtual_memory()
|
| 20 |
+
# Current process RAM (MB)
|
| 21 |
+
process = psutil.Process(os.getpid())
|
| 22 |
+
ram_mb = process.memory_info().rss / (1024 * 1024)
|
| 23 |
+
|
| 24 |
+
return f"{cpu}%", f"{memory.percent}%", f"{round(ram_mb, 1)} MB"
|
| 25 |
|
| 26 |
|
| 27 |
def create_ui():
|
| 28 |
+
# fill_height=True позволяет приложению занимать всю высоту окна браузера
|
| 29 |
+
with gr.Blocks(css=CSS, title="Code LLM", fill_height=True) as demo:
|
| 30 |
+
# Таймер обновления системных показателей (раз в 2 секунды)
|
| 31 |
+
timer = gr.Timer(2, active=True)
|
| 32 |
+
|
| 33 |
+
with gr.Row(elem_id="main-row", variant="panel"):
|
| 34 |
+
# Боковая панель
|
| 35 |
+
with gr.Sidebar(elem_id="sidebar-container"):
|
| 36 |
+
gr.Markdown("## 🛠️ Dashboard")
|
| 37 |
+
|
| 38 |
+
# Блок System Health с красивой оберткой
|
| 39 |
+
with gr.Column(elem_id="system-status-container"):
|
| 40 |
+
gr.Markdown("### 📊 System Health")
|
| 41 |
+
with gr.Row():
|
| 42 |
+
cpu_display = gr.Label(label="CPU Load", value="0%")
|
| 43 |
+
ram_total_display = gr.Label(label="System RAM", value="0%")
|
| 44 |
+
ram_proc_display = gr.Label(label="Process Memory", value="0 MB")
|
| 45 |
+
|
| 46 |
+
gr.Separator()
|
| 47 |
+
|
| 48 |
+
# Настройки модели
|
| 49 |
+
gr.Markdown("### ⚙️ Model Settings")
|
| 50 |
sys_pt = gr.Textbox(
|
| 51 |
+
label="System Prompt",
|
| 52 |
+
value="Вы опытный программист. Отвечаете кратко и по делу.",
|
| 53 |
+
lines=4,
|
| 54 |
)
|
| 55 |
+
temp = gr.Slider(0, 1, value=settings.DEFAULT_TEMP, label="Temperature")
|
| 56 |
tokens = gr.Slider(
|
| 57 |
+
512,
|
| 58 |
+
8192,
|
| 59 |
+
value=settings.DEFAULT_MAX_TOKENS,
|
| 60 |
+
label="Max New Tokens",
|
| 61 |
+
step=128,
|
| 62 |
)
|
|
|
|
| 63 |
|
| 64 |
+
gr.Separator()
|
| 65 |
+
clear = gr.Button("🗑️ Clear Chat", variant="stop")
|
| 66 |
+
|
| 67 |
+
# Основная область чата
|
| 68 |
with gr.Column(elem_id="col-chat-main"):
|
| 69 |
+
# fill_height=True здесь критически важен для "окна на весь экран"
|
| 70 |
+
chatbot = gr.Chatbot(
|
| 71 |
+
elem_id="chatbot",
|
| 72 |
+
show_label=False,
|
| 73 |
+
bubble_full_width=False,
|
| 74 |
+
fill_height=True,
|
| 75 |
+
type="messages",
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
# Область ввода, прижатая к низу
|
| 79 |
with gr.Row(elem_id="input-area"):
|
| 80 |
+
msg = gr.Textbox(
|
| 81 |
+
show_label=False,
|
| 82 |
+
placeholder="Type your message here...",
|
| 83 |
+
scale=10,
|
| 84 |
+
autofocus=True,
|
| 85 |
+
container=False,
|
| 86 |
+
)
|
| 87 |
submit = gr.Button("Run ➤", variant="primary", scale=1)
|
| 88 |
|
| 89 |
+
# Связываем таймер с функцией обновления статуса
|
| 90 |
timer.tick(
|
| 91 |
get_system_status,
|
| 92 |
+
outputs=[cpu_display, ram_total_display, ram_proc_display],
|
| 93 |
show_progress="hidden",
|
| 94 |
)
|
| 95 |
|
| 96 |
+
# Настройка логики обработки соо��щений
|
| 97 |
input_args = [msg, chatbot]
|
| 98 |
output_args = [msg, chatbot]
|
| 99 |
gen_args = [chatbot, sys_pt, temp, tokens]
|
| 100 |
|
| 101 |
+
# Обработка Enter в текстовом поле
|
| 102 |
msg.submit(user_input, input_args, output_args, queue=False).then(
|
| 103 |
lambda: set_interactive(False), None, [msg, submit]
|
| 104 |
).then(bot_response, gen_args, chatbot).then(
|
| 105 |
lambda: set_interactive(True), None, [msg, submit]
|
| 106 |
)
|
| 107 |
|
| 108 |
+
# Обработка клика по кнопке Run
|
| 109 |
submit.click(user_input, input_args, output_args, queue=False).then(
|
| 110 |
lambda: set_interactive(False), None, [msg, submit]
|
| 111 |
).then(bot_response, gen_args, chatbot).then(
|
| 112 |
lambda: set_interactive(True), None, [msg, submit]
|
| 113 |
)
|
| 114 |
|
| 115 |
+
# Очистка чата
|
| 116 |
clear.click(lambda: [], None, chatbot)
|
| 117 |
|
| 118 |
return demo
|
src/ui/styles.py
CHANGED
|
@@ -1,86 +1,152 @@
|
|
| 1 |
CSS = """
|
| 2 |
-
/* 1. Глобальный контейнер на в
|
| 3 |
.gradio-container {
|
| 4 |
-
width: 100% !important;
|
| 5 |
-
height: 100vh !important;
|
| 6 |
margin: 0 !important;
|
| 7 |
padding: 0 !important;
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
-
/* 2. Основной ряд
|
| 12 |
#main-row {
|
|
|
|
| 13 |
height: 100vh !important;
|
| 14 |
-
|
|
|
|
| 15 |
overflow: hidden !important;
|
| 16 |
}
|
| 17 |
|
| 18 |
/* 3. Сайдбар */
|
| 19 |
-
|
| 20 |
-
height: 100vh !important;
|
| 21 |
border-right: 1px solid var(--border-color-primary) !important;
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
}
|
| 24 |
|
| 25 |
-
/* 4.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
#col-chat-main {
|
| 27 |
-
height: 100vh !important;
|
| 28 |
display: flex !important;
|
| 29 |
flex-direction: column !important;
|
|
|
|
| 30 |
padding: 0 !important;
|
| 31 |
margin: 0 !important;
|
| 32 |
-
|
| 33 |
-
|
| 34 |
}
|
| 35 |
|
| 36 |
-
/*
|
| 37 |
-
height: 0 + flex-grow: 1 заставляет его занять всё место,
|
| 38 |
-
даже если сообщений мало.
|
| 39 |
-
*/
|
| 40 |
#chatbot {
|
| 41 |
flex-grow: 1 !important;
|
| 42 |
-
height:
|
| 43 |
-
min-height: 0 !important; /* Разрешает сжиматься/растягиваться */
|
| 44 |
-
width: 100% !important;
|
| 45 |
-
overflow-y: auto !important; /* Скролл внутри */
|
| 46 |
border: none !important;
|
|
|
|
| 47 |
margin: 0 !important;
|
|
|
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
|
| 50 |
-
/*
|
| 51 |
#chatbot > .wrapper {
|
| 52 |
-
|
| 53 |
display: flex !important;
|
| 54 |
flex-direction: column !important;
|
| 55 |
}
|
| 56 |
|
| 57 |
-
/* 6. Поле ввода */
|
| 58 |
#input-area {
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
z-index: 10; /* Чтобы быть поверх */
|
| 65 |
}
|
| 66 |
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
filter: none !important;
|
| 74 |
-
transition: none !important;
|
| 75 |
}
|
| 76 |
|
| 77 |
-
/*
|
| 78 |
-
.
|
| 79 |
-
|
|
|
|
| 80 |
}
|
| 81 |
|
| 82 |
-
/*
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
}
|
| 86 |
"""
|
|
|
|
| 1 |
CSS = """
|
| 2 |
+
/* 1. Глобальный контейнер: занимаем все пространство без прокрутки страницы */
|
| 3 |
.gradio-container {
|
| 4 |
+
max-width: 100% !important;
|
|
|
|
| 5 |
margin: 0 !important;
|
| 6 |
padding: 0 !important;
|
| 7 |
+
height: 100vh !important;
|
| 8 |
+
display: flex !important;
|
| 9 |
+
flex-direction: column !important;
|
| 10 |
+
background-color: var(--background-fill-primary) !important;
|
| 11 |
}
|
| 12 |
|
| 13 |
+
/* 2. Основной ряд: Sidebar + Chat */
|
| 14 |
#main-row {
|
| 15 |
+
flex-grow: 1 !important;
|
| 16 |
height: 100vh !important;
|
| 17 |
+
margin: 0 !important;
|
| 18 |
+
gap: 0 !important;
|
| 19 |
overflow: hidden !important;
|
| 20 |
}
|
| 21 |
|
| 22 |
/* 3. Сайдбар */
|
| 23 |
+
#sidebar-container {
|
|
|
|
| 24 |
border-right: 1px solid var(--border-color-primary) !important;
|
| 25 |
+
background-color: var(--background-fill-secondary) !important;
|
| 26 |
+
padding: 1.25rem !important;
|
| 27 |
+
height: 100% !important;
|
| 28 |
+
display: flex !important;
|
| 29 |
+
flex-direction: column !important;
|
| 30 |
+
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.02) !important;
|
| 31 |
}
|
| 32 |
|
| 33 |
+
/* 4. Блок "System Health": карточный дизайн */
|
| 34 |
+
#system-status-container {
|
| 35 |
+
background: var(--block-background-fill) !important;
|
| 36 |
+
border: 1px solid var(--border-color-primary) !important;
|
| 37 |
+
border-radius: 12px !important;
|
| 38 |
+
padding: 12px !important;
|
| 39 |
+
margin-top: 10px !important;
|
| 40 |
+
margin-bottom: 20px !important;
|
| 41 |
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05) !important;
|
| 42 |
+
transition: all 0.3s ease-in-out !important;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
#system-status-container:hover {
|
| 46 |
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1) !important;
|
| 47 |
+
transform: translateY(-1px);
|
| 48 |
+
border-color: var(--color-accent) !important;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
/* Стилизация заголовков внутри блока статуса */
|
| 52 |
+
#system-status-container h3 {
|
| 53 |
+
font-size: 0.9rem !important;
|
| 54 |
+
margin-bottom: 12px !important;
|
| 55 |
+
color: var(--body-text-color-subdued) !important;
|
| 56 |
+
text-transform: uppercase;
|
| 57 |
+
letter-spacing: 0.05em;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/* Убираем "старение" (побледнение) при обновлении данных */
|
| 61 |
+
.stale {
|
| 62 |
+
opacity: 1 !important;
|
| 63 |
+
filter: none !important;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
/* 5. Правая колонка с чатом (Главная область) */
|
| 67 |
#col-chat-main {
|
|
|
|
| 68 |
display: flex !important;
|
| 69 |
flex-direction: column !important;
|
| 70 |
+
height: 100vh !important;
|
| 71 |
padding: 0 !important;
|
| 72 |
margin: 0 !important;
|
| 73 |
+
gap: 0 !important;
|
| 74 |
+
background: var(--background-fill-primary) !important;
|
| 75 |
}
|
| 76 |
|
| 77 |
+
/* ЧАТБОТ: Растягиваем на всю высоту */
|
|
|
|
|
|
|
|
|
|
| 78 |
#chatbot {
|
| 79 |
flex-grow: 1 !important;
|
| 80 |
+
height: 100% !important;
|
|
|
|
|
|
|
|
|
|
| 81 |
border: none !important;
|
| 82 |
+
border-radius: 0 !important;
|
| 83 |
margin: 0 !important;
|
| 84 |
+
display: flex !important;
|
| 85 |
+
flex-direction: column !important;
|
| 86 |
+
overflow-y: auto !important;
|
| 87 |
}
|
| 88 |
|
| 89 |
+
/* Фикс для внутренней обертки Gradio Chatbot */
|
| 90 |
#chatbot > .wrapper {
|
| 91 |
+
flex-grow: 1 !important;
|
| 92 |
display: flex !important;
|
| 93 |
flex-direction: column !important;
|
| 94 |
}
|
| 95 |
|
| 96 |
+
/* 6. Поле ввода: всегда внизу */
|
| 97 |
#input-area {
|
| 98 |
+
padding: 1rem 2rem !important;
|
| 99 |
+
border-top: 1px solid var(--border-color-primary) !important;
|
| 100 |
+
background: var(--background-fill-primary) !important;
|
| 101 |
+
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.02) !important;
|
| 102 |
+
flex-shrink: 0 !important;
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
+
#input-area textarea {
|
| 106 |
+
border-radius: 10px !important;
|
| 107 |
+
border: 1px solid var(--border-color-primary) !important;
|
| 108 |
+
padding: 12px 16px !important;
|
| 109 |
+
font-size: 1rem !important;
|
| 110 |
+
transition: border-color 0.2s ease !important;
|
| 111 |
+
}
|
| 112 |
|
| 113 |
+
#input-area textarea:focus {
|
| 114 |
+
border-color: var(--color-accent) !important;
|
| 115 |
+
box-shadow: 0 0 0 2px rgba(var(--color-accent-rgb), 0.1) !important;
|
|
|
|
|
|
|
| 116 |
}
|
| 117 |
|
| 118 |
+
/* 7. Кнопки и прочие элементы */
|
| 119 |
+
.gr-button-primary {
|
| 120 |
+
border-radius: 8px !important;
|
| 121 |
+
font-weight: 600 !important;
|
| 122 |
}
|
| 123 |
|
| 124 |
+
/* Кастомный скроллбар */
|
| 125 |
+
::-webkit-scrollbar {
|
| 126 |
+
width: 6px;
|
| 127 |
+
height: 6px;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
::-webkit-scrollbar-track {
|
| 131 |
+
background: transparent;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
::-webkit-scrollbar-thumb {
|
| 135 |
+
background: var(--neutral-300);
|
| 136 |
+
border-radius: 10px;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
::-webkit-scrollbar-thumb:hover {
|
| 140 |
+
background: var(--neutral-400);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
/* Скрытие футера */
|
| 144 |
+
footer {
|
| 145 |
+
display: none !important;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* Убираем лишние зазоры между элементами */
|
| 149 |
+
.gap {
|
| 150 |
+
gap: 12px !important;
|
| 151 |
}
|
| 152 |
"""
|