X commited on
Commit
4bc8cbc
·
verified ·
1 Parent(s): 1dd974b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +32 -284
app.py CHANGED
@@ -1,57 +1,18 @@
1
- import gradio as gr
2
- from transformers import AutoModelForCausalLM, AutoTokenizer
3
- import torch
4
- import json
5
- from pathlib import Path
6
- from datetime import datetime
7
- import requests
8
-
9
- # === ЗАГРУЗКА МОДЕЛИ ===
10
  print("🚀 Загрузка модели...")
11
  model_id = "OpenRussianAI/OpenAirAI-X"
12
  tokenizer = AutoTokenizer.from_pretrained(model_id)
 
 
 
 
 
13
  model = AutoModelForCausalLM.from_pretrained(model_id)
14
  device = "cuda" if torch.cuda.is_available() else "cpu"
15
  model = model.to(device).eval()
16
  print(f"✅ Модель загружена на {device}")
17
 
18
- # === ХРАНИЛИЩЕ ИСТОРИИ ===
19
- HISTORY_DIR = Path("chat_history")
20
- HISTORY_DIR.mkdir(exist_ok=True)
21
-
22
- def get_history_path(username):
23
- safe_name = "".join(c for c in username if c.isalnum() or c in ('-', '_')).lower()
24
- return HISTORY_DIR / f"{safe_name}.json"
25
-
26
- def load_history(username):
27
- path = get_history_path(username)
28
- if path.exists():
29
- with open(path, "r", encoding="utf-8") as f:
30
- return json.load(f)
31
- return {}
32
-
33
- def save_history(username, history_data):
34
- path = get_history_path(username)
35
- with open(path, "w", encoding="utf-8") as f:
36
- json.dump(history_data, f, ensure_ascii=False, indent=2)
37
-
38
- def check_if_pro(token):
39
- if not token or not token.startswith("hf_"):
40
- return False
41
- try:
42
- response = requests.get(
43
- "https://huggingface.co/api/whoami-v2",
44
- headers={"Authorization": f"Bearer {token}"},
45
- timeout=5
46
- )
47
- if response.status_code == 200:
48
- data = response.json()
49
- return data.get("isPro", False) or data.get("plan", {}).get("name") == "PRO"
50
- except Exception as e:
51
- print(f"Ошибка проверки PRO: {e}")
52
- return False
53
-
54
- # === ГЕНЕРАЦИЯ ОТВЕТА ===
55
  def generate_response(message, history, username, current_chat_id):
56
  if not username:
57
  gr.Warning("Сначала введите имя пользователя!")
@@ -60,8 +21,10 @@ def generate_response(message, history, username, current_chat_id):
60
  if not message.strip():
61
  return history, gr.update(), current_chat_id, gr.update()
62
 
 
63
  history = history + [{"role": "user", "content": message}]
64
 
 
65
  prompt = ""
66
  for msg in history:
67
  if msg["role"] == "user":
@@ -71,19 +34,37 @@ def generate_response(message, history, username, current_chat_id):
71
  prompt += "AI:"
72
 
73
  inputs = tokenizer(prompt, return_tensors="pt").to(device)
 
74
  with torch.no_grad():
75
  outputs = model.generate(
76
  **inputs,
77
- max_new_tokens=256,
78
  temperature=0.7,
79
  top_p=0.9,
80
  do_sample=True,
81
- pad_token_id=tokenizer.eos_token_id
 
 
82
  )
83
 
84
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
85
- ai_response = response.split("AI:")[-1].strip()
 
 
 
 
 
86
 
 
 
 
 
 
 
 
 
 
 
87
  history = history + [{"role": "assistant", "content": ai_response}]
88
 
89
  if username and current_chat_id:
@@ -98,237 +79,4 @@ def generate_response(message, history, username, current_chat_id):
98
  save_history(username, history_data)
99
 
100
  chat_list_choices = get_chat_list(username)
