KoDer123 commited on
Commit
aac5e18
·
verified ·
1 Parent(s): 0f6b0ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -285
app.py CHANGED
@@ -7,6 +7,7 @@ import logging
7
  import gc
8
  import threading
9
  import json
 
10
 
11
  # Настройка логирования
12
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -14,119 +15,48 @@ logger = logging.getLogger(__name__)
14
 
15
  # Проверка наличия ZERO GPU
16
  ZERO_GPU_ENABLED = os.environ.get("HF_ZERO_GPU", "0") == "1"
17
- if ZERO_GPU_ENABLED:
18
- logger.info("Обнаружена переменная HF_ZERO_GPU=1, используем Zero GPU режим")
19
- else:
20
- logger.info("Zero GPU не активирован, используем стандартный режим")
21
 
22
  # Получаем API токен из переменных окружения
23
  HF_TOKEN = os.environ.get("HF_TOKEN", None)
24
- if HF_TOKEN:
25
- logger.info("API токен найден")
26
- else:
27
- logger.warning("API токен не найден! Для доступа к закрытой модели необходимо добавить HF_TOKEN в секреты репозитория")
28
 
29
  # Информация о системе и CUDA
30
  logger.info("===== Запуск приложения =====")
31
  logger.info(f"PyTorch: {torch.__version__}")
32
 
33
- # Проверяем доступность CUDA более безопасным способом
34
- try:
35
- cuda_available = torch.cuda.is_available()
36
- logger.info(f"CUDA доступен: {cuda_available}")
37
-
38
- # Если Zero GPU включен, но CUDA не обнаружена, выводим предупреждение
39
- if ZERO_GPU_ENABLED and not cuda_available:
40
- logger.warning("Zero GPU включен, но CUDA не обнаружена. Возможно, требуется перезапуск Space.")
41
-
42
- if cuda_available:
43
- try:
44
- cuda_device_count = torch.cuda.device_count()
45
- logger.info(f"Количество CUDA устройств: {cuda_device_count}")
46
-
47
- for i in range(cuda_device_count):
48
- try:
49
- logger.info(f"CUDA устройство {i}: {torch.cuda.get_device_name(i)}")
50
- # Проверяем доступную память GPU
51
- try:
52
- free_mem = torch.cuda.get_device_properties(i).total_memory - torch.cuda.memory_allocated(i)
53
- logger.info(f"Устройство {i}: свободно {free_mem / 1024**3:.2f} ГБ")
54
- except:
55
- logger.warning(f"Не удалось определить свободную память на устройстве {i}")
56
- except Exception as e:
57
- logger.warning(f"Не удалось получить имя устройства {i}: {e}")
58
-
59
- try:
60
- current_device = torch.cuda.current_device()
61
- logger.info(f"Текущее CUDA устройство: {current_device}")
62
- except Exception as e:
63
- logger.warning(f"Не удалось определить текущее устройство: {e}")
64
-
65
- except Exception as e:
66
- logger.warning(f"Ошибка при получении информации о CUDA: {e}")
67
- cuda_available = False
68
- else:
69
- logger.info("CUDA недоступен, будет использоваться CPU")
70
- except Exception as e:
71
- logger.error(f"Критическая ошибка при проверке CUDA: {e}")
72
- cuda_available = False
73
- logger.info("Принудительно переключаемся на CPU режим из-за ошибки")
74
 
75
- # Используем домашнюю директорию пользователя (она всегда должна быть доступна)
76
  user_home = os.path.expanduser("~")
77
  DISK_DIR = os.path.join(user_home, "app_data")
78
-
79
- # Создаем директорию, если она не существует
80
- os.makedirs(DISK_DIR, exist_ok=True)
81
- logger.info(f"Используем директорию для хр��нения: {DISK_DIR}")
82
-
83
- # Настраиваем пути для сохранения моделей
84
  CACHE_DIR = os.path.join(DISK_DIR, "models_cache")
85
- TORCH_HOME = os.path.join(DISK_DIR, "torch_home")
86
-
87
- # Создаем директории
88
  os.makedirs(CACHE_DIR, exist_ok=True)
89
- os.makedirs(TORCH_HOME, exist_ok=True)
90
-
91
- # Устанавливаем переменные окружения для управления кэшированием
92
  os.environ["TRANSFORMERS_CACHE"] = CACHE_DIR
