AnatoliiG commited on
Commit
6c363d4
·
1 Parent(s): f314e13

Update callbacks.py

Browse files
Files changed (1) hide show
  1. src/ui/callbacks.py +60 -27
src/ui/callbacks.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
 
3
  import gradio as gr
4
  import PyPDF2
@@ -53,6 +54,7 @@ def bot_response(
53
  messages = [{"role": "system", "content": system_prompt}]
54
  file_info, file_content = "", ""
55
 
 
56
  if uploaded_file and os.path.exists(uploaded_file):
57
  filename = os.path.basename(uploaded_file)
58
  size_kb = os.path.getsize(uploaded_file) / 1024
@@ -75,28 +77,44 @@ def bot_response(
75
  except Exception as e:
76
  file_info = f"❌ **Ошибка файла:** `{filename}` ({e})"
77
 
 
78
  for msg in history[-7:]:
79
- messages.append(
80
- {"role": msg["role"], "content": get_clean_text(msg["content"])}
81
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  history.append({"role": "assistant", "content": "⏳ Инициализация..."})
84
  yield history
85
 
 
86
  search_info = ""
87
  if use_search:
88
  history[-1]["content"] = (
89
  file_info + "\n" if file_info else ""
90
- ) + "🤔 Агент анализирует запрос..."
91
  yield history
92
 
93
  agent_messages = [
94
  {
95
  "role": "system",
96
  "content": (
97
- "Ты AI-агент. Оцени, нужен ли поиск в интернете для ответа на последний вопрос пользователя. "
98
- "Если вопрос требует свежих новостей, фактов или данных извне — напиши ТОЛЬКО поисковый запрос (2-5 слов, без кавычек и лишних слов). "
99
- "Если поиск не нужен (это простое общение, приветствие, или ответ есть в тексте файла) ответь ровно одним словом: NO_SEARCH."
100
  ),
101
  }
102
  ]
@@ -106,26 +124,27 @@ def bot_response(
106
  agent_messages.append({"role": "user", "content": msg["content"]})
107
 
108
  try:
109
- # Увеличиваем лимит токенов до 512, чтобы Reasoning-модель успела подумать
110
  eval_response = engine.generate(
111
- messages=agent_messages, max_tokens=512, temperature=0.1, stream=False
 
 
 
112
  )
113
 
114
- raw_query = eval_response["choices"][0]["message"]["content"].strip()
115
 
116
- # ФИЛЬТРУЕМ ТЕГИ <think>
117
- if "</think>" in raw_query:
118
- # Берем всё, что модель написала ПОСЛЕ окончания размышлений
119
- clean_query = raw_query.split("</think>")[-1].strip()
120
- elif raw_query.startswith("<think>"):
121
- # Если тег не закрылся из-за лимита токенов, отменяем поиск
122
- clean_query = "NO_SEARCH"
123
- else:
124
- clean_query = raw_query.strip()
125
 
126
- clean_query = clean_query.replace('"', "").replace("'", "")
 
 
 
 
 
 
127
 
128
- if clean_query and "NO_SEARCH" not in clean_query.upper():
129
  search_info = f'🌐 Ищем: *"{clean_query}"*...'
130
  history[-1]["content"] = (
131
  f"{file_info + '\n' if file_info else ''}{search_info}"
@@ -136,7 +155,6 @@ def bot_response(
136
 
137
  if search_results:
138
  search_info = f'🌐 Найдено {len(search_results)} результатов по запросу *"{clean_query}"*'
139
-
140
  search_context = (
141
  "СВЕЖИЕ РЕЗУЛЬТАТЫ ПОИСКА ИЗ ИНТЕРНЕТА ДЛЯ ТВОЕГО ОТВЕТА:\n\n"
142
  )
@@ -172,6 +190,7 @@ def bot_response(
172
  history[-1]["content"] = status_header + "⏳ Генерация ответа..."
173
  yield history
174
 
 
175
  try:
176
  stream = engine.generate(
177
  messages=messages,
@@ -185,14 +204,28 @@ def bot_response(
185
  if delta.get("content"):
186
  partial_text += delta["content"]
187
 
188
- # КРАСИВЫЙ UI-ФИЛЬТР ДЛЯ ФИНАЛЬНОГО ОТВЕТА
189
- # Превращаем <think> в красивый блок цитаты
190
- display_text = partial_text.replace(
191
- "<think>", "*(🤔 Внутренние размышления модели:)*\n> "
192
- ).replace("</think>", "\n\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
  history[-1]["content"] = status_header + display_text
195
  yield history
 
196
  except Exception as e:
197
  history[-1]["content"] = status_header + f"\n\n❌ Ошибка: {str(e)}"
198
  yield history
 
1
  import os
2
+ import re
3
 
4
  import gradio as gr
5
  import PyPDF2
 
54
  messages = [{"role": "system", "content": system_prompt}]
55
  file_info, file_content = "", ""
56
 
57
+ # --- 1. ОБРАБОТКА ФАЙЛА ---
58
  if uploaded_file and os.path.exists(uploaded_file):
59
  filename = os.path.basename(uploaded_file)
60
  size_kb = os.path.getsize(uploaded_file) / 1024
 
77
  except Exception as e:
78
  file_info = f"❌ **Ошибка файла:** `{filename}` ({e})"
79
 
80
+ # --- 2. ОЧИСТКА И ФОРМИРОВАНИЕ ИСТОРИИ (ИСПРАВЛЕН БАГ ГАЛЛЮЦИНАЦИЙ) ---
81
  for msg in history[-7:]:
82
+ content = str(msg["content"])
83
+
84
+ if msg["role"] == "assistant":
85
+ # 1. Отрезаем UI-плашку статусов (всё, что до разделителя ---)
86
+ if "---\n\n" in content:
87
+ content = content.split("---\n\n", 1)[-1]
88
+
89
+ # 2. Полностью удаляем прошлые размышления модели из контекста,
90
+ # чтобы они не переполняли память и не путали модель
91
+ content = re.sub(r"<details.*?>.*?</details>", "", content, flags=re.DOTALL)
92
+ content = content.strip()
93
+
94
+ # Если после очистки пусто, ставим заглушку
95
+ if not content:
96
+ content = "*(размышления скрыты)*"
97
+
98
+ messages.append({"role": msg["role"], "content": get_clean_text(content)})
99
 
100
  history.append({"role": "assistant", "content": "⏳ Инициализация..."})
101
  yield history
102
 
103
+ # --- 3. АГЕНТ ПОИСКА ---
104
  search_info = ""
105
  if use_search:
106
  history[-1]["content"] = (
107
  file_info + "\n" if file_info else ""
108
+ ) + "🤔 Агент анализирует необходимость поиска..."
109
  yield history
110
 
111
  agent_messages = [
112
  {
113
  "role": "system",
114
  "content": (
115
+ "Ты системный маршрутизатор. Оцени, нужен ли поиск в интернете для ответа на последний вопрос пользователя. "
116
+ "ПРАВИЛО 1: Рассуждай максимально коротко.\n"
117
+ "ПРАВИЛО 2: В самом конце, после своих мыслей, ОБЯЗАТЕЛЬНО выведи результат СТРОГО в формате [QUERY: твой поисковый запрос] или [QUERY: NO_SEARCH]."
118
  ),
119
  }
120
  ]
 
124
  agent_messages.append({"role": "user", "content": msg["content"]})
125
 
126
  try:
 
127
  eval_response = engine.generate(
128
+ messages=agent_messages,
129
+ max_tokens=800, # Даем больше токенов для размышлений
130
+ temperature=0.1,
131
+ stream=False,
132
  )
133
 
134
+ raw_query = eval_response["choices"][0]["message"]["content"]
135
 
136
+ # Ищем строгий ��ормат вывода, игнорируя весь остальной текст и теги
137
+ query_match = re.search(r"\[QUERY:\s*(.*?)\]", raw_query)
 
 
 
 
 
 
 
138
 
139
+ if query_match:
140
+ clean_query = (
141
+ query_match.group(1).strip().replace('"', "").replace("'", "")
142
+ )
143
+ else:
144
+ # Фолбэк на случай сбоя модели
145
+ clean_query = "NO_SEARCH"
146
 
147
+ if clean_query and clean_query.upper() != "NO_SEARCH":
148
  search_info = f'🌐 Ищем: *"{clean_query}"*...'
149
  history[-1]["content"] = (
150
  f"{file_info + '\n' if file_info else ''}{search_info}"
 
155
 
156
  if search_results:
157
  search_info = f'🌐 Найдено {len(search_results)} результатов по запросу *"{clean_query}"*'
 
158
  search_context = (
159
  "СВЕЖИЕ РЕЗУЛЬТАТЫ ПОИСКА ИЗ ИНТЕРНЕТА ДЛЯ ТВОЕГО ОТВЕТА:\n\n"
160
  )
 
190
  history[-1]["content"] = status_header + "⏳ Генерация ответа..."
191
  yield history
192
 
193
+ # --- 4. СТРИМИНГ И УМНЫЙ UI (UX как в ChatGPT o1) ---
194
  try:
195
  stream = engine.generate(
196
  messages=messages,
 
204
  if delta.get("content"):
205
  partial_text += delta["content"]
206
 
207
+ display_text = partial_text
208
+
209
+ # Если модель начала думать, но еще не закончила (открываем спойлер)
210
+ if "<think>" in partial_text and "</think>" not in partial_text:
211
+ display_text = (
212
+ partial_text.replace(
213
+ "<think>",
214
+ "<details open><summary><i>🧠 Идет анализ (в процессе)...</i></summary>\n\n",
215
+ )
216
+ + "\n\n</details>"
217
+ )
218
+
219
+ # Если модель закончила думать (закрываем спойлер автоматически!)
220
+ elif "</think>" in partial_text:
221
+ display_text = partial_text.replace(
222
+ "<think>",
223
+ "<details><summary><i>🧠 Анализ завершен (нажмите, чтобы раскрыть)</i></summary>\n\n",
224
+ ).replace("</think>", "\n\n</details>\n\n")
225
 
226
  history[-1]["content"] = status_header + display_text
227
  yield history
228
+
229
  except Exception as e:
230
  history[-1]["content"] = status_header + f"\n\n❌ Ошибка: {str(e)}"
231
  yield history