101
- # Возвращаем обновленный список через gr.update
102
- return history, gr.update(value=""), current_chat_id, gr.update(choices=chat_list_choices)
103
-
104
- # === УПРАВЛЕНИЕ ЧАТАМИ ===
105
- def new_chat(username):
106
- if not username:
107
- return [], None, gr.update(choices=[])
108
- chat_id = datetime.now().strftime("%Y%m%d_%H%M%S")
109
- history_data = load_history(username)
110
- history_data[chat_id] = {
111
- "title": "Новый чат",
112
- "created": datetime.now().isoformat(),
113
- "messages": []
114
- }
115
- save_history(username, history_data)
116
- chat_list_choices = get_chat_list(username)
117
- return [], chat_id, gr.update(choices=chat_list_choices, value=None)
118
-
119
- def load_chat(chat_title, username, current_chat_id):
120
- if not username or not chat_title:
121
- return [], current_chat_id
122
- history_data = load_history(username)
123
- for cid, data in history_data.items():
124
- if data["title"] == chat_title:
125
- return data["messages"], cid
126
- return [], current_chat_id
127
-
128
- def get_chat_list(username):
129
- if not username:
130
- return []
131
- history_data = load_history(username)
132
- sorted_chats = sorted(
133
- history_data.items(),
134
- key=lambda x: x[1].get("created", ""),
135
- reverse=True
136
- )
137
- return [data["title"] for _, data in sorted_chats]
138
-
139
- def delete_chat(chat_title, username):
140
- if not username or not chat_title:
141
- return [], {}, None, gr.update(choices=[])
142
- history_data = load_history(username)
143
- for cid, data in list(history_data.items()):
144
- if data["title"] == chat_title:
145
- del history_data[cid]
146
- break
147
- save_history(username, history_data)
148
- chat_list_choices = get_chat_list(username)
149
- return [], history_data, None, gr.update(choices=chat_list_choices, value=None)
150
-
151
- # === CSS ===
152
- CUSTOM_CSS = """
153
- .main-header {
154
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
155
- color: white;
156
- padding: 20px;
157
- border-radius: 12px;
158
- margin-bottom: 20px;
159
- text-align: center;
160
- }
161
- .main-header h1 { margin: 0; font-size: 2em; }
162
- .pro-badge {
163
- background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
164
- color: #333;
165
- padding: 4px 12px;
166
- border-radius: 20px;
167
- font-weight: bold;
168
- display: inline-block;
169
- margin-left: 10px;
170
- box-shadow: 0 2px 8px rgba(255, 215, 0, 0.4);
171
- }
172
- .user-info {
173
- padding: 10px;
174
- background: white;
175
- border-radius: 8px;
176
- margin-bottom: 10px;
177
- text-align: center;
178
- font-weight: 600;
179
- }
180
- .privacy-banner {
181
- background: linear-gradient(135deg, #10b981 0%, #059669 100%);
182
- color: white;
183
- padding: 15px 20px;
184
- border-radius: 10px;
185
- margin: 15px 0;
186
- display: flex;
187
- align-items: center;
188
- gap: 12px;
189
- font-size: 0.95em;
190
- box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
191
- }
192
- .privacy-banner .icon { font-size: 1.8em; }
193
- .privacy-banner strong { display: block; font-size: 1.1em; margin-bottom: 4px; }
194
- """
195
-
196
- PRIVACY_BANNER = """
197
- <div class="privacy-banner">
198
- <div class="icon">🔒</div>
199
- <div>
200
- <strong>Ваши данные в безопасности</strong>
201
- Все сообщения обрабатываются локально.
202
- <b>Данные НЕ передаются в OpenRussianAI</b>.
203
- История хранится только внутри контейнера.
204
- </div>
205
- </div>
206
- """
207
-
208
- # === ИНТЕРФЕЙС ===
209
- with gr.Blocks(title="OpenAirAI-X Chat") as demo:
210
- username_state = gr.State("")
211
- history_state = gr.State({})
212
- current_chat_id_state = gr.State(None)
213
-
214
- with gr.Column(visible=False) as main_interface:
215
- gr.HTML("<div class='main-header'><h1>🤖 OpenAirAI-X Chat</h1><p>Русскоязычный AI-ассистент</p></div>")
216
- gr.HTML(PRIVACY_BANNER)
217
-
218
- with gr.Row():
219
- with gr.Column(scale=1, min_width=250):
220
- user_info = gr.Textbox(label="Пользователь", value="Не авторизован", interactive=False, elem_classes="user-info")
221
- pro_badge = gr.HTML('<div class="pro-badge">👑 PRO</div>', visible=False)
222
- new_chat_btn = gr.Button("➕ Новый чат", variant="primary")
223
- gr.Markdown("### 📚 История чатов")
224
- # Используем Dropdown с allow_custom_value=False для стабильности
225
- chat_list = gr.Dropdown(choices=[], label="Ваши чаты", interactive=True, allow_custom_value=False, value=None)
226
- delete_chat_btn = gr.Button("🗑️ Удалить выбранный чат", variant="stop", size="sm")
227
- logout_btn = gr.Button("🚪 Выйти", size="sm")
228
-
229
- with gr.Column(scale=3):
230
- chatbot = gr.Chatbot(label="Диалог", height=500)
231
- with gr.Row():
232
- msg_input = gr.Textbox(placeholder="Напишите сообщение...", lines=2, scale=5, show_label=False)
233
- send_btn = gr.Button("📤 Отправить", variant="primary", scale=1)
234
-
235
- with gr.Column(visible=True) as login_screen:
236
- gr.HTML("<div class='main-header'><h1>🤖 OpenAirAI-X Chat</h1><p>Введите данные для входа</p></div>")
237
- gr.HTML(PRIVACY_BANNER)
238
- gr.Markdown("### 🔐 Вход в систему\nВведите ваше имя пользователя. История чатов будет привязана к этому имени.")
239
- username_input = gr.Textbox(label="Имя пользователя (HF Username)", placeholder="Например: RootLinux21")
240
- token_input = gr.Textbox(label="HF Token (необязательно, для PRO)", placeholder="hf_...")
241
- login_btn = gr.Button("🔑 Войти", variant="primary")
242
-
243
- # === ОБРАБОТЧИКИ ===
244
-
245
- def handle_login(username, token):
246
- if not username.strip():
247
- gr.Warning("Введите имя пользователя!")
248
- return gr.update(), gr.update(), "Не авторизован", "", {}, None, gr.update(choices=[], value=None), gr.update(visible=False)
249
-
250
- is_pro = check_if_pro(token)
251
- history_data = load_history(username)
252
- chat_list_choices = get_chat_list(username)
253
-
254
- current_chat_id = None
255
- if not history_data:
256
- chat_id = datetime.now().strftime("%Y%m%d_%H%M%S")
257
- history_data[chat_id] = {
258
- "title": "Новый чат",
259
- "created": datetime.now().isoformat(),
260
- "messages": []
261
- }
262
- save_history(username, history_data)
263
- current_chat_id = chat_id
264
- chat_list_choices = get_chat_list(username)
265
-
266
- pro_badge_html = '<div class="pro-badge">👑 PRO</div>'
267
- display_name = f"👤 {username}"
268
-
269
- return (
270
- gr.update(visible=True),
271
- gr.update(visible=False),
272
- display_name,
273
- username,
274
- history_data,
275
- current_chat_id,
276
- gr.update(choices=chat_list_choices, value=None),
277
- gr.update(visible=is_pro, value=pro_badge_html)
278
- )
279
-
280
- def handle_logout():
281
- return (
282
- gr.update(visible=False),
283
- gr.update(visible=True),
284
- "Не авторизован",
285
- "",
286
- {},
287
- None,
288
- gr.update(choices=[], value=None),
289
- gr.update(visible=False)
290
- )
291
-
292
- login_btn.click(
293
- fn=handle_login,
294
- inputs=[username_input, token_input],
295
- outputs=[main_interface, login_screen, user_info, username_state, history_state, current_chat_id_state, chat_list, pro_badge]
296
- )
297
-
298
- send_btn.click(
299
- fn=generate_response,
300
- inputs=[msg_input, chatbot, username_state, current_chat_id_state],
301
- outputs=[chatbot, msg_input, current_chat_id_state, chat_list]
302
- )
303
-
304
- msg_input.submit(
305
- fn=generate_response,
306
- inputs=[msg_input, chatbot, username_state, current_chat_id_state],
307
- outputs=[chatbot, msg_input, current_chat_id_state, chat_list]
308
- )
309
-
310
- new_chat_btn.click(
311
- fn=new_chat,
312
- inputs=[username_state],
313
- outputs=[chatbot, current_chat_id_state, chat_list]
314
- )
315
-
316
- chat_list.change(
317
- fn=load_chat,
318
- inputs=[chat_list, username_state, current_chat_id_state],
319
- outputs=[chatbot, current_chat_id_state]
320
- )
321
-
322
- delete_chat_btn.click(
323
- fn=delete_chat,
324
- inputs=[chat_list, username_state],
325
- outputs=[chatbot, history_state, current_chat_id_state, chat_list]
326
- )
327
-
328
- logout_btn.click(
329
- fn=handle_logout,
330
- inputs=None,
331
- outputs=[main_interface, login_screen, user_info, username_state, history_state, current_chat_id_state, chat_list, pro_badge]
332
- )
333
-
334
- demo.launch(server_name="0.0.0.0", server_port=7860, css=CUSTOM_CSS)
 