93
  os.environ["HF_HOME"] = CACHE_DIR
94
- os.environ["TORCH_HOME"] = TORCH_HOME
95
-
96
- # Функция для проверки свободного места на диске
97
- def check_disk_space(path):
98
- try:
99
- total, used, free = shutil.disk_usage(path)
100
- logger.info(f"Диск {path}: всего {total // (1024**3)} ГБ, свободно {free // (1024**3)} ГБ")
101
- return free
102
- except Exception as e:
103
- logger.warning(f"Не удалось проверить диск {path}: {e}")
104
- return None
105
-
106
- # Выводим информацию о диске перед загрузкой
107
- logger.info("Информация о дисках перед загрузкой:")
108
- check_disk_space("/")
109
- check_disk_space(DISK_DIR)
110
-
111
- # Выбираем модель в зависимости от доступных ресурсов
112
- if cuda_available:
113
- # Для режима GPU используем основную модель
114
- model_name = "KoDer123/Nerealnost_phi"
115
- else:
116
- # Для режима CPU - та же модель или можно выбрать модель поменьше
117
- model_name = "KoDer123/Nerealnost_phi"
118
 
 
 
119
  logger.info(f"Выбрана модель: {model_name}")
120
 
121
- # Глобальные переменные для модели
122
  model = None
123
  tokenizer = None
124
  is_model_loaded = False
125
-
126
- # Переопределяем EOS_TOKEN для случая, когда токенизатор не загружен
127
  DEFAULT_EOS_TOKEN = "</s>"
128
 
129
- # Класс для обработки таймаута, работает в любом потоке
130
  class TimeoutManager:
131
  def __init__(self, seconds):
132
  self.seconds = seconds
@@ -153,21 +83,17 @@ class TimeoutManager:
153
  class TimeoutException(Exception):
154
  pass
155
 
156
- # Функция для очистки памяти
157
  def clear_memory():
158
  if cuda_available:
159
  torch.cuda.empty_cache()
160
  gc.collect()
161
 
162
- # Функция для загрузки модели
163
  def load_model():
164
  global model, tokenizer, is_model_loaded
165
 
166
- # Подключаем нужные библиотеки при необходимости
167
- from transformers import AutoModelForCausalLM, AutoTokenizer
168
-
169
  try:
170
- # Очищаем память перед загрузкой
171
  clear_memory()
172
 
173
  logger.info("Загружаем токенизатор...")
@@ -175,140 +101,96 @@ def load_model():
175
  model_name,
176
  token=HF_TOKEN,
177
  cache_dir=CACHE_DIR,
178
- local_files_only=False
 
179
  )
180
-
181
- # Устанавливаем pad_token, если его нет
182
  if tokenizer.pad_token is None:
183
  tokenizer.pad_token = tokenizer.eos_token
 
 
 
 
 
184
 
185
  logger.info("Загружаем модель...")
186
- # Определяем оптимальный режим загрузки
187
  model_kwargs = {
188
  "cache_dir": CACHE_DIR,
189
  "trust_remote_code": True,
190
- "token": HF_TOKEN
 
191
  }
192
 
193
- # Проверяем доступность CUDA
194
  if cuda_available:
195
  logger.info("Загружаем модель в режиме GPU...")
196
  model_kwargs.update({
197
  "torch_dtype": torch.float16,
198
- "device_map": "auto", # Автоматически распределить по устройствам
 
199
  })
200
  else:
201
  logger.info("Загружаем модель в режиме CPU...")
202
  model_kwargs.update({
203
  "torch_dtype": torch.float32,
 
204
  })
205
 
206
- # Загружаем модель
207
  model = AutoModelForCausalLM.from_pretrained(
208
  model_name,
209
  **model_kwargs
210
  )
211
 
212
- # Если GPU недоступен, явно переносим модель на CPU
213
  if not cuda_available:
214
  model = model.to("cpu")
215
 
216
  device_info = next(model.parameters()).device
217
- logger.info(f"Модель успешно загружена на устройство: {device_info}")
218
 
219
  is_model_loaded = True
220
- return f"Модель успешно загружена на {device_info}"
221
  except Exception as e:
222
- error_msg = str(e)
223
- logger.error(f"Ошибка загрузки модели: {error_msg}")
224
  is_model_loaded = False
225
- return f"Ошибка загрузки модели: {error_msg}"
226
 
227
- # Загружаем модель
228
  start_time = time.time()
229
  load_result = load_model()
230
- end_time = time.time()
231
- logger.info(f"Загрузка модели заняла {end_time - start_time:.2f} секунд. Результат: {load_result}")
232
-
233
- # Выводим информацию о диске после загрузки
234
- logger.info("Информация о дисках после загрузки:")
235
- check_disk_space("/")
236
- check_disk_space(DISK_DIR)
237
 
238
- # Определяем шаблон Q&A, как при обучении
239
- qa_prompt = "<s>Пользователь: {}\nАссистент: {}"
240
- EOS_TOKEN = DEFAULT_EOS_TOKEN
241
- if tokenizer is not None and hasattr(tokenizer, 'eos_token') and tokenizer.eos_token:
242
- EOS_TOKEN = tokenizer.eos_token
243
 
244
- def respond(
245
- message,
246
- history,
247
- system_message,
248
- max_tokens,
249
- temperature,
250
- top_p,
251
- generation_timeout,
252
- ):
253
  global model, tokenizer, is_model_loaded
254
 
255
- # Проверяем, загружена ли модель
256
  if not is_model_loaded or model is None or tokenizer is None:
257
- if not HF_TOKEN:
258
- return "Модель не загружена. Для доступа к закрытой модели требуется добавить HF_TOKEN в секреты репозитория."
259
- else:
260
- return "Модель не загружена или произошла ошибка при загрузке. Проверьте логи для получения дополнительной информации."
261
 
262
- # Очищаем память перед генерацией
263
  clear_memory()
264
-
265
- # Замеряем время
266
  start_time = time.time()
267
 
268
- # Преобразование истории в правильный формат
269
- formatted_history = []
270
- if isinstance(history, list):
271
- for item in history:
272
- if isinstance(item, tuple) and len(item) == 2:
273
- formatted_history.append(item)
274
- elif isinstance(item, dict) and "role" in item and "content" in item:
275
- # Обрабатываем формат messages
276
- if item["role"] == "user":
277
- user_message = item["content"]
278
- assistant_message = None
279
- # Ищем следующее сообщение assistant
280
- idx = history.index(item)
281
- if idx + 1 < len(history) and isinstance(history[idx+1], dict) and history[idx+1].get("role") == "assistant":
282
- assistant_message = history[idx+1].get("content")
283
- if assistant_message:
284
- formatted_history.append((user_message, assistant_message))
285
-
286
- # Формируем историю в текстовом формате
287
  full_prompt = ""
288
  if system_message:
289
  full_prompt += qa_prompt.format(system_message, "") + "\n"
290
-
291
- for user_msg, assistant_msg in formatted_history:
292
  if user_msg and assistant_msg:
293
  full_prompt += qa_prompt.format(user_msg, assistant_msg) + EOS_TOKEN + "\n"
294
-
295
  full_prompt += qa_prompt.format(message, "")
296
 
297
- logger.info(f"Генерируем ответ на запрос: '{message[:50]}...' (длина промпта: {len(full_prompt)})")
298
 
299
  try:
300
- # Настраиваем таймаут
301
  timeout_mgr = TimeoutManager(generation_timeout)
302
  timeout_mgr.start()
303
 
304
- # Токенизация входных данных
305
  inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
 
306
 
