Spaces:
Runtime error
Runtime error
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| import gradio as gr | |
| import os | |
| import re | |
| # Конфигурация модели | |
| MODEL_NAME = "Qwen/Qwen2.5-Coder-14B-Instruct-GPTQ-Int4" | |
| DEVICE = "cpu" # Запуск на CPU | |
| # Глобальная загрузка модели (один раз при запуске) | |
| def load_model(): | |
| print("🔄 Загружаем токенизатор...") | |
| tokenizer = AutoTokenizer.from_pretrained( | |
| MODEL_NAME, | |
| trust_remote_code=True | |
| ) | |
| # Добавляем pad token если его нет | |
| if tokenizer.pad_token is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| print("🔄 Загружаем GPTQ-модель Qwen2.5-Coder-14B...") | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_NAME, | |
| torch_dtype=torch.float32, # Используем float32 для CPU | |
| device_map="cpu", # Явно указываем CPU | |
| trust_remote_code=True | |
| ) | |
| model.eval() # Переводим модель в режим оценки | |
| return tokenizer, model | |
| # Загружаем модель один раз при старте | |
| try: | |
| tokenizer, model = load_model() | |
| print("✅ Qwen2.5-Coder-14B-Instruct-GPTQ-Int4 успешно загружена!") | |
| except Exception as e: | |
| print(f"❌ Ошибка загрузки модели: {e}") | |
| tokenizer, model = None, None | |
| def read_file_content(file_path): | |
| """Читает содержимое файла с обработкой различных кодировок""" | |
| try: | |
| # Пробуем разные кодировки | |
| encodings = ['utf-8', 'cp1251', 'iso-8859-1', 'windows-1251'] | |
| for encoding in encodings: | |
| try: | |
| with open(file_path, 'r', encoding=encoding) as f: | |
| content = f.read() | |
| return content | |
| except UnicodeDecodeError: | |
| continue | |
| # Если текстовые кодировки не работают, пробуем бинарный режим | |
| if file_path.endswith(('.py', '.txt', '.js', '.html', '.css', '.json', '.md')): | |
| with open(file_path, 'rb') as f: | |
| content = f.read() | |
| return content.decode('utf-8', errors='replace') | |
| else: | |
| return f"Файл {os.path.basename(file_path)} не является текстовым файлом" | |
| except Exception as e: | |
| return f"Ошибка чтения файла: {str(e)}" | |
| def extract_search_terms(prompt): | |
| """Извлекает поисковые термины из промпта в формате [поиск: ...]""" | |
| search_pattern = r'\[поиск:\s*(.*?)\]' | |
| matches = re.findall(search_pattern, prompt, re.IGNORECASE) | |
| if matches: | |
| # Удаляем поисковую часть из промпта | |
| clean_prompt = re.sub(search_pattern, '', prompt).strip() | |
| return clean_prompt, matches[0].strip() | |
| return prompt, None | |
| def generate_code_with_context(prompt, files, max_length=1024, temperature=0.7, top_p=0.9): | |
| """ | |
| Генерирует код на основе промпта пользователя с учетом загруженных файлов. | |
| Эта функция автоматически станет доступна как MCP-инструмент для других приложений. | |
| Args: | |
| prompt (str): Запрос пользователя, может содержать [поиск: термин] | |
| files (list): Список загруженных файлов для анализа | |
| max_length (int): Максимальная длина ответа в токенах | |
| temperature (float): Параметр температуры для генерации | |
| top_p (float): Параметр top-p для генерации | |
| Returns: | |
| str: Сгенерированный код или сообщение об ошибке | |
| """ | |
| if model is None or tokenizer is None: | |
| return "❌ Ошибка: модель не загружена. Проверьте:\n- Подключение к интернету\n- Достаточно ли оперативной памяти (рекомендуется 16+ ГБ)\n- Установлены ли зависимости: `pip install auto-gptq optimum`" | |
| try: | |
| # Извлекаем поисковые термины из промпта | |
| clean_prompt, search_term = extract_search_terms(prompt) | |
| # Обрабатываем загруженные файлы | |
| file_contexts = [] | |
| if files: | |
| for file_info in files: | |
| if hasattr(file_info, 'name'): | |
| file_path = file_info.name | |
| else: | |
| file_path = file_info | |
| content = read_file_content(file_path) | |
| filename = os.path.basename(file_path) | |
| file_contexts.append(f"Файл: {filename}\n```\n{content[:2000]}\n```") | |
| # Формируем финальный промпт | |
| final_prompt = clean_prompt | |
| if file_contexts: | |
| files_context = "\n\n".join(file_contexts) | |
| final_prompt = f"""Контекст из загруженных файлов: | |
| {files_context} | |
| Запрос: {clean_prompt}""" | |
| # Форматируем сообщение для модели в соответствии с официальным форматом Qwen2.5 :cite[1] | |
| messages = [ | |
| {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, | |
| {"role": "user", "content": final_prompt} | |
| ] | |
| # Применяем шаблон чата :cite[1] | |
| text = tokenizer.apply_chat_template( | |
| messages, | |
| tokenize=False, | |
| add_generation_prompt=True | |
| ) | |
| # Токенизируем входные данные с ограничением длины | |
| inputs = tokenizer( | |
| text, | |
| return_tensors="pt", | |
| truncation=True, | |
| max_length=2048, | |
| padding=True, | |
| return_attention_mask=True | |
| ) | |
| # Генерация с использованием обновленного кода и attention_mask | |
| with torch.no_grad(): | |
| generated_ids = model.generate( | |
| inputs.input_ids, | |
| attention_mask=inputs.attention_mask, # Добавляем attention_mask | |
| max_new_tokens=max_length, | |
| temperature=temperature, | |
| top_p=top_p, | |
| do_sample=True, | |
| pad_token_id=tokenizer.eos_token_id, | |
| repetition_penalty=1.1, | |
| no_repeat_ngram_size=3 | |
| ) | |
| # Декодируем результат | |
| generated_text = tokenizer.decode(generated_ids[0], skip_special_tokens=True) | |
| # Убираем оригинальный промпт из ответа | |
| if generated_text.startswith(text): | |
| response = generated_text[len(text):].strip() | |
| else: | |
| response = generated_text.strip() | |
| return response | |
| except Exception as e: | |
| return f"❌ Ошибка при генерации кода: {str(e)}" | |
| # Создаем интерфейс Gradio | |
| with gr.Blocks(title="Qwen2.5-Coder-14B with MCP", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # 🚀 Qwen2.5-Coder-14B-Instruct-GPTQ-Int4 + MCP | |
| **Профессиональный генератор кода с поддержкой Model Context Protocol** | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| prompt_input = gr.Textbox( | |
| lines=4, | |
| placeholder="""Введите ваш запрос... Примеры: | |
| - "Напиши функцию для быстрой сортировки на Python" | |
| - "Создай REST API на FastAPI для управления пользователями" | |
| - "Найди и исправь ошибку в загруженном коде [поиск: syntax error]" """, | |
| label="Запрос на генерацию кода", | |
| info="Используйте [поиск: ...] для поиска в файлах" | |
| ) | |
| with gr.Accordion("📁 Загрузка файлов для анализа", open=True): | |
| file_input = gr.File( | |
| label="Загрузите файлы для анализа", | |
| file_count="multiple", | |
| file_types=[".txt", ".py", ".js", ".html", ".css", ".json", ".md", ".java", ".cpp", ".c"], | |
| type="filepath" | |
| ) | |
| with gr.Accordion("⚙️ Параметры генерации", open=False): | |
| max_length_slider = gr.Slider( | |
| minimum=100, maximum=2048, value=512, | |
| label="Максимальная длина ответа (токены)" | |
| ) | |
| temperature_slider = gr.Slider( | |
| minimum=0.1, maximum=1.0, value=0.7, | |
| label="Температура (креативность)" | |
| ) | |
| top_p_slider = gr.Slider( | |
| minimum=0.1, maximum=1.0, value=0.9, | |
| label="Top-p (вероятностный отбор)" | |
| ) | |
| generate_btn = gr.Button("🚀 Сгенерировать код", variant="primary") | |
| with gr.Column(): | |
| response_output = gr.Textbox( | |
| label="Сгенерированный код", | |
| lines=18, | |
| show_copy_button=True | |
| ) | |
| # Добавляем информацию о MCP | |
| with gr.Accordion("🔗 MCP Сервер - Подключение к другим приложениям", open=True): | |
| gr.Markdown(""" | |
| **MCP (Model Context Protocol) сервер активирован!** | |
| Ваш генератор кода теперь доступен как MCP-инструмент для: | |
| - Claude Desktop | |
| - Cursor | |
| - Cline | |
| - Других MCP-клиентов | |
| **URL для подключения:** | |
| - Основной MCP URL: `http://localhost:7860/gradio_api/mcp/` | |
| - SSE URL: `http://localhost:7860/gradio_api/mcp/sse` | |
| **Для подключения к Claude Desktop** добавьте в настройки (`claude_desktop_config.json`): | |
| ```json | |
| { | |
| "mcpServers": { | |
| "qwen-coder-generator": { | |
| "url": "http://localhost:7860/gradio_api/mcp/sse" | |
| } | |
| } | |
| } | |
| ``` | |
| """) | |
| # Информация о модели | |
| with gr.Accordion("ℹ️ О модели Qwen2.5-Coder-14B", open=False): | |
| gr.Markdown(""" | |
| **Qwen2.5-Coder-14B-Instruct** - это специализированная модель для программирования :cite[6]: | |
| - **Параметры**: 14.7 миллиардов (квантованные в INT4) | |
| - **Специализация**: Генерация кода, исправление ошибок, код-ризонинг | |
| - **Контекст**: До 128K токенов :cite[1] | |
| - **Языки программирования**: Поддержка 40+ языков :cite[6] | |
| - **Память**: ~4-6 ГБ RAM (благодаря GPTQ-квантованию) | |
| **Улучшения Qwen2.5 по сравнению с Qwen2** :cite[1]: | |
| - Значительно больше знаний и улучшенные возможности в программировании | |
| - Улучшенное следование инструкциям и генерация длинных текстов | |
| - Поддержка многоязычия (29+ языков) | |
| """) | |
| # Обработчики событий | |
| generate_btn.click( | |
| fn=generate_code_with_context, | |
| inputs=[prompt_input, file_input, max_length_slider, temperature_slider, top_p_slider], | |
| outputs=response_output | |
| ) | |
| # Запускаем приложение с MCP-сервером | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| ssr_mode=False, # Явно отключаем SSR | |
| mcp_server=True | |
| ) |