Spaces:
Build error
Build error
| 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, # Добавим этот параметр | |
| ) | |