AnatoliiG commited on
Commit
2491e55
·
1 Parent(s): 49dd18b

refactor code

Browse files
Files changed (3) hide show
  1. chat_logic.py +76 -0
  2. styles.py +64 -0
  3. ui.py +32 -150
chat_logic.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+
3
+ import gradio as gr
4
+
5
+ from model import engine
6
+ from utils import get_clean_text
7
+
8
+
9
+ def user_input(user_message, history):
10
+ """Обработка ввода пользователя"""
11
+ if not user_message:
12
+ return None, history
13
+
14
+ if history is None:
15
+ history = []
16
+
17
+ clean_history = []
18
+ for msg in history:
19
+ raw_content = msg.get("content", "")
20
+ text_content = get_clean_text(raw_content)
21
+ clean_history.append({"role": msg["role"], "content": text_content})
22
+
23
+ clean_history.append({"role": "user", "content": str(user_message)})
24
+ return "", clean_history
25
+
26
+
27
+ def bot_response(history, system_prompt, temperature, max_tokens):
28
+ """Генерация ответа модели (стриминг)"""
29
+ if not engine.llm:
30
+ history.append({"role": "assistant", "content": "Error: Model failed to load."})
31
+ yield history
32
+ return
33
+
34
+ messages = [{"role": "system", "content": system_prompt}]
35
+
36
+ # Контекстное окно (последние 15 сообщений)
37
+ relevant_history = history[-15:] if len(history) > 15 else history
38
+
39
+ for msg in relevant_history:
40
+ raw_content = msg.get("content", "")
41
+ text_content = get_clean_text(raw_content)
42
+ messages.append({"role": msg["role"], "content": text_content})
43
+
44
+ history.append({"role": "assistant", "content": ""})
45
+
46
+ try:
47
+ stream = engine.generate(
48
+ messages=messages,
49
+ max_tokens=max_tokens,
50
+ temperature=temperature,
51
+ stream=True,
52
+ )
53
+
54
+ partial_text = ""
55
+ for chunk in stream:
56
+ delta = chunk["choices"][0]["delta"]
57
+ if "content" in delta:
58
+ partial_text += delta["content"]
59
+ history[-1]["content"] = partial_text
60
+ yield history
61
+
62
+ except Exception as e:
63
+ traceback.print_exc()
64
+ history[-1]["content"] = partial_text + f"\n\n❌ **Error:** {str(e)}"
65
+ yield history
66
+
67
+
68
+ def set_interactive(is_interactive):
69
+ """Переключение состояния кнопок во время генерации"""
70
+ return (
71
+ gr.update(
72
+ interactive=is_interactive,
73
+ placeholder="Wait..." if not is_interactive else "Type code question...",
74
+ ),
75
+ gr.update(interactive=is_interactive),
76
+ )
styles.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CSS = """
2
+ /* Убираем лишние отступы у всего приложения */
3
+ .gradio-container {
4
+ width: 100% !important;
5
+ height: 100vh !important;
6
+ margin: 0 !important;
7
+ padding: 0 !important;
8
+ overflow: hidden !important;
9
+ }
10
+
11
+ /* Сайдбар на всю высоту */
12
+ .sidebar {
13
+ height: 100vh !important;
14
+ border-right: 1px solid var(--border-color-primary) !important;
15
+ }
16
+
17
+ /*
18
+ ГЛАВНАЯ КОЛОНКА (ID: col-chat-main)
19
+ Превращаем её во Flex-контейнер.
20
+ Она занимает 100% высоты экрана.
21
+ */
22
+ #col-chat-main {
23
+ height: 100vh !important;
24
+ max-height: 100vh !important;
25
+ display: flex !important;
26
+ flex-direction: column !important;
27
+ justify-content: space-between !important;
28
+ padding: 0 !important;
29
+ margin: 0 !important;
30
+ border: none !important;
31
+ }
32
+
33
+ /*
34
+ ЧАТБОТ (ID: chatbot)
35
+ flex-grow: 1 -> Заставляет чат занять всё доступное пространство.
36
+ min-height: 0 -> КРИТИЧНО для работы скролла внутри flex-элемента.
37
+ */
38
+ #chatbot {
39
+ flex-grow: 1 !important;
40
+ height: auto !important;
41
+ min-height: 0 !important;
42
+ overflow-y: auto !important;
43
+ margin: 0 !important;
44
+ border: none !important;
45
+ border-radius: 0 !important;
46
+ padding-bottom: 10px !important;
47
+ }
48
+
49
+ /*
50
+ ЗОНА ВВОДА (ID: input-area)
51
+ flex-shrink: 0 -> Запрещаем сжиматься.
52
+ Прижимается к низу автоматически из-за flex-grow у чата.
53
+ */
54
+ #input-area {
55
+ flex-shrink: 0 !important;
56
+ padding: 15px !important;
57
+ background: var(--background-fill-secondary);
58
+ border-top: 1px solid var(--border-color-primary);
59
+ width: 100% !important;
60
+ }
61
+
62
+ /* Убираем футер */
63
+ footer { display: none !important; }
64
+ """
ui.py CHANGED
@@ -1,139 +1,19 @@
1
- import traceback
2
-
3
  import gradio as gr