307
- # Проверяем таймаут
308
- if timeout_mgr.timeout_occurred:
309
- raise TimeoutException("Timeout during tokenization")
310
-
311
- # Генерация ответа
312
  gen_kwargs = {
313
  "input_ids": inputs.input_ids,
314
  "max_new_tokens": max_tokens,
@@ -319,162 +201,80 @@ def respond(
319
  }
320
 
321
  outputs = model.generate(**gen_kwargs)
322
-
323
- # Останавливаем таймаут
324
  timeout_mgr.stop()
325
 
326
- # Декодирование полного вывода
327
- generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
328
-
329
- # Извлекаем только часть после "Ассистент: "
330
- response_start = generated_text.rfind("Ассистент: ") + len("Ассистент: ")
331
- if response_start >= len("Ассистент: "): # Проверяем, что "Ассистент: " найден
332
- response = generated_text[response_start:].strip()
333
- else:
334
- # Если не найдено, возвращаем весь текст
335
- response = generated_text.strip()
336
-
337
- end_time = time.time()
338
- generation_time = end_time - start_time
339
- logger.info(f"Генерация заняла {generation_time:.2f} секунд. Получен ответ длиной {len(response)} символов")
340
 
 
341
  return response
342
 
343
  except TimeoutException:
344
- logger.warning(f"Генерация превысила лимит времени ({generation_timeout} секунд)")
345
- return f"Генерация ответа превысила лимит времени ({generation_timeout} секунд). Попробуйте уменьшить количество токенов или задать более простой вопрос."
346
  except Exception as e:
347
- logger.error(f"Ошибка при генерации ответа: {str(e)}")
348
- return f"Произошла ошибка при генерации ответа: {str(e)}"
349
  finally:
350
- # Гарантируем остановку таймера
351
  if 'timeout_mgr' in locals():
352
  timeout_mgr.stop()
353
 
354
- # Настройка интерфейса Gradio
355
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
356
  gr.Markdown("# НереальностьQA - Чат с экспертом по эзотерике")
357
 
358
  if not HF_TOKEN:
359
- gr.Markdown("""
360
- ## ⚠️ Внимание: API токен не найден!
361
-
362
- Для работы с закрытой моделью необходимо добавить HF_TOKEN в секреты репозитория:
363
- 1. Settings > Repository secrets > New secret
364
- 2. Name: HF_TOKEN
365
- 3. Value: ваш токен доступа с huggingface.co/settings/tokens
366
- """, elem_id="warning-box")
367
 
368
  with gr.Row():
369
  with gr.Column(scale=4):
370
- chatbot = gr.Chatbot(label="Диалог") # Используем формат по умолчанию для совместимости
371
- user_input = gr.Textbox(
372
- placeholder="Введите ваш вопрос здесь...",
373
- label="Ваш вопрос",
374
- lines=2
375
- )
376
-
377
  with gr.Row():
378
  submit_btn = gr.Button("Отправить", variant="primary")
379
- clear_btn = gr.Button("Очистить историю")
380
-
381
  with gr.Column(scale=1):
382
- with gr.Accordion("Настройки генерации", open=False):
383
  system_msg = gr.Textbox(
384
- value="Твоя задача найти точный ответ на вопрос пользователя.",
385
  label="Системное сообщение",
386
  lines=4
387
  )
388
- max_tokens = gr.Slider(
389
- minimum=1,
390
- maximum=1024,
391
- value=64 if not cuda_available else 256, # Меньше токенов для CPU
392
- step=1,
393
- label="Максимальное число токенов"
394
- )
395
- temperature = gr.Slider(
396
- minimum=0.1,
397
- maximum=1.2,
398
- value=0.5,
399
- step=0.1,
400
- label="Температура"
401
- )
402
- top_p = gr.Slider(
403
- minimum=0.1,
404
- maximum=1.0,
405
- value=0.9,
406
- step=0.05,
407
- label="Top-p"
408
- )
409
- generation_timeout = gr.Slider(
410
- minimum=10,
411
- maximum=300,
412
- value=60 if cuda_available else 120, # Больше времени для CPU
413
- step=10,
414
- label="Таймаут генерации (секунды)"
415
- )
416
 
417
- with gr.Accordion("Информация о системе", open=True):
418
  system_info = {
419
  "Модель": model_name,
420
- "Режим работы": "GPU" if cuda_available else "CPU",
421
- "Zero GPU": "Активирован" if ZERO_GPU_ENABLED else "Не активирован",
422
- "Директория для кэша": CACHE_DIR,
423
- "Статус загрузки": "Успешно" if is_model_loaded else "Ошибка",
424
  "API токен": "Настроен" if HF_TOKEN else "Отсутствует"
425
  }
426
-
427
- info_text = gr.Markdown("\n".join([f"* **{k}**: {v}" for k, v in system_info.items()]))
428
 
429
- # Примеры вопросов
430
  with gr.Accordion("Примеры вопросов", open=True):
431
- examples = gr.Examples(
432
  examples=[
433
- "Что известно о мире отшедших душ и их взаимодействии с нашим миром?",
434
- "Что такое энергетическая ось человека и как она связана с его биополем?",
435
- "Расскажи о роли энергии мысли и желания в мире отшедших."
436
  ],
437
  inputs=user_input
438
  )
439
 
440
- # Функция обработки отправки сообщения
441
  def chat(message, history):
442
- if message == "":
443
  return history, ""
444
-
445
- # Генерируем ответ
446
- bot_message = respond(
447
- message,
448
- history,
449
- system_msg.value,
450
- max_tokens.value,
451
- temperature.value,
452
- top_p.value,
453
- generation_timeout.value
454
- )
455
-
456
- # Добавляем в историю и возвращаем
457
- history = history + [(message, bot_message)]
458
  return history, ""
459
 
460
- # Обработчики событий
461
- submit_btn.click(
462
- chat,
463
- inputs=[user_input, chatbot],
464
- outputs=[chatbot, user_input]
465
- )
466
-
467
- user_input.submit(
468
- chat,
469
- inputs=[user_input, chatbot],
470
- outputs=[chatbot, user_input]
471
- )
472
-
473
- clear_btn.click(
474
- lambda: ([], ""),
475
- outputs=[chatbot, user_input]
476
- )
477
 
478
- # Запуск приложения
479
  if __name__ == "__main__":
480
  demo.launch()
 
7
  import gc
8
  import threading
9
  import json
10
+ from transformers import AutoModelForCausalLM, AutoTokenizer, AutoConfig
11
 
12
  # Настройка логирования
13
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
15
 
16
  # Проверка наличия ZERO GPU
17
  ZERO_GPU_ENABLED = os.environ.get("HF_ZERO_GPU", "0") == "1"
18
+ logger.info(f"Zero GPU активирован: {ZERO_GPU_ENABLED}")
 
 
 
19
 
20
  # Получаем API токен из переменных окружения
21
  HF_TOKEN = os.environ.get("HF_TOKEN", None)
22
+ logger.info("API токен найден" if HF_TOKEN else "API токен не найден! Добавьте HF_TOKEN в секреты репозитория")
 
 
 
23
 
24
  # Информация о системе и CUDA
25
  logger.info("===== Запуск приложения =====")
26
  logger.info(f"PyTorch: {torch.__version__}")
27
 
28
+ # Проверка CUDA
29
+ cuda_available = torch.cuda.is_available()
30
+ logger.info(f"CUDA доступен: {cuda_available}")
31
+ if cuda_available:
32
+ logger.info(f"Количество CUDA устройств: {torch.cuda.device_count()}")
33
+ for i in range(torch.cuda.device_count()):
34
+ logger.info(f"CUDA устройство {i}: {torch.cuda.get_device_name(i)}")
35
+ free_mem = torch.cuda.get_device_properties(i).total_memory - torch.cuda.memory_allocated(i)
36
+ logger.info(f"Устройство {i}: свободно {free_mem / 1024**3:.2f} ГБ")
37
+ else:
38
+ logger.info("CUDA недоступен, используется CPU")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # Настройка директорий
41
  user_home = os.path.expanduser("~")
42
  DISK_DIR = os.path.join(user_home, "app_data")
 
 
 
 
 
 
43
  CACHE_DIR = os.path.join(DISK_DIR, "models_cache")
 
 
 
44
  os.makedirs(CACHE_DIR, exist_ok=True)
 
 
 
45
  os.environ["TRANSFORMERS_CACHE"] = CACHE_DIR
46
  os.environ["HF_HOME"] = CACHE_DIR
47
+ logger.info(f"Используем директорию для кэша: {CACHE_DIR}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # Модель
50
+ model_name = "unsloth/Phi-3.5-mini-instruct"
51
  logger.info(f"Выбрана модель: {model_name}")
52
 
53
+ # Глобальные переменные
54
  model = None
55
  tokenizer = None
56
  is_model_loaded = False
 
 
57
  DEFAULT_EOS_TOKEN = "</s>"
58
 
59
+ # Класс для таймаута
60
  class TimeoutManager:
61
  def __init__(self, seconds):
62
  self.seconds = seconds
 
83
  class TimeoutException(Exception):
84
  pass
85
 
86
+ # Очистка памяти
87
  def clear_memory():
88
  if cuda_available:
89
  torch.cuda.empty_cache()
90
  gc.collect()
91
 
92
+ # Загрузка модели
93
  def load_model():
94
  global model, tokenizer, is_model_loaded
95
 
 
 
 
96
  try:
 
97
  clear_memory()
98
 
99
  logger.info("Загружаем токенизатор...")
 
101
  model_name,
102
  token=HF_TOKEN,
103
  cache_dir=CACHE_DIR,
104
+ local_files_only=False,
105
+ revision="main"
106
  )
 
 
107
  if tokenizer.pad_token is None:
108
  tokenizer.pad_token = tokenizer.eos_token
109
+ logger.info(f"Токенизатор загружен: vocab_size={tokenizer.vocab_size}")
110
+
111
+ logger.info("Загружаем конфигурацию модели...")
112
+ config = AutoConfig.from_pretrained(model_name, token=HF_TOKEN, cache_dir=CACHE_DIR)
113
+ logger.info(f"Конфигурация модели: {config}")
114
 
115
  logger.info("Загружаем модель...")
 
116
  model_kwargs = {
117
  "cache_dir": CACHE_DIR,
118
  "trust_remote_code": True,
119
+ "token": HF_TOKEN,
120
+ "config": config
121
  }
122
 
 
123
  if cuda_available:
124
  logger.info("Загружаем модель в режиме GPU...")
125
  model_kwargs.update({
126
  "torch_dtype": torch.float16,
127
+ "device_map": "auto",
128
+ "load_in_4bit": True # Оптимизация от unsloth
129
  })
130
  else:
131
  logger.info("Загружаем модель в режиме CPU...")
132
  model_kwargs.update({
133
  "torch_dtype": torch.float32,
134
+ "load_in_4bit": False
135
  })
136
 
 
137
  model = AutoModelForCausalLM.from_pretrained(
138
  model_name,
139
  **model_kwargs
140
  )
141
 
 
142
  if not cuda_available:
143
  model = model.to("cpu")
144
 
145
  device_info = next(model.parameters()).device
146
+ logger.info(f"Модель загружена на устройство: {device_info}")
147
 
148
  is_model_loaded = True
149
+ return f"Модель загружена на {device_info}"
150
  except Exception as e:
151
+ logger.error(f"Ошибка загрузки модели: {str(e)}")
 
152
  is_model_loaded = False
153
+ return f"Ошибка загрузки модели: {str(e)}"
154
 
155
+ # Загружаем модель при запуске
156
  start_time = time.time()
157
  load_result = load_model()
158
+ logger.info(f"Загрузка заняла {time.time() - start_time:.2f} секунд. Результат: {load_result}")
 
 
 
 
 
 
159
 
160
+ # Шаблон для генерации
161
+ EOS_TOKEN = tokenizer.eos_token if tokenizer and tokenizer.eos_token else DEFAULT_EOS_TOKEN
162
+ qa_prompt = "<|user|>{}\n<|assistant|> {}" # Формат для Phi-3.5-mini-instruct
 
 
163
 
164
+ # Функция генерации ответа
165
+ def respond(message, history, system_message, max_tokens, temperature, top_p, generation_timeout):
 
 
 
 
 
 
 
166
  global model, tokenizer, is_model_loaded
167
 
 
168
  if not is_model_loaded or model is None or tokenizer is None:
169
+ return "Модель не загружена. Проверьте логи или добавьте HF_TOKEN."
 
 
 
170
 
 
171
  clear_memory()
 
 
172
  start_time = time.time()
173
 
174
+ # Форматирование истории
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  full_prompt = ""
176
  if system_message:
177
  full_prompt += qa_prompt.format(system_message, "") + "\n"
178
+
179
+ for user_msg, assistant_msg in history:
180
  if user_msg and assistant_msg:
181
  full_prompt += qa_prompt.format(user_msg, assistant_msg) + EOS_TOKEN + "\n"
182
+
183
  full_prompt += qa_prompt.format(message, "")
184
 
185
+ logger.info(f"Генерируем ответ на: '{message[:50]}...'")
186
 
187
  try:
 
188
  timeout_mgr = TimeoutManager(generation_timeout)
189
  timeout_mgr.start()
190
 
 
191
  inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
192
+ timeout_mgr.check_timeout()
193
 
 
 
 
 
 
194
  gen_kwargs = {
195
  "input_ids": inputs.input_ids,
196
  "max_new_tokens": max_tokens,
 
201
  }
202
 
203
  outputs = model.generate(**gen_kwargs)
 
 
204
  timeout_mgr.stop()
205
 
206
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
207
+ response_start = response.rfind("<|assistant|> ") + len("<|assistant|> ")
208
+ response = response[response_start:].strip() if response_start >= len("<|assistant|> ") else response.strip()
 
 
 
 
 
 
 
 
 
 
 
209
 
210
+ logger.info(f"Генерация заняла {time.time() - start_time:.2f} секунд")
211
  return response
212
 
213
  except TimeoutException:
214
+ return f"Таймаут генерации ({generation_timeout} секунд)."
 
215
  except Exception as e:
216
+ logger.error(f"Ошибка генерации: {str(e)}")
217
+ return f"Ошибка: {str(e)}"
218
  finally:
 
219
  if 'timeout_mgr' in locals():
220
  timeout_mgr.stop()
221
 
222
+ # Интерфейс Gradio
223
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
224
  gr.Markdown("# НереальностьQA - Чат с экспертом по эзотерике")
225
 
226
  if not HF_TOKEN:
227
+ gr.Markdown("⚠️ Добавьте HF_TOKEN в секреты репозитория!")
 
 
 
 
 
 
 
228
 
229
  with gr.Row():
230
  with gr.Column(scale=4):
231
+ chatbot = gr.Chatbot(label="Диалог")
232
+ user_input = gr.Textbox(placeholder="Введите вопрос...", label="Ваш вопрос", lines=2)
 
 
 
 
 
233
  with gr.Row():
234
  submit_btn = gr.Button("Отправить", variant="primary")
235
+ clear_btn = gr.Button("Очистить")
236
+
237
  with gr.Column(scale=1):
238
+ with gr.Accordion("Настройки", open=False):
239
  system_msg = gr.Textbox(
240
+ value="Твоя задача дать точный ответ на вопрос пользователя.",
241
  label="Системное сообщение",
242
  lines=4
243
  )
244
+ max_tokens = gr.Slider(1, 1024, value=256, step=1, label="Макс. токенов")
245
+ temperature = gr.Slider(0.1, 1.2, value=0.7, step=0.1, label="Температура")
246
+ top_p = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-p")
247
+ generation_timeout = gr.Slider(10, 300, value=60, step=10, label="Таймаут (с)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
+ with gr.Accordion("Информация", open=True):
250
  system_info = {
251
  "Модель": model_name,
252
+ "Режим": "GPU" if cuda_available else "CPU",
253
+ "Статус": "Успешно" if is_model_loaded else "Ошибка",
 
 
254
  "API токен": "Настроен" if HF_TOKEN else "Отсутствует"
255
  }
256
+ gr.Markdown("\n".join([f"* **{k}**: {v}" for k, v in system_info.items()]))
 
257
 
 
258
  with gr.Accordion("Примеры вопросов", open=True):
259
+ gr.Examples(
260
  examples=[
261
+ "Что известно о мире отшедших душ?",
262
+ "Что такое энергетическая ось человека?",
263
+ "Роль энергии мысли в мире отшедших?"
264
  ],
265
  inputs=user_input
266
  )
267
 
 
268
  def chat(message, history):
269
+ if not message:
270
  return history, ""
271
+ bot_message = respond(message, history, system_msg.value, max_tokens.value, temperature.value, top_p.value, generation_timeout.value)
272
+ history.append((message, bot_message))
 
 
 
 
 
 
 
 
 
 
 
 
273
  return history, ""
274
 
275
+ submit_btn.click(chat, [user_input, chatbot], [chatbot, user_input])
276
+ user_input.submit(chat, [user_input, chatbot], [chatbot, user_input])
277
+ clear_btn.click(lambda: ([], ""), None, [chatbot, user_input])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
 
 
279
  if __name__ == "__main__":
280
  demo.launch()