chatx / app.py
vlasdadsda's picture
Update app.py
ecf0feb verified
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"![Generated Image]({image_url})"
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"![Generated Image]({image_url})\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, # Добавим этот параметр
)