Kremon96 commited on
Commit
adbdeb6
·
verified ·
1 Parent(s): 12f2dbd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -125
app.py CHANGED
@@ -1,17 +1,16 @@
1
  import torch
2
- from transformers import AutoTokenizer, AutoModelForCausalLM
3
  import gradio as gr
4
  import os
5
  import re
6
- from pathlib import Path
7
 
8
  # Конфигурация модели
9
- MODEL_NAME = "Qwen/Qwen2.5-Coder-3B-Instruct"
10
  DEVICE = "cpu" # Запуск на CPU
11
 
12
  # Глобальная загрузка модели (один раз при запуске)
13
  def load_model():
14
- print("Загрузка токенизатора...")
15
  tokenizer = AutoTokenizer.from_pretrained(
16
  MODEL_NAME,
17
  trust_remote_code=True
@@ -21,11 +20,10 @@ def load_model():
21
  if tokenizer.pad_token is None:
22
  tokenizer.pad_token = tokenizer.eos_token
23
 
24
- print("Загрузка модели... (это может занять несколько минут)")
25
  model = AutoModelForCausalLM.from_pretrained(
26
  MODEL_NAME,
27
  torch_dtype=torch.float32, # Используем float32 для CPU
28
- low_cpu_mem_usage=True, # Снижаем использование памяти
29
  device_map="cpu", # Явно указываем CPU
30
  trust_remote_code=True
31
  )
@@ -35,9 +33,9 @@ def load_model():
35
  # Загружаем модель один раз при старте
36
  try:
37
  tokenizer, model = load_model()
38
- print("Модель успешно загружена!")
39
  except Exception as e:
40
- print(f"Ошибка загрузки модели: {e}")
41
  tokenizer, model = None, None
42
 
43
  def read_file_content(file_path):
@@ -54,11 +52,10 @@ def read_file_content(file_path):
54
  except UnicodeDecodeError:
55
  continue
56
 
57
- # Если текстовые кодировки не работают, пробуем бинарный режим для определенных типов
58
  if file_path.endswith(('.py', '.txt', '.js', '.html', '.css', '.json', '.md')):
59
  with open(file_path, 'rb') as f:
60
  content = f.read()
61
- # Пробуем декодировать с заменой ошибок
62
  return content.decode('utf-8', errors='replace')
63
  else:
64
  return f"Файл {os.path.basename(file_path)} не является текстовым файлом"
@@ -78,39 +75,24 @@ def extract_search_terms(prompt):
78
 
79
  return prompt, None
80
 
81
- def search_and_highlight(content, search_term):
82
- """Ищет термин в содержимом и возвращает подсвеченные результаты"""
83
- if not content or not search_term:
84
- return content
85
-
86
- lines = content.split('\n')
87
- highlighted_lines = []
88
- matches_found = 0
89
 
90
- for i, line in enumerate(lines):
91
- if search_term.lower() in line.lower():
92
- matches_found += 1
93
- # Подсвечиваем найденный термин
94
- highlighted_line = re.sub(
95
- f'({re.escape(search_term)})',
96
- '**\\1**',
97
- line,
98
- flags=re.IGNORECASE
99
- )
100
- highlighted_lines.append(f"🔍 Строка {i+1}: {highlighted_line}")
101
 
102
- if matches_found > 0:
103
- return f"Найдено совпадений: {matches_found}\n\n" + "\n".join(highlighted_lines[:10]) # Ограничиваем вывод
104
- else:
105
- return "Совпадений не найдено"
106
-
107
- def generate_code_with_context(prompt, files, max_length=2048, temperature=0.7, top_p=0.9):
108
- """
109
- Генерирует код на основе промпта пользователя с учетом загруженных файлов
110
- и автоматическим поиском в них
111
  """
112
  if model is None or tokenizer is None:
113
- return "Ошибка: модель не загружена"
114
 
115
  try:
116
  # Извлекаем поисковые термины из промпта
@@ -118,7 +100,6 @@ def generate_code_with_context(prompt, files, max_length=2048, temperature=0.7,
118
 
119
  # Обрабатываем загруженные файлы
120
  file_contexts = []
121
- search_results = []
122
 
123
  if files:
124
  for file_info in files:
@@ -129,43 +110,24 @@ def generate_code_with_context(prompt, files, max_length=2048, temperature=0.7,
129
 
130
  content = read_file_content(file_path)
131
  filename = os.path.basename(file_path)
132
-
133
- # Если есть поисковый термин, выполняем поиск
134
- if search_term:
135
- search_result = search_and_highlight(content, search_term)
136
- search_results.append(f"**Файл: {filename}**\n{search_result}")
137
-
138
- # Добавляем полное содержимое для контекста (ограниченное)
139
  file_contexts.append(f"Файл: {filename}\n```\n{content[:2000]}\n```")
140
 
141
  # Формируем финальный промпт
142
  final_prompt = clean_prompt
143
-
144
- # Добавляем результаты поиска если есть
145
- if search_results:
146
- search_context = "\n\n".join(search_results)
147
- final_prompt = f"""Результаты поиска "{search_term}" в файлах:
148
- {search_context}
149
-
150
- Запрос: {clean_prompt}"""
151
-
152
- # Добавляем полный контекст файлов если нет поиска или мало результатов
153
- elif file_contexts:
154
  files_context = "\n\n".join(file_contexts)
155
  final_prompt = f"""Контекст из загруженных файлов:
156
  {files_context}
157
 
158
  Запрос: {clean_prompt}"""
159
 
160
- # Форматируем сообщение для модели
161
  messages = [
162
- {"role": "system", "content": """Ты - эксперт по программированию.
163
- Анализируй загруженные файлы и результаты поиска, затем генерируй релевантный код.
164
- Используй контекст из файлов для понимания структуры и стиля кода."""},
165
  {"role": "user", "content": final_prompt}
166
  ]
167
 
168
- # Применяем шаблон чата
169
  text = tokenizer.apply_chat_template(
170
  messages,
171
  tokenize=False,
@@ -180,7 +142,7 @@ def generate_code_with_context(prompt, files, max_length=2048, temperature=0.7,
180
  max_length=2048
181
  )
182
 
183
- # Генерируем ответ
184
  with torch.no_grad():
185
  outputs = model.generate(
186
  inputs.input_ids,
@@ -206,27 +168,25 @@ def generate_code_with_context(prompt, files, max_length=2048, temperature=0.7,
206
  return response
207
 
208
  except Exception as e:
209
- return f"Ошибка при генерации кода: {str(e)}"
210
 
211
- # Создаем расширенный интерфейс Gradio
212
- with gr.Blocks(title="Qwen2.5-Coder-3B with Smart Search", theme=gr.themes.Soft()) as demo:
213
  gr.Markdown("""
214
- # 🚀 Qwen2.5-Coder-3B: Умный поиск и генерация кода
215
- **Объединенная система для анализа файлов, поиска и генерации кода**
216
  """)
217
 
218
  with gr.Row():
219
  with gr.Column():
220
  prompt_input = gr.Textbox(
221
  lines=4,
222
- placeholder="""Опишите задачу для генерации кода. Используйте [поиск: термин] для поиска в файлах.
223
-
224
- Примеры:
225
- - "Добавь валидацию в функцию [поиск: validate_user]"
226
- - "Исправь баг в модуле [поиск: calculate_score]"
227
- - "Создай тесты для классов из загруженных файлов" """,
228
- label="Запрос на генерацию кода с поиском",
229
- info="Используйте [поиск: ...] для указания что искать в файлах"
230
  )
231
 
232
  with gr.Accordion("📁 Загрузка файлов для анализа", open=True):
@@ -239,7 +199,7 @@ with gr.Blocks(title="Qwen2.5-Coder-3B with Smart Search", theme=gr.themes.Soft(
239
 
240
  with gr.Accordion("⚙️ Параметры генерации", open=False):
241
  max_length_slider = gr.Slider(
242
- minimum=100, maximum=4096, value=1024,
243
  label="Максимальная длина ответа (токены)"
244
  )
245
  temperature_slider = gr.Slider(
@@ -251,77 +211,70 @@ with gr.Blocks(title="Qwen2.5-Coder-3B with Smart Search", theme=gr.themes.Soft(
251
  label="Top-p (вероятностный отбор)"
252
  )
253
 
254
- generate_btn = gr.Button("🔍 Найти и сгенерировать", variant="primary")
255
- clear_btn = gr.Button("Очистить всё")
256
 
257
  with gr.Column():
258
- code_output = gr.Code(
259
  label="Сгенерированный код",
260
- language="python",
261
- interactive=True,
262
- lines=20
263
  )
264
 
265
- # Добавляем примеры использования
266
- with gr.Accordion("🎯 Примеры использования", open=True):
267
  gr.Markdown("""
268
- ### **Умные сценарии использования:**
269
-
270
- **1. Поиск и модификация:**
271
- ```
272
- "Добавь обработку ошибок в функцию [поиск: process_data]"
273
- ```
274
 
275
- **2. Анализ и расширение:**
276
- ```
277
- "Создай unit-тесты для классов в загруженных файлах [поиск: class User]"
278
- ```
 
279
 
280
- **3. Рефакторинг:**
281
- ```
282
- "Оптимизируй алгоритмы сортировки [поиск: def sort]"
283
- ```
284
 
285
- **4. Без поиска (анализ всех файлов):**
286
- ```
287
- "Проанализируй архитектуру проекта и предложи улучшения"
 
 
 
 
 
 
288
  ```
289
  """)
290
 
291
- # Добавляем информацию о системе
292
- with gr.Accordion("ℹ️ О системе", open=False):
293
  gr.Markdown("""
294
- **Как это работает:**
295
- 1. **Загрузите файлы** - вашу кодовую базу для анализа
296
- 2. **Введите запрос** с опциональным `[поиск: термин]`
297
- 3. **Система автоматически:**
298
- - Находит указанные термины в файлах
299
- - Анализирует контекст вокруг найденного
300
- - Генерирует релевантный код с учетом структуры проекта
301
 
302
- **Поддерживаемые языки:** Python, JavaScript, Java, C++, HTML, CSS, JSON и другие
 
 
 
303
  """)
304
 
305
  # Обработчики событий
306
  generate_btn.click(
307
  fn=generate_code_with_context,
308
  inputs=[prompt_input, file_input, max_length_slider, temperature_slider, top_p_slider],
309
- outputs=code_output
310
- )
311
-
312
- def clear_all():
313
- return "", None, ""
314
-
315
- clear_btn.click(
316
- fn=clear_all,
317
- inputs=[],
318
- outputs=[prompt_input, file_input, code_output]
319
  )
320
 
321
- # Запускаем интерфейс
322
  if __name__ == "__main__":
323
  demo.launch(
324
  server_name="0.0.0.0",
325
  server_port=7860,
326
- share=False
 
327
  )
 
1
  import torch
2
+ from transformers import AutoModelForCausalLM, AutoTokenizer
3
  import gradio as gr
4
  import os
5
  import re
 
6
 
7
  # Конфигурация модели
8
+ MODEL_NAME = "Qwen/Qwen2.5-Coder-14B-Instruct-GPTQ-Int4"
9
  DEVICE = "cpu" # Запуск на CPU
10
 
11
  # Глобальная загрузка модели (один раз при запуске)
12
  def load_model():
13
+ print("🔄 Загружаем токенизатор...")
14
  tokenizer = AutoTokenizer.from_pretrained(
15
  MODEL_NAME,
16
  trust_remote_code=True
 
20
  if tokenizer.pad_token is None:
21
  tokenizer.pad_token = tokenizer.eos_token
22
 
23
+ print("🔄 Загружаем GPTQ-модель Qwen2.5-Coder-14B...")
24
  model = AutoModelForCausalLM.from_pretrained(
25
  MODEL_NAME,
26
  torch_dtype=torch.float32, # Используем float32 для CPU
 
27
  device_map="cpu", # Явно указываем CPU
28
  trust_remote_code=True
29
  )
 
33
  # Загружаем модель один раз при старте
34
  try:
35
  tokenizer, model = load_model()
36
+ print(" Qwen2.5-Coder-14B-Instruct-GPTQ-Int4 успешно загружена!")
37
  except Exception as e:
38
+ print(f"Ошибка загрузки модели: {e}")
39
  tokenizer, model = None, None
40
 
41
  def read_file_content(file_path):
 
52
  except UnicodeDecodeError:
53
  continue
54
 
55
+ # Если текстовые кодировки не работают, пробуем бинарный режим
56
  if file_path.endswith(('.py', '.txt', '.js', '.html', '.css', '.json', '.md')):
57
  with open(file_path, 'rb') as f:
58
  content = f.read()
 
59
  return content.decode('utf-8', errors='replace')
60
  else:
61
  return f"Файл {os.path.basename(file_path)} не является текстовым файлом"
 
75
 
76
  return prompt, None
77
 
78
+ def generate_code_with_context(prompt, files, max_length=1024, temperature=0.7, top_p=0.9):
79
+ """
80
+ Генерирует код на основе промпта пользователя с учетом загруженных файлов.
 
 
 
 
 
81
 
82
+ Эта функция автоматически станет доступна как MCP-инструмент для других приложений.
 
 
 
 
 
 
 
 
 
 
83
 
84
+ Args:
85
+ prompt (str): Запрос пользователя, может содержать [поиск: термин]
86
+ files (list): Список загруженных файлов для анализа
87
+ max_length (int): Максимальная длина ответа в токенах
88
+ temperature (float): Параметр температуры для генерации
89
+ top_p (float): Параметр top-p для генерации
90
+
91
+ Returns:
92
+ str: Сгенерированный код или сообщение об ошибке
93
  """
94
  if model is None or tokenizer is None:
95
+ return "Ошибка: модель не загружена. Проверьте:\n- Подключение к интернету\n- Достаточно ли оперативной памяти (рекомендуется 16+ ГБ)\n- Установлены ли зависимости: `pip install auto-gptq optimum`"
96
 
97
  try:
98
  # Извлекаем поисковые термины из промпта
 
100
 
101
  # Обрабатываем загруженные файлы
102
  file_contexts = []
 
103
 
104
  if files:
105
  for file_info in files:
 
110
 
111
  content = read_file_content(file_path)
112
  filename = os.path.basename(file_path)
 
 
 
 
 
 
 
113
  file_contexts.append(f"Файл: {filename}\n```\n{content[:2000]}\n```")
114
 
115
  # Формируем финальный промпт
116
  final_prompt = clean_prompt
117
+ if file_contexts:
 
 
 
 
 
 
 
 
 
 
118
  files_context = "\n\n".join(file_contexts)
119
  final_prompt = f"""Контекст из загруженных файлов:
120
  {files_context}
121
 
122
  Запрос: {clean_prompt}"""
123
 
124
+ # Форматируем сообщение для модели в соответствии с официальным форматом Qwen2.5 :cite[1]
125
  messages = [
126
+ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
 
 
127
  {"role": "user", "content": final_prompt}
128
  ]
129
 
130
+ # Применяем шаблон чата :cite[1]
131
  text = tokenizer.apply_chat_template(
132
  messages,
133
  tokenize=False,
 
142
  max_length=2048
143
  )
144
 
145
+ # Генерируем ответ с оптимизированными параметрами для CPU
146
  with torch.no_grad():
147
  outputs = model.generate(
148
  inputs.input_ids,
 
168
  return response
169
 
170
  except Exception as e:
171
+ return f"Ошибка при генерации кода: {str(e)}"
172
 
173
+ # Создаем интерфейс Gradio
174
+ with gr.Blocks(title="Qwen2.5-Coder-14B with MCP", theme=gr.themes.Soft()) as demo:
175
  gr.Markdown("""
176
+ # 🚀 Qwen2.5-Coder-14B-Instruct-GPTQ-Int4 + MCP
177
+ **Профессиональный генератор кода с поддержкой Model Context Protocol**
178
  """)
179
 
180
  with gr.Row():
181
  with gr.Column():
182
  prompt_input = gr.Textbox(
183
  lines=4,
184
+ placeholder="""Введите ваш запрос... Примеры:
185
+ - "Напиши функцию для быстрой сортировки на Python"
186
+ - "Создай REST API на FastAPI для управления пользователями"
187
+ - "Найди и исправь ошибку в загруженном коде [поиск: syntax error]" """,
188
+ label="Запрос на генерацию кода",
189
+ info="Используйте [поиск: ...] для поиска в файлах"
 
 
190
  )
191
 
192
  with gr.Accordion("📁 Загрузка файлов для анализа", open=True):
 
199
 
200
  with gr.Accordion("⚙️ Параметры генерации", open=False):
201
  max_length_slider = gr.Slider(
202
+ minimum=100, maximum=2048, value=512,
203
  label="Максимальная длина ответа (токены)"
204
  )
205
  temperature_slider = gr.Slider(
 
211
  label="Top-p (вероятностный отбор)"
212
  )
213
 
214
+ generate_btn = gr.Button("🚀 Сгенерировать код", variant="primary")
 
215
 
216
  with gr.Column():
217
+ response_output = gr.Textbox(
218
  label="Сгенерированный код",
219
+ lines=18,
220
+ show_copy_button=True
 
221
  )
222
 
223
+ # Добавляем информацию о MCP
224
+ with gr.Accordion("🔗 MCP Сервер - Подключение к другим приложениям", open=True):
225
  gr.Markdown("""
226
+ **MCP (Model Context Protocol) сервер активирован!**
 
 
 
 
 
227
 
228
+ Ваш генератор кода теперь доступен как MCP-инструмент для:
229
+ - Claude Desktop
230
+ - Cursor
231
+ - Cline
232
+ - Других MCP-клиентов
233
 
234
+ **URL для подключения:**
235
+ - Основной MCP URL: `http://localhost:7860/gradio_api/mcp/`
236
+ - SSE URL: `http://localhost:7860/gradio_api/mcp/sse`
 
237
 
238
+ **Для подключения к Claude Desktop** добавьте в настройки (`claude_desktop_config.json`):
239
+ ```json
240
+ {
241
+ "mcpServers": {
242
+ "qwen-coder-generator": {
243
+ "url": "http://localhost:7860/gradio_api/mcp/sse"
244
+ }
245
+ }
246
+ }
247
  ```
248
  """)
249
 
250
+ # Информация о модели
251
+ with gr.Accordion("ℹ️ О модели Qwen2.5-Coder-14B", open=False):
252
  gr.Markdown("""
253
+ **Qwen2.5-Coder-14B-Instruct** - это специализированная модель для программирования :cite[6]:
254
+ - **Параметры**: 14.7 миллиардов (квантованные в INT4)
255
+ - **Специализация**: Генерация кода, исправление ошибок, код-ризонинг
256
+ - **Контекст**: До 128K токенов :cite[1]
257
+ - **Языки программирования**: Поддержка 40+ языков :cite[6]
258
+ - **Память**: ~4-6 ГБ RAM (благодаря GPTQ-квантованию)
 
259
 
260
+ **Улучшения Qwen2.5 по сравнению с Qwen2** :cite[1]:
261
+ - Значительно больше знаний и улучшенные возможности в программировании
262
+ - Улучшенное следование инструкциям и генерация длинных текстов
263
+ - Поддержка многоязычия (29+ языков)
264
  """)
265
 
266
  # Обработчики событий
267
  generate_btn.click(
268
  fn=generate_code_with_context,
269
  inputs=[prompt_input, file_input, max_length_slider, temperature_slider, top_p_slider],
270
+ outputs=response_output
 
 
 
 
 
 
 
 
 
271
  )
272
 
273
+ # Запускаем приложение с MCP-сервером
274
  if __name__ == "__main__":
275
  demo.launch(
276
  server_name="0.0.0.0",
277
  server_port=7860,
278
+ share=False,
279
+ mcp_server=True # 🔥 ВКЛЮЧАЕМ MCP-СЕРВЕР
280
  )