1
+ # === ЗАГРУЗКА МОДЕЛИ (ИСПРАВЛЕННАЯ ЧАСТЬ) ===
 
 
 
 
 
 
 
 
2
  print("🚀 Загрузка модели...")
3
  model_id = "OpenRussianAI/OpenAirAI-X"
4
  tokenizer = AutoTokenizer.from_pretrained(model_id)
5
+
6
+ # ВАЖНО: Убеждаемся, что у токенизатора есть pad_token
7
+ if tokenizer.pad_token is None:
8
+ tokenizer.pad_token = tokenizer.eos_token
9
+
10
  model = AutoModelForCausalLM.from_pretrained(model_id)
11
  device = "cuda" if torch.cuda.is_available() else "cpu"
12
  model = model.to(device).eval()
13
  print(f"✅ Модель загружена на {device}")
14
 
15
+ # === ГЕНЕРАЦИЯ ОТВЕТА (ПОЛНОСТЬЮ ПЕРЕПИСАННАЯ) ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def generate_response(message, history, username, current_chat_id):
17
  if not username:
18
  gr.Warning("Сначала введите имя пользователя!")
 
21
  if not message.strip():
22
  return history, gr.update(), current_chat_id, gr.update()
23
 
24
+ # Добавляем сообщение пользователя в историю
25
  history = history + [{"role": "user", "content": message}]
