zelk12 commited on
Commit
5c05e3e
·
verified ·
1 Parent(s): c858e0c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +2 -397
app.py CHANGED
@@ -21,403 +21,8 @@ print("add API key complete ")
21
  print("add model")
22
 
23
  # --- Используем более подходящую модель для чата, если Gemma IT недоступна или неоптимальна ---
24
- # used_model = "gemma-3-27b-it" # Gemma может не поддерживать role='model' явно, Gemini Pro лучше для чата
25
- used_model = "gemini-1.5-flash-latest" # Рекомендуется для чата с историей
26
- # ------------------------------------------------------------------------------------------
27
- model = genai.GenerativeModel(used_model)
28
-
29
- print(f"add model {used_model} complete\n")
30
-
31
- def format_chat_history(messages: list) -> list:
32
- print("\nstart format history")
33
- """
34
- Formats the chat history into a structure Gemini can understand
35
- """
36
- formatted_history = []
37
- for message in messages:
38
- # Пропускаем пустые сообщения или сообщения без контента (на всякий случай)
39
- content = message.get("content")
40
- if not content:
41
- continue
42
-
43
- role = message.get("role")
44
- if role == "user":
45
- formatted_history.append({
46
- "role": "user",
47
- "parts": [content]
48
- })
49
- elif role == "assistant":
50
- # Пропускаем "thinking" сообщения, если они есть (хотя мы их убрали)
51
- if not message.get("metadata"):
52
- formatted_history.append({
53
- "role": "model", # Gemini API ожидает 'model' для ответов ассистента
54
- "parts": [content]
55
- })
56
- # Игнорируем другие возможные роли или типы сообщений
57
- print(f"Formatted history length: {len(formatted_history)}")
58
- print("return formatted history")
59
- return formatted_history
60
-
61
- # --- Новая функция для Undo ---
62
- def undo_last(history: List[ChatMessage]) -> Tuple[List[ChatMessage], List[ChatMessage]]:
63
- """
64
- Removes the last user message and the last assistant response.
65
- Returns the updated history and the pair that was removed (for potential redo).
66
- """
67
- print("\nAttempting Undo")
68
- undone_pair = []
69
- if len(history) >= 2:
70
- # Проверяем, что последние два сообщения - это user и assistant
71
- if history[-2].role == "user" and history[-1].role == "assistant":
72
- print("Found user/assistant pair to undo.")
73
- undone_pair = history[-2:]
74
- history = history[:-2]
75
- else:
76
- print("Last two messages are not a user/assistant pair. Cannot undo.")
77
- else:
78
- print("Not enough messages in history to undo.")
79
- return history, undone_pair
80
- # -----------------------------
81
-
82
- # --- Новая функция для Redo ---
83
- def redo_last(history: List[ChatMessage], undone_pair: List[ChatMessage]) -> Tuple[List[ChatMessage], List[ChatMessage]]:
84
- """
85
- Restores the last undone user/assistant pair.
86
- Returns the updated history and clears the undone pair state.
87
- """
88
- print("\nAttempting Redo")
89
- if undone_pair:
90
- print("Found undone pair to redo.")
91
- history.extend(undone_pair)
92
- undone_pair = [] # Очищаем состояние после восстановления
93
- else:
94
- print("No undone pair available to redo.")
95
- return history, undone_pair
96
- # -----------------------------
97
-
98
- # --- Новая функция для Regenerate ---
99
- def regenerate_response(history: List[ChatMessage]) -> Tuple[List[ChatMessage], Optional[str], List[ChatMessage]]:
100
- """
101
- Removes the last assistant response and returns the history ending
102
- with the last user message, and the content of that user message.
103
- Also returns an empty list to clear the undone_state.
104
- """
105
- print("\nAttempting Regenerate")
106
- last_user_message_content = None
107
- history_for_regen = history[:] # Создаем копию
108
-
109
- # Ищем последний user message с конца
110
- last_user_index = -1
111
- for i in range(len(history_for_regen) - 1, -1, -1):
112
- if history_for_regen[i].role == "user":
113
- last_user_index = i
114
- break
115
-
116
- if last_user_index != -1:
117
- print(f"Found last user message at index {last_user_index}")
118
- last_user_message_content = history_for_regen[last_user_index].content
119
- # Обрезаем историю до этого user message включительно
120
- history_for_regen = history_for_regen[:last_user_index + 1]
121
- print("Prepared history for regeneration.")
122
- else:
123
- print("No user message found to regenerate from.")
124
- history_for_regen = history # Возвращаем оригинал, если не нашли
125
-
126
- # Регенерация инвалидирует предыдущее undo
127
- return history_for_regen, last_user_message_content, [] # Возвращаем историю и контент пользователя, очищаем undone_state
128
- # ----------------------------------
129
-
130
-
131
- def stream_gemini_response(user_message: str, messages: list) -> Iterator[list]:
132
- print("start model response stream")
133
- """
134
- Streams response with conversation history support for text input only.
135
- (Убрали логику "Thinking" для упрощения и совместимости)
136
- """
137
- # --- Добавили проверку на None или пустую строку ---
138
- if not user_message or not user_message.strip():
139
- print("Empty or None user message received. Skipping API call.")
140
- # Можно добавить сообщение об ошибке или просто ничего не делать
141
- # messages.append(ChatMessage(role="assistant", content="Please provide a non-empty text message."))
142
- yield messages # Просто возвращаем текущее состояние
143
- return
144
- # -----------------------------------------------------
145
-
146
- try:
147
- print(f"\n=== New Request (Text) ===")
148
- print(f"User message: {user_message}") # Может быть None при регенерации, но мы проверили выше
149
-
150
- # Форматируем историю *перед* добавлением нового ответа ассистента
151
- # Важно: история для API должна содержать только завершенные user/model пары
152
- chat_history = format_chat_history(messages) # messages здесь уже содержит user message, с которого регенерируем, или новый
153
-
154
- print("Chat parameter")
155
- chat = model.start_chat(history=chat_history)
156
- print("Start response")
157
- # Отправляем *только* последнее сообщение пользователя (которое уже есть в messages)
158
- # Gemini API берет историю из start_chat, а последнее сообщение из send_message
159
- # Важно: user_message здесь - это то, на что мы генерируем ответ
160
- response = chat.send_message(user_message, stream=True)
161
-
162
- response_buffer = ""
163
- # Добавляем пустое сообщение ассистента, которое будем обновлять
164
- messages.append(ChatMessage(role="assistant", content=""))
165
- yield messages # Показываем пустой контейнер для ответа
166
-
167
- print("Streaming response...")
168
- for chunk in response:
169
- # --- Обработка возможных ошибок в чанке ---
170
- try:
171
- # Проверяем наличие candidates и parts
172
- if chunk.candidates and chunk.candidates[0].content and chunk.candidates[0].content.parts:
173
- current_chunk_text = chunk.candidates[0].content.parts[0].text
174
- # print(f"Chunk: '{current_chunk_text}'") # Отладка чанков
175
- response_buffer += current_chunk_text
176
- messages[-1] = ChatMessage(
177
- role="assistant",
178
- content=response_buffer
179
- )
180
- else:
181
- # Обработка случая, когда структура чанка неожиданная
182
- print(f"Warning: Unexpected chunk structure: {chunk}")
183
- # Можно добавить запасной вариант или пропустить чанк
184
- if hasattr(chunk, 'text'): # Иногда API возвращает просто text
185
- response_buffer += chunk.text
186
- messages[-1] = ChatMessage(role="assistant", content=response_buffer)
187
-
188
- except (AttributeError, IndexError, ValueError) as chunk_err:
189
- # Ловим ошибки доступа к атрибутам/индексам или другие проблемы чанка
190
- print(f"Error processing chunk: {chunk_err}")
191
- print(f"Problematic chunk data: {chunk}")
192
- # Можно прервать или продолжить, добавив сообщение об ошибке
193
- messages[-1] = ChatMessage(
194
- role="assistant",
195
- content=response_buffer + f"\n\n[Ошибка обработки части ответа: {chunk_err}]"
196
- )
197
- yield messages
198
- return # Прерываем стриминг при ошибке в чанке
199
- # -------------------------------------------
200
-
201
- # time.sleep(0.05) # Раскомментируйте для замедления стриминга (отладка)
202
- yield messages # Отправляем обновле��ное сообщение в интерфейс
203
-
204
- print(f"\n=== Final Response ===\n{response_buffer}")
205
- # Проверка на пустой финальный ответ (если модель ничего не вернула)
206
- if not response_buffer.strip() and len(messages) > 0 and messages[-1].role == "assistant":
207
- messages[-1] = ChatMessage(
208
- role="assistant",
209
- content="[Модель не дала ответа]"
210
- )
211
- yield messages
212
-
213
-
214
- # --- Улучшенная обработка ошибок API ---
215
- except Exception as e:
216
- error_message = f"Произошла ошибка при обращении к Gemini API: {str(e)}"
217
- print(f"\n=== Error ===\n{error_message}")
218
- # Пытаемся добавить сообщение об ошибке в чат, если возможно
219
- if messages and isinstance(messages[-1], ChatMessage) and messages[-1].role == "assistant" and messages[-1].content == "":
220
- # Если последний элемент - пустое сообщение ассистента, заменяем его ошибкой
221
- messages[-1] = ChatMessage(role="assistant", content=error_message)
222
- else:
223
- # Иначе добавляем новое сообщение с ошибкой
224
- messages.append(ChatMessage(role="assistant", content=error_message))
225
- yield messages
226
- # -------------------------------------
227
-
228
-
229
- def user_message(msg: str, history: list) -> tuple[str, list, list]:
230
- """Adds user message to chat history and clears the undone state."""
231
- print(f"\nUser message added: '{msg}'")
232
- history.append(ChatMessage(role="user", content=msg))
233
- # Новое сообщение пользователя инвалидирует предыдущее undo
234
- return "", history, [] # Возвращаем пустую строку, историю и пустой undone_state
235
-
236
- # Create the Gradio interface
237
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo:
238
- gr.Markdown("# Chat with " + used_model)
239
-
240
- gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fzelk12%2FGemini-2">
241
- <img src="https://api.visitorbadge.io/api/combined?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fzelk12%2FGemini-2&countColor=%23263759" />
242
- </a>""")
243
-
244
- # --- Добавляем состояние для Undo/Redo ---
245
- undone_state = gr.State([])
246
- # --------------------------------------
247
-
248
- chatbot = gr.Chatbot(
249
- [], # Начинаем с пустого списка
250
- type="messages",
251
- label=used_model + " Chatbot (Streaming Output)",
252
- render_markdown=True,
253
- scale=1,
254
- # editable="all", # editable="all" может мешать undo/redo, лучше отключить или user
255
- editable=False,
256
- avatar_images=(None,"https://lh3.googleusercontent.com/oxz0sUBF0iYoN4VvhqWTmux-cxfD1rxuYkuFEfm1SFaseXEsjjE4Je_C_V3UQPuJ87sImQK3HfQ3RXiaRnQetjaZbjJJUkiPL5jFJ1WRl5FKJZYibUA=w214-h214-n-nu"),
257
- height=600 # Задаем высоту для лучшего вида
258
- )
259
-
260
- with gr.Row(equal_height=False): # equal_height=False может быть лучше для кнопок разной высоты
261
- input_box = gr.Textbox(
262
- lines=1,
263
- label="Chat Message",
264
- placeholder="Type your message here...",
265
- scale=4
266
- )
267
-
268
- # --- Группируем кнопки управления ---
269
- with gr.Column(scale=1, min_width=150): # Даем минимальную ширину колонке с кнопками
270
- submit_button = gr.Button("Submit", scale=1, variant="primary") # Выделяем основную кнопку
271
- with gr.Row():
272
- undo_button = gr.Button("Undo", scale=1)
273
- redo_button = gr.Button("Redo", scale=1)
274
- regenerate_button = gr.Button("Regenerate", scale=1)
275
- clear_button = gr.Button("Clear Chat", scale=1)
276
-
277
-
278
- # --- Переименовали кнопки для ясности ---
279
- # undo_button = test_button
280
- # redo_button = test1_button
281
- # regenerate_button = test2_button
282
- # ----------------------------------------
283
-
284
- # Add example prompts
285
- example_prompts = [
286
- ["Write a short poem about the sunset."],
287
- ["Explain the theory of relativity in simple terms."],
288
- ["If a train leaves Chicago at 6am traveling at 60mph, and another train leaves New York at 8am traveling at 80mph, at what time will they meet?"],
289
- ["Summarize the plot of Hamlet."],
290
- ["Write a haiku about a cat."]
291
- ]
292
-
293
- gr.Examples(
294
- examples=example_prompts,
295
- inputs=input_box,
296
- label="Examples: Try these prompts!", # Убрали про thinking, т.к. убрали его вывод
297
- examples_per_page=5
298
- )
299
-
300
- # --- Обновленные обработчики событий ---
301
- msg_store = gr.State("") # Store for preserving user message during processing
302
-
303
- # --- Обработчик для Enter в input_box ---
304
- input_box.submit(
305
- user_message, # 1. Добавить сообщение пользователя в историю, очистить undone_state
306
- inputs=[input_box, chatbot],
307
- outputs=[input_box, chatbot, undone_state] # input_box очищается первым возвращаемым значением ""
308
- ).then(
309
- lambda x: (x, x), # 2. Скопировать сообщение из history[-1].content в msg_store для stream_gemini_response
310
- inputs=[chatbot], # Берем историю, где последнее сообщение - пользовательское
311
- outputs=[msg_store, msg_store], # Копируем контент последнего сообщения (пользователя)
312
- fn=lambda history: (history[-1].content, history[-1].content) if history and history[-1].role == "user" else (None, None)
313
- ).then(
314
- stream_gemini_response, # 3. Сгенерировать и стримить ответ
315
- inputs=[msg_store, chatbot], # Используем сохраненное сообщение и обновленную историю
316
- outputs=[chatbot]
317
- )
318
-
319
- # --- Обработчик для кнопки Submit ---
320
- submit_button.click(
321
- user_message, # 1. Добавить сообщение пользователя, очистить undone_state
322
- inputs=[input_box, chatbot],
323
- outputs=[input_box, chatbot, undone_state]
324
- ).then(
325
- lambda x: (x, x), # 2. Скопировать сообщение пользователя в msg_store
326
- inputs=[chatbot],
327
- outputs=[msg_store, msg_store],
328
- fn=lambda history: (history[-1].content, history[-1].content) if history and history[-1].role == "user" else (None, None)
329
- ).then(
330
- stream_gemini_response, # 3. Сгенерировать ответ
331
- inputs=[msg_store, chatbot],
332
- outputs=[chatbot]
333
- )
334
-
335
- # --- Обработчик для кнопки Clear ---
336
- clear_button.click(
337
- lambda: ([], "", [], ""), # Очищаем chatbot, input_box, msg_store, undone_state
338
- outputs=[chatbot, input_box, msg_store, undone_state],
339
- queue=False # Очистка быстрая, очередь не нужна
340
- )
341
-
342
- # --- Обработчик для кнопки Undo ---
343
- undo_button.click(
344
- undo_last,
345
- inputs=[chatbot],
346
- outputs=[chatbot, undone_state], # Обновляем историю и сохраняем удаленную пару
347
- queue=False # Действие быстрое
348
- )
349
-
350
- # --- Обработчик для кнопки Redo ---
351
- redo_button.click(
352
- redo_last,
353
- inputs=[chatbot, undone_state], # Нужна история и сохраненная пара
354
- outputs=[chatbot, undone_state], # Обновляем историю и очищаем сохраненную пару
355
- queue=False # Действие быстрое
356
- )
357
-
358
- # --- Обработчик для кнопки Regenerate ---
359
- regenerate_button.click(
360
- regenerate_response, # 1. Подготовить историю и получить сообщение пользователя, очистить undone_state
361
- inputs=[chatbot],
362
- outputs=[chatbot, msg_store, undone_state] # Обновляем историю, помещаем user msg в msg_store, очищаем undone
363
- ).then(
364
- stream_gemini_response, # 2. Сгенерировать новый ответ на user msg из msg_store
365
- inputs=[msg_store, chatbot], # Используем user msg из msg_store и подготовленную историю
366
- outputs=[chatbot]
367
- )
368
- # -------------------------------------
369
-
370
- gr.Markdown(
371
- """
372
- <br><br><br>
373
- ---
374
- ### About this Chatbot
375
- **Try out the example prompts below!**
376
- **Key Features:**
377
- * Powered by Google's **""" + used_model + """** model.
378
- * Supports **conversation history**, **undo**, **redo**, and **regenerate**.
379
- * Uses **streaming** for responses.
380
- **Instructions:**
381
- 1. Type your message or select an example.
382
- 2. Press Enter or click Submit.
383
- 3. Use Undo/Redo to correct mistakes or explore alternatives.
384
- 4. Use Regenerate to get a different response to your last message.
385
- 5. Use Clear Chat to start over.
386
- """
387
- )
388
-
389
-
390
- # Launch the interface
391
- if __name__ == "__main__":
392
- # --- Добавим возможность запускать на публичном URL через share=True ---
393
- # demo.launch(debug=True) # Для локальной отладки
394
- demo.launch(share=True) # Для получения пуб��ичной ссылки (если нужно)
395
- # demo.launch() # Стандартный локальный запуск
396
- # -----------------------------------------------------------------import os
397
- import gradio as gr
398
- from gradio import ChatMessage
399
- from typing import Iterator, List, Tuple, Optional # Добавили типы
400
- import google.generativeai as genai
401
- import time # Import time module for potential debugging/delay
402
-
403
- print("import library complete")
404
- print("add API key")
405
-
406
- # get Gemini API Key from the environ variable
407
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
408
- # --- Безопасность: Добавим проверку наличия ключа ---
409
- if not GEMINI_API_KEY:
410
- raise ValueError("GEMINI_API_KEY environment variable not set!")
411
- # ----------------------------------------------------
412
- genai.configure(api_key=GEMINI_API_KEY)
413
-
414
-
415
- print("add API key complete ")
416
- print("add model")
417
-
418
- # --- Используем более подходящую модель для чата, если Gemma IT недоступна или неоптимальна ---
419
- # used_model = "gemma-3-27b-it" # Gemma может не поддерживать role='model' явно, Gemini Pro лучше для чата
420
- used_model = "gemini-1.5-flash-latest" # Рекомендуется для чата с историей
421
  # ------------------------------------------------------------------------------------------
422
  model = genai.GenerativeModel(used_model)
423
 
 
21
  print("add model")
22
 
23
  # --- Используем более подходящую модель для чата, если Gemma IT недоступна или неоптимальна ---
24
+ used_model = "gemma-3-27b-it" # Gemma может не поддерживать role='model' явно, Gemini Pro лучше для чата
25
+ #used_model = "gemini-1.5-flash-latest" # Рекомендуется для чата с историей
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  # ------------------------------------------------------------------------------------------
27
  model = genai.GenerativeModel(used_model)
28