Spaces:
Sleeping
Sleeping
import g4f | |
import gradio as gr | |
import tempfile | |
import os | |
from pathlib import Path | |
from g4f.client import Client | |
from PIL import Image | |
import io | |
import requests | |
import numpy as np | |
from ultralytics import YOLO | |
import subprocess | |
import sys | |
import time | |
from typing import Dict, Any, Optional | |
import easyocr | |
from web_engine import search_info, get_page_text # Импортируем функции из web_engine.py | |
AVAILABLE_MODELS = { | |
# "GPT-4": "gpt-4", | |
"O3 (NEW)": "openai", | |
} | |
SYSTEM_PROMPT = """ | |
1. Всегда используйте кодировку UTF-8 | |
2. Используйте обработку ошибок | |
3. Если пользователь отправляет изображение или пишет что-то на подобие 'посмотри на изображение': | |
- Автоматически проанализируйте изображение | |
- Опишите что вы видите на русском языке | |
- Тебе дается текстовая информация, которая может быть полезна для анализа | |
4. Если пользователь просит найти информацию, например, "найди что-то про Адольфа Гитлера": | |
- Ответь с текстом [SEARCH: {prompt}] | |
- Затем используй полученную информацию для ответа на вопрос | |
4.1 Если ты не знаешь информации которая пользователь запрашивает то напиши: | |
[SEARCH: {PROMPT}] | |
5. Если пользователь просит нарисовать или сгенерировать изображение: | |
- Начните ответ с [GENERATE_IMAGE] | |
- Напишите детальный промпт на английском языке | |
- Завершите промпт строкой [/GENERATE_IMAGE] | |
- Продолжите обычный ответ на русском языке | |
6. В режиме размышления: | |
- Используйте [THINKING_STEP] для обозначения шага размышления | |
- Используйте [FINAL_ANSWER] для итогового ответа | |
- Старайтесь подробно описывать ход мыслей | |
""" | |
def test_code(code: str, file_type: str = ".py") -> Dict[str, Any]: | |
"""Тестирование кода в безопасной среде""" | |
start_time = time.time() | |
result = { | |
"success": False, | |
"output": "", | |
"error": "", | |
"execution_time": 0 | |
} | |
try: | |
code = "# -*- coding: utf-8 -*-\n" + code | |
with tempfile.NamedTemporaryFile(suffix=file_type, mode='w', encoding='utf-8', delete=False) as tmp: | |
tmp.write(code) | |
tmp_path = tmp.name | |
process = subprocess.Popen( | |
[sys.executable, tmp_path], | |
stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE, | |
text=True, | |
encoding='utf-8' | |
) | |
try: | |
stdout, stderr = process.communicate(timeout=10) | |
result["success"] = process.returncode == 0 | |
result["output"] = stdout | |
result["error"] = stderr | |
except subprocess.TimeoutExpired: | |
process.kill() | |
result["error"] = "Превышено время выполнения (10 сек)" | |
finally: | |
os.unlink(tmp_path) | |
except Exception as e: | |
result["error"] = str(e) | |
result["execution_time"] = time.time() - start_time | |
return result | |
def process_file(file): | |
"""Обработка загруженного файла""" | |
if not file: | |
return None | |
try: | |
# print() | |
# Получаем расширение файла | |
file_extension = file.name.lower().split('.')[-1] if hasattr(file, 'name') else '' | |
print(file_extension) | |
# Список поддерживаемых форматов изображений | |
image_extensions = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'} | |
# Если это изображение | |
if file_extension in image_extensions: | |
try: | |
# Открываем изображение | |
if isinstance(file, str): | |
image = Image.open(file) | |
else: | |
image = Image.open(io.BytesIO(file.read())) | |
# Анализируем изображение | |
return process_image(image) | |
except Exception as e: | |
return f"[Ошибка обработки изображения: {str(e)}]" | |
# Если это текстовый файл | |
if isinstance(file, str): | |
with open(file, 'r', encoding='utf-8') as f: | |
return f.read() | |
else: | |
return file.read().decode('utf-8') | |
except UnicodeDecodeError: | |
return "[Бинарный файл]" | |
except Exception as e: | |
return f"[Ошибка чтения файла: {str(e)}]" | |
def process_audio(file_path: str, operation: str) -> Optional[str]: | |
"""Обработка аудио файлов""" | |
try: | |
from pydub import AudioSegment | |
audio = AudioSegment.from_file(file_path) | |
if "громкость" in operation.lower(): | |
audio = audio + 10 | |
output_path = os.path.join(tempfile.gettempdir(), f"processed_{int(time.time())}.mp3") | |
audio.export(output_path, format="mp3") | |
return output_path | |
except Exception as e: | |
return f"Ошибка обработки аудио: {str(e)}" | |
def generate_image(prompt: str) -> str: | |
"""Генерация изображения через g4f""" | |
try: | |
client = Client() | |
response = client.images.generate( | |
model="flux-pro", | |
prompt=prompt, | |
response_format="url", | |
provider='PollinationsAI' | |
) | |
image_url = response.data[0].url | |
# Проверяем доступность изображения | |
timeout = 30 # Максимальное время ожидания в секундах | |
interval = 0.1 # Интервал между проверками в секундах | |
elapsed_time = 0 | |
while elapsed_time < timeout: | |
# Проверяем доступность изображения | |
head_response = requests.head(image_url) | |
if head_response.status_code == 200: | |
# Дополнительная проверка на наличие изображения | |
get_response = requests.get(image_url) | |
if get_response.status_code == 200: | |
return image_url # Изображение доступно | |
else: | |
return "❌ Ошибка: изображение недоступно." | |
time.sleep(interval) | |
elapsed_time += interval | |
return "❌ Ошибка: время ожидания изображения истекло." # Время ожидания истекло | |
except Exception as e: | |
return f"Ошибка при генерации изобраения: {str(e)}" | |
def process_image(image_input) -> str: | |
"""Обработка изображения через провайдера Blackbox.""" | |
try: | |
# Инициализация клиента GPT с провайдером Blackbox | |
client = Client() | |
# Определяем, как было передано изображение (URL или объект PIL.Image) | |
if isinstance(image_input, str): # Если передан URL изображения | |
response = requests.get(image_input, stream=True) | |
if response.status_code == 200: | |
image = Image.open(response.raw) # Преобразуем поток в объект PIL.Image | |
else: | |
return "❌ Ошибка загрузки изображения по URL." | |
elif isinstance(image_input, Image.Image): # Если передано изображение PIL | |
image = image_input | |
else: | |
return "❌ Неподдерживаемый формат изображения." | |
# Конвертируем изображение в режим RGB, если оно в RGBA или другом формате | |
if image.mode != 'RGB': | |
image = image.convert('RGB') | |
# Сохраняем изображение во временный файл | |
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp: | |
image.save(tmp.name, format="JPEG") # Сохраняем изображение как JPEG | |
image_file = open(tmp.name, "rb") # Открываем файл для передачи в запрос | |
# Запрос на анализ изображения через Blackbox | |
response = client.chat.completions.create( | |
model="gpt-4o", | |
messages=[{"role": "user", "content": "Опишите, что изображено на этом изображении."}], | |
image=image_file, | |
provider="Blackbox" | |
) | |
print("MADEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE") | |
print(response) | |
# Закрываем временный файл | |
image_file.close() | |
# Возвращаем результат анализа | |
return response | |
except Exception as e: | |
return f"❌ Ошибка при обработке изображения: {str(e)}" | |
finally: | |
# Удаляем временный файл после использования | |
if "tmp" in locals(): | |
Path(tmp.name).unlink(missing_ok=True) | |
def chat_response(message, history, model_name, direct_mode, thinking_depth=1.0, uploaded_file=None): | |
messages = [{"role": "system", "content": SYSTEM_PROMPT}] | |
partial_message = "" # Инициализация переменной | |
history = history or [] | |
history.append({"role": "user", "content": message}) | |
history.append({"role": "assistant", "content": ""}) | |
try: | |
# Обычный поток обработки сообщений | |
for msg in history[:-2]: | |
messages.append({"role": msg["role"], "content": msg["content"]}) | |
messages.append({"role": "user", "content": str(message)}) | |
partial_message = "" | |
response = None # Инициализация переменной response | |
response = g4f.ChatCompletion.create( | |
model=AVAILABLE_MODELS.get(model_name, "O3"), | |
messages=messages, | |
stream=False, | |
provider="PollinationsAI" | |
) | |
if response and isinstance(response, str): | |
partial_message += response | |
history[-1]["content"] = f""" | |
<div class="text-fade-in"> | |
{partial_message} | |
</div> | |
""" | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
# Если в ответе от модели есть [SEARCH:], выполняем поиск | |
if "[SEARCH:" in partial_message: | |
history[-1]["content"] = """ | |
<div class="search-animation"> | |
<div class="search-text">🔍 Выполняется поиск...</div> | |
</div> | |
""" | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
prompt = partial_message.split("[SEARCH:")[1].split("]")[0].strip() | |
# Выполняем поиск | |
results = search_info(prompt) | |
if not results: | |
history[-1]["content"] = "Не удалось найти информацию." | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
return | |
info_text = "" | |
for link in results: | |
info_text += f"Ссылка: {link}\n" | |
page_text = get_page_text(link) | |
info_text += f"Текст: {page_text[:500]}...\n\n" | |
search_response_prompt = f"""Вот информация по запросу "{prompt}": | |
{info_text} | |
На основе этой информации сформируй краткий и понятный ответ на русском языке. | |
""" | |
search_response = g4f.ChatCompletion.create( | |
model="gpt-4", | |
messages=[{"role": "user", "content": search_response_prompt}], | |
stream=False, | |
provider="Mhystical" | |
) | |
final_response = search_response if isinstance(search_response, str) else "Не удалось получить ответ." | |
final_response = final_response.replace(f"[SEARCH: {prompt}]", "").strip() | |
history.append({"role": "assistant", "content": final_response}) | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
if response and isinstance(response, str): | |
steps = response.split("[THINKING_STEP]") | |
final_answer = "" | |
for i, step in enumerate(steps): | |
if "[FINAL_ANSWER]" in step: | |
final_answer = step.split("[FINAL_ANSWER]")[1].strip() | |
step = step.split("[FINAL_ANSWER]")[0] | |
step_html = f""" | |
<div class="thinking-step-block"> | |
<span class="thinking-step">Шаг {i + 1}: {step.strip()}</span> | |
</div> | |
""" | |
history[-1]["content"] += step_html | |
yield history, gr.update(visible=False) | |
# Добавляем финальный блок с заключением | |
final_html = f""" | |
<div class="final-answer-block"> | |
<span class="thinking-conclusion">Ответ: {final_answer}</span> | |
</div> | |
""" | |
history[-1]["content"] += final_html | |
yield history, gr.update(visible=True) | |
return | |
history[-1]["content"] = partial_message.strip() | |
yield history, *[gr.update(visible=True) for _ in range(6)] | |
except Exception as e: | |
history[-1]["content"] = f"❌ Произошла ошибка: {str(e)}" | |
yield ( | |
history, | |
gr.update(visible=False), | |
gr.update(visible=True, value=str(e)), | |
gr.update(visible=True), | |
"", | |
gr.update(visible=False), | |
gr.update(value=None) | |
) | |
if direct_mode: | |
image_url = generate_image(message) | |
if not image_url.startswith("Ошибка"): | |
history[-1]["content"] = f"" | |
else: | |
history[-1]["content"] = f"❌ {image_url}" | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
return | |
yield ( | |
history, | |
gr.update(visible=False), | |
gr.update(visible=False), | |
gr.update(visible=False), | |
"", | |
gr.update(visible=False), | |
gr.update(visible=False) | |
) | |
if uploaded_file: | |
file_content = process_file(uploaded_file) | |
if file_content: | |
message = f"Файл содержит:\n```\n{file_content}\n```\n\n{message}" | |
for msg in history[:-2]: | |
messages.append({"role": msg["role"], "content": msg["content"]}) | |
messages.append({"role": "user", "content": str(message)}) | |
partial_message = "" | |
code_block = None | |
# response = g4f.ChatCompletion.create( | |
# model=AVAILABLE_MODELS.get(model_name, "O3"), | |
# messages=messages, | |
# stream=False, | |
# provider="PollinationsAI" | |
# ) | |
if response and isinstance(response, str): | |
partial_message += response | |
if "[GENERATE_IMAGE]" in partial_message: | |
history[-1]["content"] = """ | |
<div class="generating-animation"> | |
<div class="generating-text">Генерация изображения...</div> | |
</div> | |
""" | |
if "[GENERATE_IMAGE]" in partial_message and "[/GENERATE_IMAGE]" in partial_message: | |
start_idx = partial_message.find("[GENERATE_IMAGE]") + len("[GENERATE_IMAGE]") | |
end_idx = partial_message.find("[/GENERATE_IMAGE]") | |
image_prompt = partial_message[start_idx:end_idx].strip() | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
image_url = generate_image(image_prompt) | |
if not image_url.startswith("Ошибка"): | |
explanation_text = partial_message[end_idx + len("[/GENERATE_IMAGE]"):].strip() | |
partial_message = f"\n\n{explanation_text}" | |
else: | |
partial_message = f"❌ {image_url}" | |
history[-1]["content"] = partial_message + "|" | |
yield history, *[gr.update(visible=False) for _ in range(6)] | |
if "```" in partial_message: | |
code_start = partial_message.rfind("```") + 3 | |
code_end = partial_message.find("```", code_start) | |
if code_end != -1: | |
code_block = partial_message[code_start:code_end].strip() | |
history[-1]["content"] = partial_message | |
yield ( | |
history, | |
*[gr.update(visible=True if code_block else False) for _ in range(5)], | |
gr.update(value=None) | |
) | |
def analyze_code(code): | |
"""Анализ кода и получение объяснения""" | |
if not code: | |
return [{"role": "assistant", "content": "❌ Нет кода для анализа"}] | |
prompt = f"""Проанализируй этот код и объясни: | |
1. Что он делает | |
2. Возможные проблемы или ошибки | |
3. Как можно улучшить | |
4. Примеры использования | |
Код: | |
``` | |
{code} | |
```""" | |
try: | |
response = g4f.ChatCompletion.create( | |
model="gpt-4o", | |
messages=[{"role": "user", "content": prompt}], | |
stream=False | |
) | |
return [{"role": "assistant", "content": response if isinstance(response, str) else "Не удалось получить анализ"}] | |
except Exception as e: | |
return [{"role": "assistant", "content": f"❌ Ошибка при анализе: {str(e)}"}] | |
def create_interface(): | |
# Обновляем тему на темую | |
theme = gr.themes.Soft( | |
primary_hue="slate", | |
secondary_hue="gray", | |
).set( | |
body_background_fill="#1a1a1a", | |
body_text_color="#ffffff", | |
button_primary_background_fill="#2d2d2d", | |
button_primary_background_fill_hover="#3d3d3d", | |
button_primary_text_color="white", | |
block_label_background_fill="#2d2d2d", | |
block_title_text_color="#ffffff", | |
input_background_fill="#2d2d2d", | |
) | |
# Обновляем CSS стили для управления цветом кода | |
css = """ | |
.gradio-container { | |
background-color: #1a1a1a !important; | |
} | |
.chatbot { | |
background-color: #2d2d2d !important; | |
border-radius: 10px; | |
box-shadow: 0 2px 6px rgba(0,0,0,0.15); | |
} | |
.text-fade-in { | |
opacity: 0; /* Исходная прозрачность */ | |
transform: translateY(10px); /* Исходное смещение */ | |
animation: fadeInText 1s ease-in-out forwards; /* Анимация при показе */ | |
} | |
@keyframes fadeInText { | |
0% { | |
opacity: 0; | |
transform: translateY(10px); /* Немного ниже */ | |
} | |
100% { | |
opacity: 1; | |
transform: translateY(0); /* Возвращается на место */ | |
} | |
} | |
.message.user { | |
background-color: #3d3d3d !important; | |
color: white !important; | |
border-radius: 15px; | |
} | |
.search-animation { | |
position: relative; | |
width: 100%; | |
min-height: 80px; | |
background: linear-gradient(45deg, #2d2d2d, #3d3d3d); | |
border-radius: 15px; | |
overflow: hidden; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.search-text { | |
color: white; | |
font-size: 18px; | |
z-index: 2; | |
animation: search-pulse 1.5s infinite; | |
} | |
.search-animation::before { | |
content: ''; | |
position: absolute; | |
width: 200%; | |
height: 200%; | |
top: -50%; | |
left: -50%; | |
background: | |
radial-gradient(2px 2px at 20% 30%, rgba(255,255,255,0.3), rgba(0,0,0,0)), | |
radial-gradient(2px 2px at 40% 70%, rgba(255,255,255,0.3), rgba(0,0,0,0)); | |
background-repeat: repeat; | |
animation: search-rotate 6s linear infinite; | |
} | |
@keyframes search-pulse { | |
0% { opacity: 0.6; } | |
50% { opacity: 1; } | |
100% { opacity: 0.6; } | |
} | |
@keyframes search-rotate { | |
from { | |
transform: rotate(0deg); | |
} | |
to { | |
transform: rotate(360deg); | |
} | |
} | |
.message.bot { | |
background-color: #2d2d2d !important; | |
color: white !important; | |
border-radius: 15px; | |
position: relative; | |
transition: all 0.3s ease; | |
} | |
/* Добавляем стиль ля блоков кода */ | |
.message.bot pre { | |
background-color: #1a1a1a !important; | |
border-radius: 8px; | |
padding: 10px; | |
} | |
.message.bot.typing::after { | |
content: '|'; | |
animation: cursor 1s infinite; | |
} | |
@keyframes cursor { | |
0% { opacity: 0; } | |
50% { opacity: 1; } | |
100% { opacity: 0; } | |
} | |
.message.bot:last-child { | |
animation: fadeIn 0.3s ease-in-out; | |
} | |
/* Убираем дублирующиеся стили курсора */ | |
.message.bot:last-child:not(:empty)::after { | |
content: ''; | |
} | |
/* Анимация появления текста */ | |
@keyframes fadeIn { | |
from { | |
opacity: 0; | |
transform: translateY(10px); | |
} | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
@keyframes cursor { | |
0% { opacity: 0; } | |
50% { opacity: 1; } | |
100% { opacity: 0; } | |
} | |
.footer { | |
display: none !important; | |
} | |
.generating-animation { | |
position: relative; | |
width: 100%; | |
height: 120px; | |
background: linear-gradient(45deg, #1a1a1a, #2d2d2d); | |
border-radius: 15px; | |
overflow: hidden; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.generating-text { | |
color: white; | |
font-size: 16px; | |
z-index: 2; | |
animation: pulse 1.5s infinite; | |
} | |
.generating-animation::before { | |
content: ''; | |
position: absolute; | |
width: 200%; | |
height: 200%; | |
top: -50%; | |
left: -50%; | |
background: | |
radial-gradient(2px 2px at 20% 30%, rgba(255,255,255,0.8), rgba(0,0,0,0)), | |
radial-gradient(2px 2px at 40% 70%, rgba(255,255,255,0.8), rgba(0,0,0,0)), | |
radial-gradient(2px 2px at 50% 40%, rgba(255,255,255,0.8), rgba(0,0,0,0)), | |
radial-gradient(2px 2px at 60% 60%, rgba(255,255,255,0.8), rgba(0,0,0,0)); | |
background-repeat: repeat; | |
animation: rotate 8s linear infinite; | |
} | |
@keyframes rotate { | |
from { | |
transform: rotate(0deg); | |
} | |
to { | |
transform: rotate(360deg); | |
} | |
} | |
@keyframes pulse { | |
0% { opacity: 0.6; } | |
50% { opacity: 1; } | |
100% { opacity: 0.6; } | |
} | |
.submit-btn { | |
min-width: 100px !important; | |
cursor: pointer !important; | |
background-color: #4a4a4a !important; | |
transition: all 0.2s ease; | |
} | |
.submit-btn:hover { | |
background-color: #5a5a5a !important; | |
} | |
.input-row { | |
gap: 8px; | |
padding: 8px; | |
background: #2d2d2d; | |
border-radius: 8px; | |
margin-top: 8px; | |
} | |
.footer { | |
display: none !important; | |
} | |
/* Добавляем анимацию для просмотра изображения */ | |
.image-analysis-animation { | |
position: relative; | |
width: 100%; | |
min-height: 100px; | |
background: linear-gradient(45deg, #2d2d2d, #3d3d3d); | |
border-radius: 15px; | |
overflow: hidden; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
margin: 10px 0; | |
} | |
.analysis-text { | |
color: white; | |
font-size: 16px; | |
z-index: 2; | |
text-align: center; | |
animation: pulse 1.5s infinite; | |
} | |
.image-analysis-animation::before { | |
content: ''; | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, | |
transparent 0%, | |
rgba(255,255,255,0.1) 50%, | |
transparent 100%); | |
animation: scanning 2s linear infinite; | |
} | |
@keyframes scanning { | |
0% { transform: translateX(-100%); } | |
100% { transform: translateX(100%); } | |
} | |
.thinking-animation { | |
position: relative; | |
width: 100%; | |
min-height: 100px; | |
background: linear-gradient(45deg, #1a1a1a, #2d2d2d); | |
border-radius: 15px; | |
overflow: hidden; | |
margin: 10px 0; | |
padding: 20px; | |
border-left: 4px solid #4a90e2; | |
transition: all 0.3s ease; | |
} | |
.thinking-animation:hover { | |
background: linear-gradient(45deg, #202020, #333333); | |
} | |
.thinking-step { | |
color: #b0b0b0; | |
font-style: italic; | |
margin-bottom: 10px; | |
padding-left: 20px; | |
border-left: 2px solid #4a90e2; | |
opacity: 0; | |
transform: translateY(10px); | |
animation: fadeInStep 0.5s ease-in-out forwards; | |
} | |
.thinking-conclusion { | |
color: white; | |
font-weight: bold; | |
margin-top: 15px; | |
padding: 10px; | |
background: rgba(74, 144, 226, 0.1); | |
border-radius: 8px; | |
border-left: 4px solid #4a90e2; | |
} | |
@keyframes fadeInStep { | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
.typing-cursor { | |
display: inline-block; | |
width: 2px; | |
height: 15px; | |
background: #4a90e2; | |
margin-left: 5px; | |
animation: blink 1s infinite; | |
} | |
@keyframes blink { | |
50% { opacity: 0; } | |
} | |
/* Стили для слайдера глубины размышления */ | |
.thinking-depth-slider { | |
margin-top: 10px; | |
padding: 8px; | |
background: rgba(74, 144, 226, 0.1); | |
border-radius: 8px; | |
border-left: 4px solid #4a90e2; | |
} | |
/* Индикатор глубины размышления в сообщении */ | |
.thinking-depth-indicator { | |
font-size: 0.9em; | |
color: #4a90e2; | |
margin-bottom: 10px; | |
font-style: italic; | |
} | |
.error-animation { | |
position: relative; | |
width: 100%; | |
min-height: 80px; | |
background: linear-gradient(45deg, #2d1a1a, #2d2d2d); | |
border-radius: 15px; | |
overflow: hidden; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
border-left: 4px solid #e24a4a; | |
padding: 15px; | |
margin: 10px 0; | |
} | |
.error-text { | |
color: #ff9999; | |
font-size: 16px; | |
z-index: 2; | |
text-align: center; | |
} | |
.thinking-step-block { | |
background: linear-gradient(45deg, #1a1a1a, #2d2d2d); | |
border-left: 4px solid #4a90e2; | |
margin: 15px 0; | |
padding: 15px; | |
border-radius: 8px; | |
animation: fadeIn 0.5s ease-in-out; | |
} | |
.final-answer-block { | |
background: linear-gradient(45deg, #1a2a3a, #2d3d4d); | |
border-left: 4px solid #50c878; | |
margin: 20px 0; | |
padding: 20px; | |
border-radius: 8px; | |
font-weight: bold; | |
animation: slideIn 0.5s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { | |
opacity: 0; | |
transform: translateY(10px); | |
} | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
@keyframes slideIn { | |
from { | |
opacity: 0; | |
transform: translateX(-10px); | |
} | |
to { | |
opacity: 1; | |
transform: translateX(0); | |
} | |
} | |
.thinking-animation { | |
position: relative; | |
width: 100%; | |
min-height: 100px; | |
background: linear-gradient(45deg, #1a1a1a, #2d2d2d); | |
border-radius: 15px; | |
overflow: hidden; | |
margin: 10px 0; | |
padding: 20px; | |
border-left: 4px solid #4a90e2; | |
transition: all 0.3s ease; | |
} | |
.thinking-step { | |
color: #b0b0b0; | |
font-style: italic; | |
margin-bottom: 10px; | |
padding-left: 20px; | |
border-left: 2px solid #4a90e2; | |
opacity: 0; | |
transform: translateY(10px); | |
animation: fadeInStep 0.5s ease-in-out forwards; | |
} | |
.thinking-conclusion { | |
color: white; | |
font-weight: bold; | |
margin-top: 15px; | |
padding: 10px; | |
background: rgba(74, 144, 226, 0.1); | |
border-radius: 8px; | |
border-left: 4px solid #4a90e2; | |
} | |
@keyframes fadeInStep { | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
""" | |
# Добавте этот скрипт в create_interface() | |
with gr.Blocks(theme=theme, css=css) as demo: | |
# gr.Markdown("# 💬 AI Chat Assistant") | |
current_code = gr.State("") | |
with gr.Row(): | |
with gr.Column(scale=4): | |
chatbot = gr.Chatbot( | |
height="600px", | |
show_label=False, | |
container=True, | |
show_copy_button=True, | |
elem_classes="chatbot", | |
type="messages", | |
render_markdown=True | |
) | |
with gr.Row(elem_classes="input-row"): | |
msg = gr.Textbox( | |
placeholder="Спросите что-нибудь...", | |
container=False, | |
scale=4, | |
show_label=False, | |
lines=1, # Явно указываем одну строку | |
elem_classes="input-textbox" | |
) | |
submit = gr.Button( | |
"Отправить", | |
variant="primary", | |
scale=1, | |
elem_classes="submit-btn" | |
) | |
# clear = gr.Button("Очистить", scale=1) | |
with gr.Column(scale=1, visible=True) as sidebar: | |
model = gr.Dropdown( | |
choices=list(AVAILABLE_MODELS.keys()), | |
value="O3 (NEW)", | |
label="Модель" | |
) | |
# Добавляем слайдер глубины размышления (изначально скрытый) | |
thinking_depth = gr.Slider( | |
minimum=2.0, # Минимум 3 шага | |
maximum=25.0, | |
value=3.0, | |
step=1.0, # Шаг целочисленный | |
label="Глубина размышления (количество шагов анализа)", | |
visible=False | |
) | |
direct_mode = gr.Checkbox( | |
label="Прямая нерация (без обрботки прмпта)", | |
value=False | |
) | |
file_output = gr.File(label="Загрузить файл") | |
# Добавляем примеры ипользования в виде кнопок | |
# Добавляем кнопки анализа и запуска кода | |
analyze_btn = gr.Button( | |
"Анализировать", | |
visible=False, | |
elem_classes="analyze-btn" | |
) | |
run_btn = gr.Button( | |
"▶️ Запустить код", | |
visible=False, | |
elem_classes="run-btn" | |
) | |
# Обработчики событий | |
msg.submit( | |
fn=chat_response, | |
inputs=[msg, chatbot, model, direct_mode, thinking_depth, file_output], | |
outputs=[chatbot, current_code], | |
api_name=None | |
) | |
submit.click( | |
fn=chat_response, | |
inputs=[msg, chatbot, model, direct_mode, thinking_depth, file_output], | |
outputs=[chatbot, current_code], | |
api_name=None | |
) | |
analyze_btn.click( | |
fn=analyze_code, | |
inputs=[current_code], | |
outputs=[chatbot] | |
) | |
# clear.click( | |
# fn=lambda: (None, ""), | |
# outputs=[chatbot, current_code] | |
# ) | |
# Добавляем обработчик изменения модели | |
def update_thinking_depth_visibility(model_name): | |
return gr.update(visible=model_name == "GPT-4o THINK") | |
model.change( | |
fn=update_thinking_depth_visibility, | |
inputs=[model], | |
outputs=[thinking_depth] | |
) | |
return demo | |
if __name__ == "__main__": | |
demo = create_interface() | |
# Запускем мониторинг изменений в тдельном потоке | |
demo.launch( | |
show_api=False, | |
show_error=True, | |
favicon_path=None, | |
auth=None, | |
# quiet=True, # Добавим этот параметр | |
) | |