chatx / app.py
vlasdadsda's picture
Update app.py
4fb8f72 verified
raw
history blame
52.1 kB
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",
"GPT-4 Turbo": "gpt-4-turbo",
"GPT-4o": "gpt-4o",
"GPT-4o THINK": "gpt-4o-think"
}
SYSTEM_PROMPT = """
1. Всегда используйте кодировку UTF-8
2. Используйте обработку ошибок
3. Если пользователь отправляет изображение или пишет что-то на подобие 'посмотри на изображение':
- Автоматически проанализируйте изображение
- Опишите что вы видите на русском языке
- Тебе дается текстовая информация, которая может быть полезна для анализа
4. Если пользователь просит найти информацию, например, "найди что-то про Адольфа Гитлера":
- Ответь с текстом [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:
# Получаем расширение файла
file_extension = file.name.lower().split('.')[-1] if hasattr(file, 'name') else ''
# Список поддерживаемых форматов изображений
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-1.1-pro",
prompt=prompt,
response_format="url",
provider=g4f.Provider.Airforce
)
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:
"""Обработка изображения и распознавание объектов через YOLOv8 и текста через EasyOCR"""
try:
# Загружаем модель YOLOv8n
model = YOLO('yolov8n.pt')
# Инициализируем EasyOCR для русского и английского языков
reader = easyocr.Reader(['ru', 'en'])
# Обработка входного изображения
if isinstance(image_input, str):
response = requests.get(image_input)
image = Image.open(io.BytesIO(response.content))
elif isinstance(image_input, Image.Image):
image = image_input
else:
return "[IMAGE]\n❌ Неподдерживаемый формат изображения\n[/IMAGE]"
# Конвертируем изображение в RGB если оно в RGBA
if image.mode == 'RGBA':
image = image.convert('RGB')
# Сохраняем изображение во временный файл для обработки
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
image.save(tmp.name, format='JPEG')
# Запускаем распознавание объектов
results = model(tmp.name)
# Распознаем текст
text_results = reader.readtext(tmp.name)
# Собираем найденные объекты
detected_objects = []
for r in results:
for box in r.boxes:
class_id = int(box.cls)
class_name = model.names[class_id]
detected_objects.append(class_name)
# Собираем найденный текст
detected_text = []
for detection in text_results:
text = detection[1]
if text.strip(): # Проверяем, что текст не пустой
detected_text.append(text)
if not detected_objects and not detected_text:
return """
<div class="image-analysis-animation">
<div class="analysis-text">К сожалению, я не смог распознать объекты на этом изобраении</div>
</div>
"""
# Формируем сообщение для GPT
prompt = "Я на этом изображении вижу "
if detected_objects:
objects_str = ", ".join(detected_objects)
prompt += f"следующие объекты: {objects_str}. "
if detected_text:
text_str = ", ".join(detected_text)
prompt += f"Также на изображении есть текст: {text_str}"
messages = [
{
"role": "system",
"content": """Ты - система компьютерного зрения.
Тебе нужно:
1. Перевести названия объектов на русский язык
2. Описать что ты видишь простыми словами
3. Всегда отвечать на русском языке
4. Если есть текст, упомянуть его в описании
5. Никогда не говори "Похоже,что у вас есть описание изображения..."
"""
},
{
"role": "user",
"content": prompt
}
]
response = g4f.ChatCompletion.create(
model="gpt-4o",
messages=messages
)
return response if isinstance(response, str) else prompt
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}]
history = history or []
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": ""})
try:
# Проверяем, содержит ли сообщение [SEARCH:]
if "[SEARCH:" in message:
print("Поиск информации...") # Отладочное сообщение
prompt = message.split("[SEARCH:")[1].strip(" ]") # Извлекаем текст запроса
response_text = f"[SEARCH: {prompt}]\n" # Форматируем ответ
history[-1]["content"] = response_text # Обновляем историю с запросом поиска
# Выполняем поиск информации
results = search_info(prompt) # Ищем информацию
if not results:
history.append({"role": "assistant", "content": "Не удалось найти информацию."})
yield history, *[gr.update(visible=False) for _ in range(6)]
return
info_text = ""
for link in results:
info_text += f"Ссылка: {link}\n"
text = get_page_text(link)
info_text += f"Текст: {text}\n\n" # Добавляем текст страницы
print(f"Вот информация по запросу '{prompt}':\n\n{info_text}\nСделай краткий ответ на основе этой информации.")
# Формируем запрос для GPT
web_search = f"Вот информация по запросу '{prompt}':\n\n{info_text}\nСделай краткий ответ на основе этой информации."
print("Отправка запроса к GPT...") # Отладочное сообщение
response = g4f.ChatCompletion.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Сделай краткий ответ на основе следующей информации:"},
{"role": "user", "content": web_search}
],
stream=True,
)
# Получаем ответ от GPT
if isinstance(response, str):
final_response = response
else:
final_response = "Не удалось получить ответ."
history.append({"role": "assistant", "content": final_response}) # Добавляем ответ ИИ
yield history, *[gr.update(visible=False) for _ in range(6)]
return
if model_name == "GPT-4o THINK":
max_retries = 5
retry_count = 0
# Формируем thinking_prompt
min_steps = 2
max_steps = 25
thinking_depth = float(thinking_depth)
num_steps = max(min_steps, int(thinking_depth))
thinking_prompt = f"""Ты - система глубокого анализа. Твоя задача - максимально подробно проанализировать вопрос.
Глубина анализа: {thinking_depth:.1f}/10
Количество шагов размышления: {num_steps}
ВАЖНО: Строго соблюдай формат ответа. Каждый тег должен быть на новой строке:
[THINKING_STEP]
Текст первого шага...
[/THINKING_STEP]
[THINKING_STEP]
Текст второго шага...
[/THINKING_STEP]
[FINAL_ANSWER]
Итоговый ответ...
[/FINAL_ANSWER]
Вопрос: {message}
"""
while retry_count < max_retries:
try:
# Показываем статус попытки
thinking_html = f"""
<div class="thinking-animation">
<div class="thinking-step">🤔 Думаю...</div>
</div>
"""
history[-1]["content"] = thinking_html
yield history, *[gr.update(visible=False) for _ in range(6)]
print('Обработка запроса')
# Создаем запрос с таймаутом только для первого чанка
response = g4f.ChatCompletion.create(
model="gpt-4o",
messages=[{"role": "system", "content": thinking_prompt}],
stream=True,
# timeout=30
)
# Флаг для отслеживания получения первого чанка
received_first_chunk = False
partial_message = ""
current_block = ""
in_thinking_step = False
in_final_answer = False
for chunk in response:
if chunk and isinstance(chunk, str):
received_first_chunk = True
current_block += chunk
# Исправляем неправильно закрытые теги
# ... существующий код ...
current_block = (current_block
.replace("[/THINKING_STEP\n", "[/THINKING_STEP]\n") # Исправляем закрывающий тег THINKING_STEP
.replace("[/FINAL_ANSWER\n", "[/FINAL_ANSWER]\n") # Исправляем закрывающий тег FINAL_ANSWER
.replace("\n[/THINKING_STEP", "\n[/THINKING_STEP]") # Исправляем порядок закрывающего тега THINKING_STEP
.replace("\n[/FINAL_ANSWER", "\n[/FINAL_ANSWER]") # Исправляем порядок закрывающего тега FINAL_ANSWER
.replace("]]]]]]", "") # Убираем лишние закрывающие скобки
.replace("]]", "") # Убираем лишние закрывающие скобки
.replace("]", "") # Исправляем закрывающий тег THINKING_STEP с учетом закрывающей скобки
)
# ... существующий код ...
# Проверяем начало блоков
if "[THINKING_STEP]" in current_block:
in_thinking_step = True
current_block = current_block.replace("[THINKING_STEP]", "")
if "[THINKING_STEP" in current_block:
in_thinking_step = True
current_block = current_block.replace("[THINKING_STEP", "")
if "[FINAL_ANSWER]" in current_block:
in_final_answer = True
current_block = current_block.replace("[FINAL_ANSWER]", "")
# Проверяем завершение блоков
if "[/THINKING_STEP]" in current_block and in_thinking_step:
block_content = current_block[:current_block.find("[/THINKING_STEP]")]
formatted_block = f'<div class="thinking-step-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/THINKING_STEP]") + len("[/THINKING_STEP]"):]
in_thinking_step = False
if "[/THINKING_STEP" in current_block and in_thinking_step:
block_content = current_block[:current_block.find("[/THINKING_STEP")]
formatted_block = f'<div class="thinking-step-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/THINKING_STEP") + len("[/THINKING_STEP"):]
in_thinking_step = False
if "[/FINAL_ANSWER]" in current_block and in_final_answer:
block_content = current_block[:current_block.find("[/FINAL_ANSWER]")]
formatted_block = f'<div class="final-answer-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/FINAL_ANSWER]") + len("[/FINAL_ANSWER]"):]
in_final_answer = False
if "[/FINAL_ANSWER" in current_block and in_final_answer:
block_content = current_block[:current_block.find("[/FINAL_ANSWER")]
formatted_block = f'<div class="final-answer-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/FINAL_ANSWER") + len("[/FINAL_ANSWER"):]
in_final_answer = False
# Убираем лишние пробелы и переносы строк
display_message = partial_message + current_block.strip()
history[-1]["content"] = display_message
yield history, *[gr.update(visible=False) for _ in range(6)]
# # Если получили первый чанк, создаем новый стрим без таймаута
# if received_first_chunk:
# response = g4f.ChatCompletion.create(
# model="gpt-4o",
# messages=[{"role": "system", "content": thinking_prompt}],
# stream=True,
# provider=g4f.Provider.Airforce
# )
# break
# Продолжаем обработку без таймаута
if received_first_chunk:
for chunk in response:
if chunk and isinstance(chunk, str):
current_block += chunk
# Исправляем неправильно закрытые теги
# ... существующий код ...
current_block = (current_block
.replace("[/THINKING_STEP\n", "[/THINKING_STEP]\n") # Исправляем закрывающий тег THINKING_STEP
.replace("[/THINKING_STEP", "[/THINKING_STEP]\n") # Исправляем закрывающий тег THINKING_STEP
# .replace("[/THINKING_STEP]", "[/THINKING_STEP]\n") # Исправляем закрывающий тег THINKING_STEP с учетом закрывающей скобки
.replace("[/FINAL_ANSWER\n", "[/FINAL_ANSWER]\n") # Исправляем закрывающий тег FINAL_ANSWER
.replace("[/FINAL_ANSWER]", "[/FINAL_ANSWER]\n") # Исправляем закрывающий тег FINAL_ANSWER с учетом закрывающей скобки
.replace("\n[/THINKING_STEP", "\n[/THINKING_STEP]") # Исправляем порядок закрывающего тега THINKING_STEP
.replace("\n[/FINAL_ANSWER", "\n[/FINAL_ANSWER]") # Исправляем порядок закрывающего тега FINAL_ANSWER
.replace("]]]]]]", "") # Убираем лишние закрывающие скобки
.replace("]]", "") # Убираем лишние закрывающие скобки
.replace("]", "") # Убираем лишние закрывающие скобки
)
# Проверяем начало блоков
if "[THINKING_STEP]" in current_block:
in_thinking_step = True
current_block = current_block.replace("[THINKING_STEP]", "")
if "[THINKING_STEP" in current_block:
in_thinking_step = True
current_block = current_block.replace("[THINKING_STEP", "")
if "[FINAL_ANSWER]" in current_block:
in_final_answer = True
current_block = current_block.replace("[FINAL_ANSWER]", "")
if "[FINAL_ANSWER" in current_block:
in_final_answer = True
current_block = current_block.replace("[FINAL_ANSWER", "")
# Проверяем завершение блоков
if "[/THINKING_STEP]" in current_block and in_thinking_step:
block_content = current_block[:current_block.find("[/THINKING_STEP]")]
formatted_block = f'<div class="thinking-step-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/THINKING_STEP]") + len("[/THINKING_STEP]"):]
in_thinking_step = False
if "[/THINKING_STEP" in current_block and in_thinking_step:
block_content = current_block[:current_block.find("[/THINKING_STEP")]
formatted_block = f'<div class="thinking-step-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/THINKING_STEP") + len("[/THINKING_STEP"):]
in_thinking_step = False
elif "[/FINAL_ANSWER]" in current_block and in_final_answer:
block_content = current_block[:current_block.find("[/FINAL_ANSWER]")]
formatted_block = f'<div class="final-answer-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/FINAL_ANSWER") + len("[/FINAL_ANSWER]"):]
in_final_answer = False
elif "[/FINAL_ANSWER" in current_block and in_final_answer:
block_content = current_block[:current_block.find("[/FINAL_ANSWER")]
formatted_block = f'<div class="final-answer-block">{block_content}</div>'
partial_message += formatted_block
current_block = current_block[current_block.find("[/FINAL_ANSWER") + len("[/FINAL_ANSWER"):]
in_final_answer = False
# Убираем лишние пробелы и переносы строк
display_message = partial_message + current_block.strip()
history[-1]["content"] = display_message
yield history, *[gr.update(visible=False) for _ in range(6)]
break
retry_count += 1
if retry_count == max_retries:
error_message = f"""
<div class="error-animation">
<div class="error-text">❌ Не удалось получить ответ после {max_retries} попыток.
Попробуйте использовать обычный режим или повторите запрос позже.</div>
</div>
"""
history[-1]["content"] = error_message
yield history, *[gr.update(visible=False) for _ in range(6)]
return
except Exception as e:
retry_count += 1
if retry_count == max_retries:
error_message = f"""
<div class="error-animation">
<div class="error-text">❌ Ошибка при анализе: {str(e)}</div>
</div>
"""
history[-1]["content"] = error_message
yield history, *[gr.update(visible=False) for _ in range(6)]
return
else:
# Обычный режим работы
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, "gpt-4o"),
messages=messages,
stream=True,
provider=g4f.Provider.Airforce
)
for chunk in response:
if chunk and isinstance(chunk, str):
partial_message += chunk
# Проверяем на запрос генерации изображения
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()
# Показываем статус генерации
history[-1]["content"] = """
<div class="generating-animation">
<div class="generating-text">Генерация изображения...</div>
</div>
"""
yield history, *[gr.update(visible=False) for _ in range(6)]
# Генерируем изображение
image_url = generate_image(image_prompt)
if not image_url.startswith("Ошибка"):
# Анализируем изображение
# image_analysis = process_image(image_url)
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) # Сбрасываем файл после ответа
)
except Exception as e:
print(f"Error: {e}")
if not history:
history = [{"role": "user", "content": message}, {"role": "assistant", "content": ""}]
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) # Сбрасываем файл в случае ошибки
)
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);
}
.message.user {
background-color: #3d3d3d !important;
color: white !important;
border-radius: 15px;
}
.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);
}
}
"""
# Добавте этот скрипт в 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
)
# Добавляем компоненты для вывода
output_text = gr.Textbox(
label="Вывод",
interactive=False,
visible=False
)
error_text = gr.Textbox(
label="⚠️ Ошибки",
interactive=False,
visible=False
)
ask_help_btn = gr.Button(
"❓ Спросить почему возникла ошибка",
visible=False
)
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="GPT-4o",
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"
)
def run_code(code):
if not code:
return {
output_text: gr.update(value=" Нет кода для выполнения", visible=True),
error_text: gr.update(visible=False),
ask_help_btn: gr.update(visible=False)
}
result = test_code(code)
return {
output_text: gr.update(value=result["output"] if result["success"] else "", visible=True),
error_text: gr.update(value=result["error"], visible=bool(result["error"])),
# ask_help_btn: gr.update(visible=bool(result["error"]))
}
def handle_error_question(error_message, code_block):
"""Обработчик кнопки помощи с ошибкой"""
error_prompt = f"""Объясните ошибку и как её исправить:
Код:
```
{code_block}
```
Ошибка:
{error_message}"""
try:
response = g4f.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": error_prompt}],
stream=False
)
return [{"role": "assistant", "content": response if isinstance(response, str) else "Не удалось получиь ответ"}]
except Exception as e:
return [{"role": "assistant", "content": f"Ошибка при получнии объяснения: {str(e)}"}]
# Обработчики событий
msg.submit(
fn=chat_response,
inputs=[msg, chatbot, model, direct_mode, thinking_depth, file_output],
outputs=[chatbot, output_text, error_text, ask_help_btn, current_code, analyze_btn, run_btn],
api_name=None
)
submit.click(
fn=chat_response,
inputs=[msg, chatbot, model, direct_mode, thinking_depth, file_output],
outputs=[chatbot, output_text, error_text, ask_help_btn, current_code, analyze_btn, run_btn],
api_name=None
)
ask_help_btn.click(
fn=handle_error_question,
inputs=[error_text, current_code],
outputs=chatbot
)
analyze_btn.click(
fn=analyze_code,
inputs=[current_code],
outputs=[chatbot]
)
run_btn.click(
fn=run_code,
inputs=[current_code],
outputs=[output_text, error_text, ask_help_btn]
)
clear.click(
fn=lambda: (None, "", "", False, ""),
outputs=[chatbot, output_text, error_text, ask_help_btn, 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, # Добавим этот параметр
)