26
 
27
+ # Формируем промпт
28
  prompt = ""
29
  for msg in history:
30
  if msg["role"] == "user":
 
34
  prompt += "AI:"
35
 
36
  inputs = tokenizer(prompt, return_tensors="pt").to(device)
37
+
38
  with torch.no_grad():
39
  outputs = model.generate(
40
  **inputs,
41
+ max_new_tokens=128, # Уменьшим, чтобы модель не уходила в бред
42
  temperature=0.7,
43
  top_p=0.9,
44
  do_sample=True,
45
+ pad_token_id=tokenizer.eos_token_id,
46
+ eos_token_id=tokenizer.eos_token_id, # Важно!
47
+ repetition_penalty=1.2 # Штраф за повторения, чтобы убрать цикл "Я OpenAI"
48
  )
49
 
50
+ # Декодируем только НОВУЮ часть текста
51
+ generated_ids = outputs[0][inputs.input_ids.shape[-1]:]
52
+ response_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
53
+
54
+ # Очистка от мусора и обрезка
55
+ # Обычно модель может начать ответ с пробела или новой строки
56
+ ai_response = response_text.strip()
57
 
58
+ # Если модель все же написала "Пользователь:" или "AI:" в конце, обрезаем
59
+ stop_words = ["Пользователь:", "User:", "\n\n"]
60
+ for stop_word in stop_words:
61
+ if stop_word in ai_response:
62
+ ai_response = ai_response.split(stop_word)[0].strip()
63
+
64
+ # Если ответ пустой после очистки
65
+ if not ai_response:
66
+ ai_response = "..."
67
+
68
  history = history + [{"role": "assistant", "content": ai_response}]
69
 
70
  if username and current_chat_id:
 
79
  save_history(username, history_data)
80
 
81
  chat_list_choices = get_chat_list(username)
82
+ return history, gr.update(value=""), current_chat_id, gr.update(choices=chat_list_choices)