vortex123 commited on
Commit
3707e31
·
verified ·
1 Parent(s): b4fa6b2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -71
app.py CHANGED
@@ -15,14 +15,14 @@ if not GEMINI_API_KEY:
15
  exit()
16
  genai.configure(api_key=GEMINI_API_KEY)
17
 
18
- # Доступные модели
19
  AVAILABLE_MODELS = [
20
  "gemini-1.5-flash",
21
  "gemini-1.5-pro",
22
- "gemini-2.0-flash-thinking-exp",
23
  ]
24
 
25
- # Пытаемся инициализировать все модели заранее и складываем в словарь
26
  MODELS = {}
27
  for model_name in AVAILABLE_MODELS:
28
  try:
@@ -30,34 +30,32 @@ for model_name in AVAILABLE_MODELS:
30
  except Exception as e:
31
  print(f"[Предупреждение] Не удалось инициализировать модель {model_name}: {e}")
32
 
33
-
34
  ###############################################################################
35
- # 2. Утилиты для преобразования истории Gradio <-> Gemini
36
  ###############################################################################
37
 
38
  def _history_gradio_to_genai(history):
39
  """
40
- Gradio хранит чат как [(user_msg, bot_msg), (user_msg, bot_msg), ...].
41
- Для Gemini нужен формат [{'role': 'user', 'content': ...}, ...].
42
  """
43
  genai_history = []
44
  for user_text, bot_text in history:
45
  if user_text:
46
- genai_history.append({"role": "user", "content": user_text})
47
  if bot_text:
48
- genai_history.append({"role": "assistant", "content": bot_text})
49
  return genai_history
50
 
51
 
52
  ###############################################################################
53
- # 3. Функции-генераторы для запроса ответа от моделей (обычный/thinking)
54
  ###############################################################################
55
 
56
  async def _respond_stream(model_name, user_message, history):
57
  """
58
- Генерация ответа для Обычных моделей (без thinking) с помощью stream=True
59
- - Возвращаем куски текста через yield
60
- - В конце просто делаем return (без значения)
61
  """
62
  if model_name not in MODELS:
63
  yield "Ошибка: модель не найдена."
@@ -67,17 +65,18 @@ async def _respond_stream(model_name, user_message, history):
67
  genai_history = _history_gradio_to_genai(history)
68
 
69
  try:
 
70
  chat = model.start_chat(history=genai_history)
 
71
  response_stream = chat.send_message(user_message, stream=True)
72
 
73
  partial_text = ""
74
  for chunk in response_stream:
75
  chunk_text = chunk.text or ""
76
  partial_text += chunk_text
77
- # Возвращаем промежуточный вариант ответа
78
  yield partial_text
79
 
80
- return # Завершить генератор без возвращения значения
81
  except Exception as e:
82
  yield f"Ошибка при запросе к API: {e}"
83
  return
@@ -85,29 +84,28 @@ async def _respond_stream(model_name, user_message, history):
85
 
86
  async def _respond_thinking(model_name, user_message, history):
87
  """
88
- Генерация ответа для модели с "thinking" (например, gemini-2.0-flash-thinking-exp).
89
- 1) Сначала выдаём "Думаю..."
90
- 2) Затем, когда модель ответит (stream=False), выделяем thinking + финальный ответ.
91
- 3) Возвращаем (полезный_ответ, размышления) в виде кортежа.
92
- В Gradio это обычно [(assistant_text, thinking_text), ...].
93
  """
94
  if model_name not in MODELS:
95
- # Выдаем ошибку через yield
96
  yield "Ошибка: модель не найдена.", ""
97
  return
98
 
99
  model = MODELS[model_name]
100
  genai_history = _history_gradio_to_genai(history)
101
 
102
- # Шаг 1: временно "Думаю..."
103
  yield "Думаю...", ""
104
 
105
  try:
106
  chat = model.start_chat(history=genai_history)
 
107
  response = chat.send_message(user_message, stream=False)
108
 
109
  thinking_process_text = ""
110
  final_text = ""
 
111
  if response.candidates:
112
  parts = response.candidates[0].content.parts
113
  for p in parts:
@@ -116,7 +114,6 @@ async def _respond_thinking(model_name, user_message, history):
116
  else:
117
  final_text += p.text or ""
118
 
119
- # Возвращаем готовый ответ и размышления
120
  yield final_text, thinking_process_text
121
  return
122
  except Exception as e:
@@ -125,7 +122,7 @@ async def _respond_thinking(model_name, user_message, history):
125
 
126
 
127
  ###############################################################################
