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