Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -1,218 +1,81 @@
|
|
1 |
-
#
|
2 |
|
3 |
-
# Определяем CSS стили
|
4 |
-
custom_css = """
|
5 |
-
/* Общий фон */
|
6 |
-
.gradio-container {
|
7 |
-
/* background: linear-gradient(to bottom right, #e0f7fa, #f1f8e9); */ /* Убрали градиент, может мешать */
|
8 |
-
background-color: #f4f7f9; /* Спокойный светло-серый фон */
|
9 |
-
border-radius: 15px;
|
10 |
-
padding: 25px;
|
11 |
-
color: #333; /* Основной цвет текста */
|
12 |
-
}
|
13 |
-
|
14 |
-
/* Заголовок */
|
15 |
-
h1 {
|
16 |
-
color: #1a237e; /* Глубокий индиго */
|
17 |
-
text-align: center;
|
18 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* Читаемый шрифт */
|
19 |
-
margin-bottom: 15px;
|
20 |
-
font-weight: 600;
|
21 |
-
}
|
22 |
-
#title-markdown p { /* Стили для параграфа под заголовком */
|
23 |
-
text-align: center;
|
24 |
-
color: #546e7a; /* Серо-синий */
|
25 |
-
margin-top: -10px;
|
26 |
-
margin-bottom: 25px;
|
27 |
-
}
|
28 |
-
|
29 |
-
|
30 |
-
/* --- СТИЛИ ЧАТА --- */
|
31 |
-
|
32 |
-
/* Контейнер чата */
|
33 |
-
#chatbot {
|
34 |
-
background-color: #eceff1; /* Очень светлый серо-голубой фон чата */
|
35 |
-
border-radius: 12px; /* Немного другие скругления */
|
36 |
-
border: 1px solid #cfd8dc; /* Граница контейнера */
|
37 |
-
padding: 10px; /* Внутренние отступы */
|
38 |
-
box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); /* Легкая внутренняя тень */
|
39 |
-
}
|
40 |
-
|
41 |
-
/* Сообщения пользователя */
|
42 |
-
#chatbot .user-message .message-bubble-border {
|
43 |
-
border: none !important; /* Убираем стандартную рамку gradio */
|
44 |
-
}
|
45 |
-
#chatbot .user-message .message-bubble {
|
46 |
-
background: #007bff !important; /* Яркий синий фон */
|
47 |
-
color: white !important; /* Белый текст */
|
48 |
-
border-radius: 18px 18px 5px 18px !important; /* Асимметричное скругление */
|
49 |
-
padding: 12px 18px !important;
|
50 |
-
margin: 8px 5px 8px 0 !important; /* Отступы */
|
51 |
-
align-self: flex-end !important;
|
52 |
-
max-width: 80% !important;
|
53 |
-
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2); /* Тень под цвет */
|
54 |
-
word-wrap: break-word;
|
55 |
-
text-align: left;
|
56 |
-
}
|
57 |
-
|
58 |
-
|
59 |
-
/* Сообщения бота */
|
60 |
-
#chatbot .bot-message .message-bubble-border {
|
61 |
-
border: none !important; /* Убираем стандартную рамку gradio */
|
62 |
-
}
|
63 |
-
#chatbot .bot-message .message-bubble {
|
64 |
-
background: #ffffff !important; /* Чисто белый фон для контраста */
|
65 |
-
color: #212529 !important; /* Темный текст (почти черный) */
|
66 |
-
border: 1px solid #dee2e6 !important; /* Легкая серая рамка */
|
67 |
-
border-radius: 18px 18px 18px 5px !important; /* Асимметричное скругление */
|
68 |
-
padding: 12px 18px !important;
|
69 |
-
margin: 8px 0 8px 5px !important; /* Отступы */
|
70 |
-
align-self: flex-start !important;
|
71 |
-
max-width: 80% !important;
|
72 |
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); /* Мягкая тень */
|
73 |
-
word-wrap: break-word;
|
74 |
-
text-align: left;
|
75 |
-
}
|
76 |
-
|
77 |
-
/* Блоки кода внутри сообщений бота */
|
78 |
-
#chatbot .bot-message .message-bubble pre {
|
79 |
-
background-color: #f8f9fa; /* Фон для блока кода */
|
80 |
-
border: 1px solid #e9ecef; /* Рамка блока кода */
|
81 |
-
border-radius: 6px; /* Скругление */
|
82 |
-
padding: 10px; /* Отступы внутри */
|
83 |
-
margin: 8px 0 5px 0; /* Отступы снаружи */
|
84 |
-
overflow-x: auto; /* Горизонтальная прокрутка если код длинный */
|
85 |
-
word-wrap: normal; /* Предотвратить перенос слов в коде */
|
86 |
-
}
|
87 |
-
#chatbot .bot-message .message-bubble pre code {
|
88 |
-
background-color: transparent !important; /* Убираем фон у самого тега code */
|
89 |
-
color: #2d3748; /* Цвет текста кода */
|
90 |
-
font-family: 'Consolas', 'Monaco', 'Courier New', monospace; /* Моноширинный шрифт */
|
91 |
-
font-size: 0.9em; /* Размер шрифта кода */
|
92 |
-
padding: 0; /* Убираем внутренние отступы тега code */
|
93 |
-
white-space: pre; /* Сохраняем пробелы и переносы */
|
94 |
-
}
|
95 |
-
|
96 |
-
/* --- ОСТАЛЬНЫЕ ЭЛЕМЕНТЫ --- */
|
97 |
-
|
98 |
-
/* Поле ввода текста */
|
99 |
-
textarea {
|
100 |
-
border: 1px solid #ced4da !important;
|
101 |
-
border-radius: 10px !important;
|
102 |
-
padding: 12px 15px !important;
|
103 |
-
background-color: #ffffff;
|
104 |
-
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
105 |
-
font-size: 1rem; /* Слегка увеличим шрифт ввода */
|
106 |
-
}
|
107 |
-
textarea:focus {
|
108 |
-
border-color: #80bdff !important;
|
109 |
-
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
110 |
-
outline: none; /* Убираем стандартный outline */
|
111 |
-
}
|
112 |
-
|
113 |
-
/* Кнопки */
|
114 |
-
button {
|
115 |
-
border-radius: 10px !important;
|
116 |
-
padding: 10px 15px !important; /* Паддинги для кнопок */
|
117 |
-
transition: background-color 0.2s ease, transform 0.1s ease !important;
|
118 |
-
font-weight: 500 !important;
|
119 |
-
border: none !important; /* Убираем рамки по умолчанию */
|
120 |
-
}
|
121 |
-
button:active {
|
122 |
-
transform: translateY(1px); /* Эффект нажатия */
|
123 |
-
}
|
124 |
-
button.primary { /* Кнопка "Отправить" */
|
125 |
-
background-color: #007bff !important; /* Синий цвет */
|
126 |
-
color: white !important;
|
127 |
-
}
|
128 |
-
button.primary:hover {
|
129 |
-
background-color: #0056b3 !important; /* Темнее при наведении */
|
130 |
-
box-shadow: 0 2px 5px rgba(0, 123, 255, 0.3);
|
131 |
-
}
|
132 |
-
button.secondary { /* Кнопка "Очистить" */
|
133 |
-
background-color: #6c757d !important; /* Серый цвет */
|
134 |
-
color: white !important;
|
135 |
-
}
|
136 |
-
button.secondary:hover {
|
137 |
-
background-color: #5a6268 !important; /* Темнее при наведении */
|
138 |
-
box-shadow: 0 2px 5px rgba(108, 117, 125, 0.3);
|
139 |
-
}
|
140 |
-
"""
|
141 |
-
|
142 |
-
# --- Python код остается тем же, что и в предыдущем ответе ---
|
143 |
-
# (начиная с import gradio as gr...)
|
144 |
-
# ... весь остальной Python код без изменений ...
|
145 |
import gradio as gr
|
146 |
-
from transformers import pipeline,
|
147 |
import random
|
148 |
-
import re
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
|
150 |
-
#
|
151 |
-
# Модель-генератор (например, русская или английская)
|
152 |
-
GENERATOR_MODEL_NAME = 'sberbank-ai/rugpt3small_based_on_gpt2' # Или 'distilgpt2'
|
153 |
try:
|
154 |
-
|
155 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
GENERATOR_LOADED = True
|
157 |
except Exception as e:
|
158 |
-
print(f"ОШИБКА: Не удалось загрузить
|
159 |
generator = None
|
|
|
|
|
160 |
GENERATOR_LOADED = False
|
161 |
|
162 |
-
# QA Модель (
|
163 |
QA_MODEL_NAME = 'timpal0l/mdeberta-v3-base-squad2'
|
164 |
try:
|
165 |
qa_pipeline = pipeline('question-answering', model=QA_MODEL_NAME)
|
166 |
print(f"QA модель ({QA_MODEL_NAME}) загружена.")
|
167 |
QA_LOADED = True
|
|
|
|
|
168 |
except Exception as e:
|
169 |
print(f"ОШИБКА: Не удалось загрузить QA модель! {e}")
|
170 |
qa_pipeline = None
|
171 |
QA_LOADED = False
|
172 |
|
173 |
-
|
|
|
174 |
knowledge_base = {
|
175 |
-
|
176 |
-
"
|
177 |
-
"
|
178 |
-
"
|
179 |
-
"
|
180 |
-
|
181 |
-
#
|
182 |
-
"напиши код": "Я не могу писать произвольный код по запросу. Моя функция - давать информацию и объяснения. Возможно, вы имели в виду пример кода для какой-то задачи? Попробуйте спросить конкретнее, например: 'пример цикла for на python'.",
|
183 |
-
"сгенерируй код": "Генерация сложного кода пока не входит в мои возможности. Я могу объяснить концепции или показать базовые синтаксические конструкции. Какой пример вас интересует?",
|
184 |
-
"напиши скрипт": "К сожалению, я не пишу готовые скрипты. Опишите задачу, и я постараюсь объяснить, какие шаги или концепции программирования могут понадобиться для ее решения.",
|
185 |
-
"код на питоне": "Пайтон - отличный язык! Уточните, какой именно аспект или пример кода на Python вас интересует? Например, 'как объявить переменную на python?' или 'пример функции на python'.",
|
186 |
-
"пример кода": "Конечно! Какой пример кода вы хотели бы увидеть? Например, 'пример условного оператора if' или 'пример простого класса на python'.",
|
187 |
-
"hello world на python": "Самый классический пример! Вот 'Hello, World!' на Python:\n```python\nprint(\"Hello, World!\")\n```",
|
188 |
-
"пример цикла for на python": "Вот пример цикла `for`, который печатает числа от 0 до 4:\n```python\nfor i in range(5):\n print(i)\n```",
|
189 |
-
"пример функции на python": "Вот простая функция на Python, которая складывает два числа:\n```python\ndef add_numbers(a, b):\n return a + b\n\nresult = add_numbers(5, 3)\nprint(result) # Выведет 8\n```",
|
190 |
-
"как объявить переменную на python": "Переменная в Python объявляется простым присваиванием значения. Например:\n```python\nmy_variable = 10\nuser_name = \"Alice\"\nis_active = True\n```",
|
191 |
-
|
192 |
-
# Основы Программирования (остальное из 0.95)
|
193 |
-
"что такое программирование": "Программирование - это процесс написания инструкций (кода) для компьютера, чтобы он выполнял определенные задачи. Это как составление очень точного рецепта для машины.",
|
194 |
-
"зачем нужно программирование": "Программирование позволяет создавать программы, веб-сайты, игры, автоматизировать задачи, анализировать данные и многое другое. Оно лежит в основе почти всех современных технологий.",
|
195 |
-
"что такое алгоритм": "Алгоритм - это пошаговая инструкция или набор правил для решения определенной задачи. Например, рецепт приготовления блюда - это алгоритм.",
|
196 |
-
"основные концепции программирования": "Ключевые идеи включают: переменные (хранение данных), типы данных (числа, строки), условные операторы (if/else - если/иначе), циклы (for, while - для повторения действий), функции (для организации кода).",
|
197 |
-
"что такое переменная": "Переменная в программировании - это как именованный контейнер или ячейка памяти, где можно хранить данные (число, текст, и т.д.), чтобы использовать их позже в программе. Например, `age = 30`.",
|
198 |
-
"что такое типы данных": "Типы данных определяют, какого рода информацию может хранить переменная и какие операции с ней можно выполнять. Распространенные типы: целые числа (integer), числа с плавающей точкой (float), строки (string - текст), булевы (boolean - истина/ложь).",
|
199 |
-
"что такое строка": "Строка (string) - это последовательность символов, обычно используемая для представления текста. В большинстве языков строки заключаются в кавычки (одинарные или двойные). Например, \"Привет, мир!\".",
|
200 |
-
"условный оператор if": "Оператор `if` (если) позволяет выполнить блок кода только в том случае, если определенное условие истинно. Часто используется вместе с `else` (иначе) для выполнения другого блока кода, если условие ложно.",
|
201 |
-
"что такое цикл": "Цикл - это конструкция, которая позволяет повторять выполнение блока кода несколько раз. Основные виды: `for` (для), который обычно используется для перебора элементов (например, в списке) или выполнения заданное число раз, и `while` (пока), который выполняется, пока истинно определенное условие.",
|
202 |
-
"цикл for": "Цикл `for` удобен, когда вы знаете, сколько раз нужно повторить действие, или когда нужно пройти по всем элементам коллекции (списка, строки).",
|
203 |
-
"цикл while": "Цикл `while` повторяет блок кода до тех пор, пока заданное условие остается истинным. Важно убедиться, что условие когда-нибудь станет ложным, иначе цикл будет бесконечным.",
|
204 |
-
"что такое функция": "Функция - это именованный блок кода, который выполняет определенную задачу. Функции помогают разбивать сложную программу на мелкие, управляемые части, избегать повторения кода и делать его более читаемым.",
|
205 |
-
"язык программирования": "Язык программирования - это формальный язык, предназначенный для записи компьютерных программ. Популярные языки: Python (хорош для новичков, науки о данных, веб-бэкенда), JavaScript (для веб-фронтенда и бэкенда), Java (для энтерпрайза, Android), C++ (для игр, системного программирования), C# (для Windows, игр на Unity).",
|
206 |
-
"python": "Python - популярный, высокоуровневый язык программирования с ��ростым синтаксисом, подходящий для широкого круга задач: веб-разработка, наука о данных, машинное обучение, автоматизация.",
|
207 |
-
"javascript": "JavaScript (JS) - основной язык для создания интерактивных веб-страниц (фронтенд). Также используется на бэкенде (Node.js) и для мобильных приложений.",
|
208 |
-
"html": "HTML (HyperText Markup Language) - это не язык программирования, а язык разметки. Он используется для определения структуры и содержимого веб-страницы (заголовки, параграфы, картинки, ссылки).",
|
209 |
-
"css": "CSS (Cascading Style Sheets) - язык стилей, используемый для описания внешнего вида документа, написанного на языке разметки (обычно HTML). Определяет цвета, шрифты, отступы, расположение элементов.",
|
210 |
-
"что такое api": "API (Application Programming Interface) - это набор правил и протоколов, который позволяет разным программам 'общаться' друг с другом. Это как меню в ресторане: вы выбираете блюдо (функцию API), и кухня (другая программа) его готовит и отдает вам.",
|
211 |
-
"что такое git": "Git - это распределенная система контроля версий. Она помогает разработчикам отслеживать изменения в коде, возвращаться к предыдущим версиям, работать над проектом совместно и не терять изменения.",
|
212 |
-
"где учиться программировать": "Существует множество ресурсов: интерактивные онлайн-курсы (Codecademy, Coursera, Stepik, Яндекс.Практикум), документация языков, книги, видеоуроки на YouTube, форумы (Stack Overflow), сообщества разработчиков."
|
213 |
}
|
214 |
|
215 |
-
# --- Утилиты ---
|
216 |
def clean_text(text):
|
217 |
if not isinstance(text, str): return ""
|
218 |
text = text.lower().strip()
|
@@ -221,134 +84,141 @@ def clean_text(text):
|
|
221 |
return text
|
222 |
|
223 |
def clean_generated_output(generated_text, prompt):
|
|
|
224 |
if not generated_text or not isinstance(generated_text, str): return ""
|
225 |
if not prompt or not isinstance(prompt, str): prompt = ""
|
226 |
cleaned = generated_text
|
227 |
prompt_clean = prompt.strip()
|
228 |
if prompt_clean and cleaned.startswith(prompt_clean):
|
229 |
cleaned = cleaned[len(prompt_clean):].strip()
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
if last_user_line.lower().startswith(("пользователь:", "user:")):
|
235 |
-
user_message_only = last_user_line.split(":", 1)[1].strip()
|
236 |
-
if user_message_only and cleaned.lower().startswith(user_message_only.lower()):
|
237 |
-
end_of_echo_pos = len(user_message_only)
|
238 |
-
following_text = cleaned[end_of_echo_pos:].lstrip()
|
239 |
-
if re.match(r'^(бот|bot)\s*[:\-]?\s*', following_text, flags=re.IGNORECASE):
|
240 |
-
following_text = re.sub(r'^(бот|bot)\s*[:\-]?\s*', '', following_text, flags=re.IGNORECASE).strip()
|
241 |
-
cleaned = following_text
|
242 |
-
cleaned = re.sub(r'^\s*(ответ|бот|пользователь|user|bot)\s*[:\-]?\s*', '', cleaned, flags=re.IGNORECASE | re.MULTILINE).strip()
|
243 |
return cleaned
|
244 |
|
245 |
-
|
|
|
246 |
def respond(message, chat_history):
|
247 |
print("-" * 30)
|
248 |
print(f"ВХОД: '{message}'")
|
249 |
user_message_clean = clean_text(message)
|
250 |
-
bot_response = ""
|
251 |
|
252 |
-
# 1.
|
253 |
if user_message_clean in knowledge_base:
|
254 |
bot_response = knowledge_base[user_message_clean]
|
255 |
-
print(f"Ответ [Точный]: {bot_response[:100]}...")
|
256 |
-
|
257 |
-
# 2.
|
258 |
-
if not bot_response: # Только если не нашли точный ответ
|
259 |
-
code_request_patterns = ["напиши код", "напиши скрипт", "сгенерируй код", "создай код", "write code", "generate code", "create script", "code example for", "пример кода для"]
|
260 |
-
is_code_request = any(user_message_clean.startswith(pattern) for pattern in code_request_patterns)
|
261 |
-
if is_code_request:
|
262 |
-
bot_response = knowledge_base.get("напиши код", "Я не могу писать произвольный код, но могу дать объяснение или пример. Уточните ваш запрос.")
|
263 |
-
print(f"Ответ [Запрос кода]: {bot_response}")
|
264 |
-
|
265 |
-
# 3. QA модель
|
266 |
-
if not bot_response and "?" in message and QA_LOADED and qa_pipeline:
|
267 |
-
context = ""
|
268 |
-
if chat_history:
|
269 |
-
context_parts = []
|
270 |
-
for user_msg, bot_msg in chat_history[-2:]:
|
271 |
-
context_parts.append(f"Пользователь: {user_msg}")
|
272 |
-
if bot_msg: context_parts.append(f"Бот: {bot_msg}")
|
273 |
-
context = "\n".join(context_parts)
|
274 |
-
if len(context) > 50:
|
275 |
-
print("Попытка ответа через QA...")
|
276 |
-
try:
|
277 |
-
qa_result = qa_pipeline(question=message, context=context)
|
278 |
-
print(f"Результат QA: score={qa_result.get('score', 0):.2f}, answer='{qa_result.get('answer', '')[:50]}...'")
|
279 |
-
if qa_result and qa_result['score'] > 0.25: # Уверенность > 25%
|
280 |
-
answer = qa_result['answer'].strip()
|
281 |
-
if answer:
|
282 |
-
bot_response = answer
|
283 |
-
print(f"Ответ [QA]: {bot_response[:100]}...")
|
284 |
-
else: print("QA модель дала пустой ответ.")
|
285 |
-
else: print("QA модель не уверена.")
|
286 |
-
except Exception as e: print(f"Ошибка при использовании QA модели: {e}")
|
287 |
-
|
288 |
-
# 4. Генерация
|
289 |
if not bot_response and GENERATOR_LOADED and generator:
|
290 |
-
print("
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
299 |
try:
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
except Exception as e:
|
310 |
-
print(f"Ошибка при
|
311 |
-
bot_response = knowledge_base.get("ошибка генерации", "Произошла ошибка при
|
312 |
|
313 |
-
#
|
314 |
if not bot_response:
|
315 |
-
|
316 |
-
|
|
|
|
|
|
|
|
|
317 |
|
318 |
|
319 |
# Добавляем в историю и возвращаем
|
320 |
chat_history.append((message, bot_response))
|
321 |
-
return "", chat_history
|
322 |
|
323 |
|
324 |
-
# --- Gradio Интерфейс ---
|
325 |
-
|
326 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
|
328 |
chatbot = gr.Chatbot(
|
329 |
-
label="Диалог",
|
330 |
-
|
331 |
-
|
332 |
-
bubble_full_width=False,
|
333 |
-
avatar_images=(None, "https://img.icons8.com/plasticine/100/bot.png") # Аватарка для бота
|
334 |
)
|
335 |
-
|
336 |
with gr.Row():
|
337 |
msg = gr.Textbox(
|
338 |
-
label="Ваше сообщение",
|
339 |
-
|
340 |
-
scale=4,
|
341 |
-
show_label=False,
|
342 |
-
container=False # Убираем контейнер текстбокса для лучшего выравнивания
|
343 |
)
|
344 |
-
submit_btn = gr.Button("➤ Отправить", variant="primary", scale=1, min_width=120)
|
345 |
-
clear_btn = gr.Button("🗑️ Очистить", variant="secondary", scale=1, min_width=120)
|
346 |
|
347 |
-
# Обработчики
|
348 |
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
349 |
submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
|
350 |
clear_btn.click(lambda: (None, []), None, [msg, chatbot], queue=False)
|
351 |
|
352 |
# Запуск
|
353 |
demo.queue()
|
354 |
-
demo.launch(debug=True)
|
|
|
1 |
+
# app.py
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
import gradio as gr
|
4 |
+
from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM # Используем Auto классы для большей гибкости
|
5 |
import random
|
6 |
+
import re
|
7 |
+
import torch # Убедимся, что torch импортирован
|
8 |
+
|
9 |
+
# --- Выбор Модели ---
|
10 |
+
# Заменяем на самую маленькую DeepSeek Coder Instruct
|
11 |
+
# !!! ПРЕДУПРЕЖДЕНИЕ: МОЖЕТ НЕ ЗАПУСТИТЬСЯ ИЛИ БЫТЬ ОЧЕНЬ МЕДЛЕННОЙ НА FREE CPU !!!
|
12 |
+
CODE_MODEL_NAME = "deepseek-ai/deepseek-coder-1.3b-instruct"
|
13 |
+
|
14 |
+
# --- Загрузка моделей ---
|
15 |
+
GENERATOR_LOADED = False
|
16 |
+
QA_LOADED = False
|
17 |
+
generator = None
|
18 |
+
qa_pipeline = None
|
19 |
|
20 |
+
# 1. Модель для кодирования/генерации
|
|
|
|
|
21 |
try:
|
22 |
+
# Для моделей кода часто лучше использовать AutoModelForCausalLM и AutoTokenizer напрямую
|
23 |
+
# Вместо pipeline('text-generation') это дает больше контроля
|
24 |
+
print(f"Загрузка токенизатора: {CODE_MODEL_NAME}")
|
25 |
+
tokenizer = AutoTokenizer.from_pretrained(CODE_MODEL_NAME, trust_remote_code=True)
|
26 |
+
print(f"Загрузка модели: {CODE_MODEL_NAME}")
|
27 |
+
# Загружаем с torch_dtype=torch.float16 для экономии памяти, если возможно
|
28 |
+
try:
|
29 |
+
generator_model = AutoModelForCausalLM.from_pretrained(
|
30 |
+
CODE_MODEL_NAME,
|
31 |
+
trust_remote_code=True,
|
32 |
+
torch_dtype=torch.float16 # Пытаемся загрузить в половинной точности
|
33 |
+
)
|
34 |
+
except Exception as e_half: # Если float16 не поддерживается/вызывает ошибку, пробуем float32
|
35 |
+
print(f"Не удалось загрузить в float16 ({e_half}), пробую float32...")
|
36 |
+
generator_model = AutoModelForCausalLM.from_pretrained(
|
37 |
+
CODE_MODEL_NAME,
|
38 |
+
trust_remote_code=True
|
39 |
+
)
|
40 |
+
|
41 |
+
# Создаем pipeline вручную
|
42 |
+
generator = pipeline('text-generation', model=generator_model, tokenizer=tokenizer, device=-1) # device=-1 значит CPU
|
43 |
+
|
44 |
+
print(f"Генератор кода/текста ({CODE_MODEL_NAME}) загружен.")
|
45 |
GENERATOR_LOADED = True
|
46 |
except Exception as e:
|
47 |
+
print(f"ОШИБКА: Не удалось загрузить модель {CODE_MODEL_NAME}! {e}")
|
48 |
generator = None
|
49 |
+
generator_model = None
|
50 |
+
tokenizer = None
|
51 |
GENERATOR_LOADED = False
|
52 |
|
53 |
+
# 2. QA Модель (оставляем как есть или можно убрать, если не нужна)
|
54 |
QA_MODEL_NAME = 'timpal0l/mdeberta-v3-base-squad2'
|
55 |
try:
|
56 |
qa_pipeline = pipeline('question-answering', model=QA_MODEL_NAME)
|
57 |
print(f"QA модель ({QA_MODEL_NAME}) загружена.")
|
58 |
QA_LOADED = True
|
59 |
+
# !!! ВНИМАНИЕ: Две модели могут потребовать слишком много RAM !!!
|
60 |
+
# !!! Возможно, стоит закомментировать загрузку QA, если возникают проблемы с памятью !!!
|
61 |
except Exception as e:
|
62 |
print(f"ОШИБКА: Не удалось загрузить QA модель! {e}")
|
63 |
qa_pipeline = None
|
64 |
QA_LOADED = False
|
65 |
|
66 |
+
|
67 |
+
# --- Встроенные знания (можно сократить, т.к. модель сама умнее) ---
|
68 |
knowledge_base = {
|
69 |
+
"кто ты": f"Я Nova (использую модель {CODE_MODEL_NAME}), работающая на Hugging Face Spaces. Я специализируюсь на помощи с кодом и ответами на вопросы по программированию.",
|
70 |
+
"что ты умеешь": "Я могу пытаться генерировать код, объяснять его, находить ошибки (в определенной степени), отвечать на вопросы по программированию и поддерживать диалог.",
|
71 |
+
"как дела": "Работаю над кодом! А если серьезно - функционирую нормально.",
|
72 |
+
"помощь": "Спросите меня о программировании, попросите написать фрагмент кода (например, 'напиши функцию на python для суммирования списка'), или просто пообщаемся.",
|
73 |
+
"python": "Python - отличный высокоуровневый язык, известный своей читаемостью. Хорошо подходит для веб-разработки, анализа данных, ИИ и автоматизации.",
|
74 |
+
"javascript": "JavaScript - основной язык веб-фронтенда, позволяет делать страницы интерактивными. С Node.js используется и на бэкенде.",
|
75 |
+
# Убрали много базовых вещей, т.к. модель должна знать их сама
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
}
|
77 |
|
78 |
+
# --- Утилиты (clean_text, clean_generated_output - как раньше) ---
|
79 |
def clean_text(text):
|
80 |
if not isinstance(text, str): return ""
|
81 |
text = text.lower().strip()
|
|
|
84 |
return text
|
85 |
|
86 |
def clean_generated_output(generated_text, prompt):
|
87 |
+
# Простая очистка - убираем промпт если он есть в начале
|
88 |
if not generated_text or not isinstance(generated_text, str): return ""
|
89 |
if not prompt or not isinstance(prompt, str): prompt = ""
|
90 |
cleaned = generated_text
|
91 |
prompt_clean = prompt.strip()
|
92 |
if prompt_clean and cleaned.startswith(prompt_clean):
|
93 |
cleaned = cleaned[len(prompt_clean):].strip()
|
94 |
+
|
95 |
+
# Попробуем убрать стандартные фразы, которые модель может добавить
|
96 |
+
cleaned = re.sub(r'^\s*(ответ|бот|вот код|конечно|here is the code)\s*[:\-]?\s*', '', cleaned, flags=re.IGNORECASE).strip()
|
97 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
return cleaned
|
99 |
|
100 |
+
|
101 |
+
# --- Основная Функция Обработки (Упрощенная, больше полагаемся на модель) ---
|
102 |
def respond(message, chat_history):
|
103 |
print("-" * 30)
|
104 |
print(f"ВХОД: '{message}'")
|
105 |
user_message_clean = clean_text(message)
|
106 |
+
bot_response = ""
|
107 |
|
108 |
+
# 1. Проверка на точное совпадение в knowledge_base (ОСТАВЛЯЕМ для мета-вопросов)
|
109 |
if user_message_clean in knowledge_base:
|
110 |
bot_response = knowledge_base[user_message_clean]
|
111 |
+
print(f"Ответ [Точный]: {bot_response[:100]}...")
|
112 |
+
|
113 |
+
# 2. Основная генерация (используем модель DeepSeek Coder)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
if not bot_response and GENERATOR_LOADED and generator:
|
115 |
+
print("Генерация ответа с помощью модели кода...")
|
116 |
+
# Формируем промпт в формате, который модель ожидае�� (часто нужен специальный формат для Instruct-моделей)
|
117 |
+
# Для DeepSeek Coder Instruct формат может быть таким:
|
118 |
+
prompt_list = []
|
119 |
+
prompt_list.append("### Instruction:")
|
120 |
+
# Добавим контекст из чата (если есть)
|
121 |
+
for user_msg, bot_msg in chat_history[-2:]: # Последние 2 обмена для контекста
|
122 |
+
prompt_list.append(f"User: {user_msg}")
|
123 |
+
if bot_msg: prompt_list.append(f"Assistant: {bot_msg}") # Используем Assistant
|
124 |
+
prompt_list.append(f"{message}") # Текущее сообщение как инструкция/вопрос
|
125 |
+
prompt_list.append("\n### Response:") # Приглашение для ответа
|
126 |
+
|
127 |
+
full_prompt = "\n".join(prompt_list)
|
128 |
+
print(f"Промпт для DeepSeek: ...{full_prompt[-600:]}") # Логируем конец
|
129 |
+
|
130 |
try:
|
131 |
+
# Параметры генерации для кода
|
132 |
+
# `max_new_tokens` - ограничивает длину *сгенерированного* текста
|
133 |
+
# `temperature` - контроль случайности (ниже = более предсказуемо)
|
134 |
+
# `top_p`, `top_k` - другие методы семплирования
|
135 |
+
# `eos_token_id` - ID токена конца последовательности (может помочь модели остановиться)
|
136 |
+
generated_output = generator(
|
137 |
+
full_prompt,
|
138 |
+
max_new_tokens=250, # Ограничим генерацию
|
139 |
+
temperature=0.7,
|
140 |
+
top_k=50,
|
141 |
+
top_p=0.95,
|
142 |
+
# eos_token_id=tokenizer.eos_token_id if tokenizer else None, # Помогает модели остановиться
|
143 |
+
pad_token_id=tokenizer.eos_token_id if tokenizer else 50256, # Часто нужно указать
|
144 |
+
num_return_sequences=1,
|
145 |
+
do_sample=True
|
146 |
+
)[0]['generated_text']
|
147 |
+
|
148 |
+
print(f"Сырой ответ DeepSeek: {generated_output}")
|
149 |
+
|
150 |
+
# Очистка - убираем весь промпт
|
151 |
+
bot_response = clean_generated_output(generated_output, full_prompt)
|
152 |
+
print(f"Очищенный ответ DeepSeek: {bot_response}")
|
153 |
+
|
154 |
+
# Дополнительно убираем инструкции, которые модель могла повторить
|
155 |
+
bot_response = bot_response.replace("### Instruction:", "").replace("### Response:", "").strip()
|
156 |
+
|
157 |
+
if not bot_response or len(bot_response) < 5:
|
158 |
+
bot_response = knowledge_base.get("не найдено", "Извините, не смог сгенерировать ответ.")
|
159 |
+
print("Генерация DeepSeek не удалась или слишком короткая.")
|
160 |
+
|
161 |
except Exception as e:
|
162 |
+
print(f"Ошибка при генерации DeepSeek: {e}")
|
163 |
+
bot_response = knowledge_base.get("ошибка генерации", f"Произошла ошибка при генерации: {e}")
|
164 |
|
165 |
+
# 3. Ответ по умолчанию
|
166 |
if not bot_response:
|
167 |
+
# Если генератор не загружен или ничего не получилось
|
168 |
+
if not GENERATOR_LOADED:
|
169 |
+
bot_response = "Модель для генерации кода не загружена."
|
170 |
+
else:
|
171 |
+
bot_response = knowledge_base.get("не найдено", "К сожалению, я пока не знаю, как на это ответить.")
|
172 |
+
print("Ответ [Заглушка/Ошибка]")
|
173 |
|
174 |
|
175 |
# Добавляем в историю и возвращаем
|
176 |
chat_history.append((message, bot_response))
|
177 |
+
return "", chat_history
|
178 |
|
179 |
|
180 |
+
# --- Gradio Интерфейс (CSS тот же) ---
|
181 |
+
custom_css = """
|
182 |
+
.gradio-container { background-color: #f4f7f9; border-radius: 15px; padding: 25px; color: #333; }
|
183 |
+
h1 { color: #1a237e; text-align: center; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin-bottom: 15px; font-weight: 600; }
|
184 |
+
#title-markdown p { text-align: center; color: #546e7a; margin-top: -10px; margin-bottom: 25px;}
|
185 |
+
#chatbot { background-color: #eceff1; border-radius: 12px; border: 1px solid #cfd8dc; padding: 10px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); }
|
186 |
+
#chatbot .user-message .message-bubble-border { border: none !important; }
|
187 |
+
#chatbot .user-message .message-bubble { background: #007bff !important; color: white !important; border-radius: 18px 18px 5px 18px !important; padding: 12px 18px !important; margin: 8px 5px 8px 0 !important; align-self: flex-end !important; max-width: 80% !important; box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2); word-wrap: break-word; text-align: left;}
|
188 |
+
#chatbot .bot-message .message-bubble-border { border: none !important; }
|
189 |
+
#chatbot .bot-message .message-bubble { background: #ffffff !important; color: #212529 !important; border: 1px solid #dee2e6 !important; border-radius: 18px 18px 18px 5px !important; padding: 12px 18px !important; margin: 8px 0 8px 5px !important; align-self: flex-start !important; max-width: 80% !important; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); word-wrap: break-word; text-align: left;}
|
190 |
+
#chatbot .bot-message .message-bubble pre { background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 6px; padding: 10px; margin: 8px 0 5px 0; overflow-x: auto; word-wrap: normal; }
|
191 |
+
#chatbot .bot-message .message-bubble pre code { background-color: transparent !important; color: #2d3748; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 0.9em; padding: 0; white-space: pre; }
|
192 |
+
textarea { border: 1px solid #ced4da !important; border-radius: 10px !important; padding: 12px 15px !important; background-color: #ffffff; transition: border-color 0.3s ease, box-shadow 0.3s ease; font-size: 1rem; }
|
193 |
+
textarea:focus { border-color: #80bdff !important; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); outline: none; }
|
194 |
+
button { border-radius: 10px !important; padding: 10px 15px !important; transition: background-color 0.2s ease, transform 0.1s ease !important; font-weight: 500 !important; border: none !important; }
|
195 |
+
button:active { transform: translateY(1px); }
|
196 |
+
button.primary { background-color: #007bff !important; color: white !important; }
|
197 |
+
button.primary:hover { background-color: #0056b3 !important; box-shadow: 0 2px 5px rgba(0, 123, 255, 0.3); }
|
198 |
+
button.secondary { background-color: #6c757d !important; color: white !important; }
|
199 |
+
button.secondary:hover { background-color: #5a6268 !important; box-shadow: 0 2px 5px rgba(108, 117, 125, 0.3); }
|
200 |
+
"""
|
201 |
+
|
202 |
+
with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue=gr.themes.colors.blue, secondary_hue=gr.themes.colors.slate)) as demo:
|
203 |
+
gr.Markdown("# 🤖 Nova (DeepSeek Test) 💡 <br> Используем модель для кода. Ответы могут быть ОЧЕНЬ медленными!")
|
204 |
|
205 |
chatbot = gr.Chatbot(
|
206 |
+
label="Диалог", height=550, elem_id="chatbot",
|
207 |
+
show_copy_button=True, bubble_full_width=False,
|
208 |
+
avatar_images=(None, "https://img.icons8.com/plasticine/100/bot.png")
|
|
|
|
|
209 |
)
|
|
|
210 |
with gr.Row():
|
211 |
msg = gr.Textbox(
|
212 |
+
label="Ваше сообщение", placeholder="Напиши функцию python для...",
|
213 |
+
scale=4, show_label=False, container=False
|
|
|
|
|
|
|
214 |
)
|
215 |
+
submit_btn = gr.Button("➤ Отправить", variant="primary", scale=1, min_width=120)
|
216 |
+
clear_btn = gr.Button("🗑️ Очистить", variant="secondary", scale=1, min_width=120)
|
217 |
|
|
|
218 |
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
219 |
submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
|
220 |
clear_btn.click(lambda: (None, []), None, [msg, chatbot], queue=False)
|
221 |
|
222 |
# Запуск
|
223 |
demo.queue()
|
224 |
+
demo.launch(debug=True)
|