128
- # 4. Основная асинхронная функция Gradio, обрабатывающая новый пользовательский ввод
129
  ###############################################################################
130
 
131
  async def user_send_message(
@@ -136,29 +133,27 @@ async def user_send_message(
136
  ):
137
  """
138
  Параметры:
139
- user_message: вход от пользователя (текущая реплика)
140
- history: история [(user, assistant), ...]
141
- model_name: выбранная модель
142
  thinking_text: текущее содержимое поля «Размышления»
143
-
144
- Выход (через yield):
145
- (обновлённая история, обновлённое thinking_text)
146
  """
147
- # Если пользователь ничего не ввёл, просто возвращаем текущее состояние
148
  if not user_message.strip():
 
149
  yield history, thinking_text
150
  return
151
 
152
- # Добавляем новую запись в историю: ассистент пока None
153
  history.append((user_message, None))
154
 
155
- # Проверяем, thinking-модель ли
156
  if "thinking" in model_name.lower():
157
- # Обрабатываем через _respond_thinking
158
  async for (assistant_text, thought_text) in _respond_thinking(model_name, user_message, history):
159
- # Обновляем последнюю пару в истории
160
  history[-1] = (user_message, assistant_text)
161
- # Обновляем thinking_text
162
  yield history, thought_text
163
  return
164
  else:
@@ -167,7 +162,6 @@ async def user_send_message(
167
  async for chunk in _respond_stream(model_name, user_message, history):
168
  partial_answer = chunk
169
  history[-1] = (user_message, partial_answer)
170
- # В обычном режиме thinking_text = ""
171
  yield history, ""
172
  return
173
 
@@ -177,40 +171,33 @@ async def user_send_message(
177
  ###############################################################################
178
 
179
  def clear_all():
180
- """
181
- Полная очистка чата и размышлений.
182
- """
183
- return [], "" # (history, thinking_store)
184
 
185
 
186
  ###############################################################################
187
- # 6. Определяем интерфейс Gradio
188
  ###############################################################################
189
 
190
  with gr.Blocks() as demo:
191
- gr.Markdown("## Gemini Chatbot (с сохранением истории и thinking-режимом)")
192
 
193
  with gr.Row():
194
  model_dropdown = gr.Dropdown(
195
  choices=AVAILABLE_MODELS,
196
  value="gemini-1.5-flash",
197
- label="Выберите модель",
198
  )
199
  clear_button = gr.Button("Очистить чат")
200
 
201
- # Храним историю чата в gr.State
202
- history_state = gr.State([]) # список кортежей (user, assistant)
203
- # Храним «размышления» отдельно
204
- thinking_store = gr.State("")
205
 
206
  chatbot = gr.Chatbot(label="Диалог с Gemini")
 
207
 
208
- user_input = gr.Textbox(
209
- label="Ваш вопрос",
210
- placeholder="Введите вопрос и нажмите Enter...",
211
- )
212
-
213
- # Отдельный блок для показа «размышлений»
214
  thinking_output = gr.Textbox(
215
  label="Размышления (только для gemini-2.0-flash-thinking-exp)",
216
  interactive=False
@@ -218,62 +205,66 @@ with gr.Blocks() as demo:
218
 
219
  send_btn = gr.Button("Отправить")
220
 
221
- # Связка: кнопка «Отправить» => user_send_message => обновление истории и размышлений
222
- send_btn.click(
223
  fn=user_send_message,
224
  inputs=[user_input, history_state, model_dropdown, thinking_store],
225
  outputs=[history_state, thinking_store],
226
  queue=True
227
- ).then(
228
- # После того как получили новую историю, обновляем чат
 
229
  fn=lambda h: h,
230
  inputs=[history_state],
231
  outputs=[chatbot],
232
  queue=True
233
- ).then(
234
- # После этого выводим thinking_store
 
235
  fn=lambda t: t,
236
  inputs=[thinking_store],
237
  outputs=[thinking_output],
238
  queue=True
239
  )
240
 
241
- # Аналогичное поведение при нажатии Enter в поле ввода
242
- user_input.submit(
243
  fn=user_send_message,
244
  inputs=[user_input, history_state, model_dropdown, thinking_store],
245
  outputs=[history_state, thinking_store],
246
  queue=True
247
- ).then(
 
248
  fn=lambda h: h,
249
  inputs=[history_state],
250
  outputs=[chatbot],
251
  queue=True
252
- ).then(
 
253
  fn=lambda t: t,
254
  inputs=[thinking_store],
255
  outputs=[thinking_output],
256
  queue=True
257
  )
258
 
259
- # Кнопка «Очистить» сбрасывает историю и thinking_store
260
- clear_button.click(
261
  fn=clear_all,
262
  inputs=[],
263
  outputs=[history_state, thinking_store],
264
  queue=False
265
- ).then(
266
- # Затем обновляем чат
267
  fn=lambda h: h,
268
  inputs=[history_state],
269
  outputs=[chatbot]
270
- ).then(
271
- # И обнуляем thinking_output
272
  fn=lambda _: "",
273
  inputs=[],
274
  outputs=[thinking_output]
275
  )
276
 
277
- # Запуск Gradio
278
  if __name__ == "__main__":
279
  demo.launch()
 
15
  exit()
16
  genai.configure(api_key=GEMINI_API_KEY)
17
 
18
+ # Список доступных моделей
19
  AVAILABLE_MODELS = [
20
  "gemini-1.5-flash",
21
  "gemini-1.5-pro",
22
+ "gemini-2.0-flash-thinking-exp"
23
  ]
24
 
25
+ # Инициализируем модели и храним их в словаре
26
  MODELS = {}
27
  for model_name in AVAILABLE_MODELS:
28
  try:
 
30
  except Exception as e:
31
  print(f"[Предупреждение] Не удалось инициализировать модель {model_name}: {e}")
32
 
 
33
  ###############################################################################
34
+ # 2. Утилиты для преобразования истории между Gradio-форматом и Gemini
35
  ###############################################################################
36
 
37
  def _history_gradio_to_genai(history):
38
  """
39
+ Gradio хранит историю как [(user_msg, bot_msg), (user_msg, bot_msg), ...].
40
+ Библиотека google.generativeai ждёт [{'role': 'user', 'parts': ...}, {'role': 'assistant', 'parts': ...}, ...].
41
  """
42
  genai_history = []
43
  for user_text, bot_text in history:
44
  if user_text:
45
+ genai_history.append({"role": "user", "parts": user_text})
46
  if bot_text:
47
+ genai_history.append({"role": "assistant", "parts": bot_text})
48
  return genai_history
49
 
50
 
51
  ###############################################################################
52
+ # 3. Асинхронные генераторы для получения ответа от моделей
53
  ###############################################################################
54
 
55
  async def _respond_stream(model_name, user_message, history):
56
  """
57
+ Генерация ответа (постепенного, stream=True) для обычных моделей (без thinking).
58
+ Возвращаем кусочки текста через yield.
 
59
  """
60
  if model_name not in MODELS:
61
  yield "Ошибка: модель не найдена."
 
65
  genai_history = _history_gradio_to_genai(history)
66
 
67
  try:
68
+ # Создаём чат с уже накопленной историей
69
  chat = model.start_chat(history=genai_history)
70
+ # Запрашиваем стриминг (постепенный) ответа
71
  response_stream = chat.send_message(user_message, stream=True)
72
 
73
  partial_text = ""
74
  for chunk in response_stream:
75
  chunk_text = chunk.text or ""
76
  partial_text += chunk_text
 
77
  yield partial_text
78
 
79
+ return # Завершить генератор без значения
80
  except Exception as e:
81
  yield f"Ошибка при запросе к API: {e}"
82
  return
 
84
 
85
  async def _respond_thinking(model_name, user_message, history):
86
  """
87
+ Генерация ответа для thinking-модели (gemini-2.0-flash-thinking-exp).
88
+ 1) Сначала отдаём "Думаю..."
89
+ 2) Затем формируем итоговый ответ + собираем «размышления» part.thought == True.
 
 
90
  """
91
  if model_name not in MODELS:
 
92
  yield "Ошибка: модель не найдена.", ""
93
  return
94
 
95
  model = MODELS[model_name]
96
  genai_history = _history_gradio_to_genai(history)
97
 
98
+ # Шаг 1: сообщаем "Думаю..."
99
  yield "Думаю...", ""
100
 
101
  try:
102
  chat = model.start_chat(history=genai_history)
103
+ # Без стриминга — дожидаемся полного ответа
104
  response = chat.send_message(user_message, stream=False)
105
 
106
  thinking_process_text = ""
107
  final_text = ""
108
+
109
  if response.candidates:
110
  parts = response.candidates[0].content.parts
111
  for p in parts:
 
114
  else:
115
  final_text += p.text or ""
116
 
 
117
  yield final_text, thinking_process_text
118
  return
119
  except Exception as e:
 
122
 
123
 
124
  ###############################################################################
125
+ # 4. Основная функция для обработки нового пользовательского сообщения в Gradio
126
  ###############################################################################
127
 
128
  async def user_send_message(
 
133
  ):
134
  """
135
  Параметры:
136
+ user_message : строка, введённая пользователем
137
+ history : текущая история [(user, assistant), ...]
138
+ model_name : выбранная модель
139
  thinking_text: текущее содержимое поля «Размышления»
140
+
141
+ Возвращаем (обновлённую историю, обновлённое thinking_text) пошагово через yield.
 
142
  """
 
143
  if not user_message.strip():
144
+ # Если пользователь не ввёл ничего, не делаем запрос
145
  yield history, thinking_text
146
  return
147
 
148
+ # Добавляем новый шаг в историю (ассистент пока None)
149
  history.append((user_message, None))
150
 
151
+ # Если модель — thinking
152
  if "thinking" in model_name.lower():
 
153
  async for (assistant_text, thought_text) in _respond_thinking(model_name, user_message, history):
154
+ # Обновляем последний элемент истории
155
  history[-1] = (user_message, assistant_text)
156
+ # Записываем «размышления» во второе поле
157
  yield history, thought_text
158
  return
159
  else:
 
162
  async for chunk in _respond_stream(model_name, user_message, history):
163
  partial_answer = chunk
164
  history[-1] = (user_message, partial_answer)
 
165
  yield history, ""
166
  return
167
 
 
171
  ###############################################################################
172
 
173
  def clear_all():
174
+ """ Очистить историю и размышления. """
175
+ return [], ""
 
 
176
 
177
 
178
  ###############################################################################
179
+ # 6. Gradio-интерфейс
180
  ###############################################################################
181
 
182
  with gr.Blocks() as demo:
183
+ gr.Markdown("## Chat с Gemini (поддержка разных моделей и «thinking»-режима)")
184
 
185
  with gr.Row():
186
  model_dropdown = gr.Dropdown(
187
  choices=AVAILABLE_MODELS,
188
  value="gemini-1.5-flash",
189
+ label="Выберите модель"
190
  )
191
  clear_button = gr.Button("Очистить чат")
192
 
193
+ # Состояние для хранения истории и размышлений
194
+ history_state = gr.State([]) # [(user, assistant), ...]
195
+ thinking_store = gr.State("") # текст размышлений
 
196
 
197
  chatbot = gr.Chatbot(label="Диалог с Gemini")
198
+ user_input = gr.Textbox(label="Ваш вопрос", placeholder="Введите текст...")
199
 
200
+ # Поле для размышлений
 
 
 
 
 
201
  thinking_output = gr.Textbox(
202
  label="Размышления (только для gemini-2.0-flash-thinking-exp)",
203
  interactive=False
 
205
 
206
  send_btn = gr.Button("Отправить")
207
 
208
+ # 1) При нажатии «Отправить»
209
+ send_chain = send_btn.click(
210
  fn=user_send_message,
211
  inputs=[user_input, history_state, model_dropdown, thinking_store],
212
  outputs=[history_state, thinking_store],
213
  queue=True
214
+ )
215
+ # Затем обновляем чат
216
+ send_chain.then(
217
  fn=lambda h: h,
218
  inputs=[history_state],
219
  outputs=[chatbot],
220
  queue=True
221
+ )
222
+ # И отображаем размышления
223
+ send_chain.then(
224
  fn=lambda t: t,
225
  inputs=[thinking_store],
226
  outputs=[thinking_output],
227
  queue=True
228
  )
229
 
230
+ # 2) При нажатии Enter в textbox
231
+ submit_chain = user_input.submit(
232
  fn=user_send_message,
233
  inputs=[user_input, history_state, model_dropdown, thinking_store],
234
  outputs=[history_state, thinking_store],
235
  queue=True
236
+ )
237
+ submit_chain.then(
238
  fn=lambda h: h,
239
  inputs=[history_state],
240
  outputs=[chatbot],
241
  queue=True
242
+ )
243
+ submit_chain.then(
244
  fn=lambda t: t,
245
  inputs=[thinking_store],
246
  outputs=[thinking_output],
247
  queue=True
248
  )
249
 
250
+ # 3) Кнопка «Очистить»
251
+ clear_chain = clear_button.click(
252
  fn=clear_all,
253
  inputs=[],
254
  outputs=[history_state, thinking_store],
255
  queue=False
256
+ )
257
+ clear_chain.then(
258
  fn=lambda h: h,
259
  inputs=[history_state],
260
  outputs=[chatbot]
261
+ )
262
+ clear_chain.then(
263
  fn=lambda _: "",
264
  inputs=[],
265
  outputs=[thinking_output]
266
  )
267
 
268
+ # Запуск приложения
269
  if __name__ == "__main__":
270
  demo.launch()