4
 
5
  import config
6
- from model import engine
7
- from utils import get_clean_text
8
-
9
- CUSTOM_CSS = """
10
- /* 1. Глобальный сброс отступов и фиксация высоты */
11
- .gradio-container {
12
- width: 100% !important;
13
- height: 100vh !important;
14
- margin: 0 !important;
15
- padding: 0 !important;
16
- overflow: hidden !important;
17
- }
18
-
19
- /* 2. Сайдбар тоже на всю высоту */
20
- .sidebar {
21
- height: 100vh !important;
22
- border-right: 1px solid var(--border-color-primary) !important;
23
- }
24
-
25
- /* 3. Главная колонка с чатом - превращаем во flex-контейнер */
26
- .main-chat-col {
27
- height: 100vh !important;
28
- display: flex !important;
29
- flex-direction: column !important;
30
- padding: 0 !important;
31
- margin: 0 !important;
32
- border: none !important;
33
- }
34
-
35
- /* 4. Окно чата: растягивается (flex-grow: 1) на всё доступное место */
36
- #chatbot {
37
- flex-grow: 1 !important;
38
- height: auto !important; /* Сбрасываем фиксированную высоту */
39
- min-height: 0 !important; /* Разрешаем сжиматься, если нужно */
40
- overflow-y: auto !important; /* Скролл внутри чата */
41
- margin-bottom: 0 !important;
42
- border: none !important;
43
- border-radius: 0 !important;
44
- }
45
-
46
- /* 5. Полоса ввода: фиксирована внизу */
47
- .input-row {
48
- flex-shrink: 0 !important; /* Не сжимать */
49
- padding: 15px !important;
50
- background: var(--background-fill-secondary); /* Цвет фона под вводом */
51
- border-top: 1px solid var(--border-color-primary);
52
- z-index: 100;
53
- }
54
-
55
- /* Доп. стили для ширины сообщений */
56
- .message { width: 100% !important; max-width: 100% !important; }
57
- .message-wrap { max-width: 100% !important; }
58
- footer { display: none !important; }
59
- """
60
-
61
-
62
- # --- Логика событий ---
63
-
64
-
65
- def user_input(user_message, history):
66
- if not user_message:
67
- return None, history
68
- if history is None:
69
- history = []
70
-
71
- clean_history = []
72
- for msg in history:
73
- raw_content = msg.get("content", "")
74
- text_content = get_clean_text(raw_content)
75
- clean_history.append({"role": msg["role"], "content": text_content})
76
-
77
- clean_history.append({"role": "user", "content": str(user_message)})
78
- return "", clean_history
79
-
80
-
81
- def bot_response(history, system_prompt, temperature, max_tokens):
82
- if not engine.llm:
83
- history.append({"role": "assistant", "content": "Error: Model failed to load."})
84
- yield history
85
- return
86
-
87
- messages = [{"role": "system", "content": system_prompt}]
88
- relevant_history = history[-15:] if len(history) > 15 else history
89
-
90
- for msg in relevant_history:
91
- raw_content = msg.get("content", "")
92
- text_content = get_clean_text(raw_content)
93
- messages.append({"role": msg["role"], "content": text_content})
94
-
95
- history.append({"role": "assistant", "content": ""})
96
-
97
- try:
98
- stream = engine.generate(
99
- messages=messages,
100
- max_tokens=max_tokens,
101
- temperature=temperature,
102
- stream=True,
103
- )
104
- partial_text = ""
105
- for chunk in stream:
106
- delta = chunk["choices"][0]["delta"]
107
- if "content" in delta:
108
- partial_text += delta["content"]
109
- history[-1]["content"] = partial_text
110
- yield history
111
- except Exception as e:
112
- traceback.print_exc()
113
- history[-1]["content"] = partial_text + f"\n\n❌ **Error:** {str(e)}"
114
- yield history
115
-
116
-
117
- def set_interactive(is_interactive):
118
- return (
119
- gr.update(
120
- interactive=is_interactive,
121
- placeholder="Wait..." if not is_interactive else "Type code question...",
122
- ),
123
- gr.update(interactive=is_interactive),
124
- )
125
-
126
-
127
- # --- Создание интерфейса ---
128
 
129
 
130
  def create_ui():
131
  theme = gr.themes.Soft(primary_hue="blue", text_size="lg")
132
 
133
- with gr.Blocks(theme=theme, css=CUSTOM_CSS, title="Qwen Coder Pro") as demo:
134
- # fill_height=True помогает растянуть layout
 
 
135
  with gr.Row(equal_height=True, variant="default", elem_classes=["main-row"]):
136
- # --- Боковая панель ---
137
  with gr.Sidebar(elem_classes=["sidebar"]):
138
  gr.Markdown("### ⚙️ Settings")
139
  system_prompt = gr.Textbox(
@@ -149,17 +29,22 @@ def create_ui():
149
  )
150
  clear_btn = gr.Button("🗑️ Clear Chat", variant="secondary")
151
 
152
- # --- Основная колонка ат + Ввод) ---
153
- with gr.Column(scale=1, elem_classes=["main-chat-col"]):
 
 
154
  chatbot = gr.Chatbot(
 
155
  label="Code Assistant",
156
- elem_id="chatbot",
157
  avatar_images=(None, "https://api.iconify.design/noto:robot.svg"),
 
158
  layout="bubble",
159
  render_markdown=True,
 
160
  )
161
 
162
- with gr.Row(elem_classes=["input-row"]):
 
163
  msg = gr.Textbox(
164
  show_label=False,
165
  placeholder="Type your code question here...",
@@ -173,25 +58,22 @@ def create_ui():
173
  "Run ➤", variant="primary", scale=1, min_width=80
174
  )
175
 
176
- # --- Привязка событий ---
177
- submit_event = (
178
- msg.submit(user_input, [msg, chatbot], [msg, chatbot], queue=False)
179
- .then(lambda: set_interactive(False), None, [msg, submit_btn], queue=False)
180
- .then(
181
- bot_response, [chatbot, system_prompt, temperature, max_tokens], chatbot
182
- )
183
- .then(lambda: set_interactive(True), None, [msg, submit_btn], queue=False)
184
- )
185
-
186
- click_event = (
187
- submit_btn.click(user_input, [msg, chatbot], [msg, chatbot], queue=False)
188
- .then(lambda: set_interactive(False), None, [msg, submit_btn], queue=False)
189
- .then(
190
- bot_response, [chatbot, system_prompt, temperature, max_tokens], chatbot
191
- )
192
- .then(lambda: set_interactive(True), None, [msg, submit_btn], queue=False)
193
- )
194
-
195
  clear_btn.click(lambda: [], None, chatbot, queue=False)
196
 
197
  return demo
 
 
 
1
  import gradio as gr
2
 
3
  import config
4
+ from chat_logic import bot_response, set_interactive, user_input
5
+ from styles import CSS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
 
8
  def create_ui():
9
  theme = gr.themes.Soft(primary_hue="blue", text_size="lg")
10
 
11
+ # fill_height=True помогает Gradio понять, что мы хотим на весь экран
12
+ with gr.Blocks(
13
+ theme=theme, css=CSS, title="Qwen Coder Pro", fill_height=True
14
+ ) as demo:
15
  with gr.Row(equal_height=True, variant="default", elem_classes=["main-row"]):
16
+ # --- ЛЕВАЯ КОЛОНКА (НАСТРОЙКИ) ---
17
  with gr.Sidebar(elem_classes=["sidebar"]):
18
  gr.Markdown("### ⚙️ Settings")
19
  system_prompt = gr.Textbox(
 
29
  )
30
  clear_btn = gr.Button("🗑️ Clear Chat", variant="secondary")
31
 
32
+ # --- ПРАВАЯ КОЛОНКААТ + ВВОД) ---
33
+ # elem_id="col-chat-main" связывает эту колонку с Flexbox-стилями в styles.py
34
+ with gr.Column(scale=1, elem_id="col-chat-main"):
35
+ # 1. Чат (растянется благодаря flex-grow: 1 в CSS)
36
  chatbot = gr.Chatbot(
37
+ elem_id="chatbot", # Важно для CSS
38
  label="Code Assistant",
 
39
  avatar_images=(None, "https://api.iconify.design/noto:robot.svg"),
40
+ show_copy_button=True,
41
  layout="bubble",
42
  render_markdown=True,
43
+ scale=1,
44
  )
45
 
46
+ # 2. Зона ввода (прижмется к низу)
47
+ with gr.Row(elem_id="input-area"):
48
  msg = gr.Textbox(
49
  show_label=False,
50
  placeholder="Type your code question here...",
 
58
  "Run ➤", variant="primary", scale=1, min_width=80
59
  )
60
 
61
+ # --- СОБЫТИЯ ---
62
+ # Enter в поле ввода
63
+ msg.submit(user_input, [msg, chatbot], [msg, chatbot], queue=False).then(
64
+ lambda: set_interactive(False), None, [msg, submit_btn], queue=False
65
+ ).then(
66
+ bot_response, [chatbot, system_prompt, temperature, max_tokens], chatbot
67
+ ).then(lambda: set_interactive(True), None, [msg, submit_btn], queue=False)
68
+
69
+ # Клик по кнопке Run
70
+ submit_btn.click(user_input, [msg, chatbot], [msg, chatbot], queue=False).then(
71
+ lambda: set_interactive(False), None, [msg, submit_btn], queue=False
72
+ ).then(
73
+ bot_response, [chatbot, system_prompt, temperature, max_tokens], chatbot
74
+ ).then(lambda: set_interactive(True), None, [msg, submit_btn], queue=False)
75
+
76
+ # Очистка
 
 
 
77
  clear_btn.click(lambda: [], None, chatbot, queue=False)
78
 
79
  return demo