sterepando commited on
Commit
d34f2b5
·
verified ·
1 Parent(s): 2d6e66f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -0
app.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import os
4
+ import torch
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
6
+ from peft import PeftModel
7
+ from threading import Thread
8
+
9
+ # --- КОНФИГУРАЦИЯ ---
10
+ # Базовая модель. Для CPU лучше использовать gemma-2b, но вы просили 7b.
11
+ # Если будет падать по памяти, замените на "google/gemma-2b-it"
12
+ BASE_MODEL_ID = "google/gemma-7b-it"
13
+ ADAPTER_PATH = "mandre_qlora_adapter" # Путь, куда вы положите обученный адаптер (если есть)
14
+
15
+ # Глобальные переменные для модели
16
+ model = None
17
+ tokenizer = None
18
+
19
+ # ==========================================
20
+ # ЧАСТЬ 1: ГЕНЕРАТОР ДАТАСЕТА (MandreLib Logic)
21
+ # ==========================================
22
+
23
+ def read_file_content(file_obj):
24
+ """Читает содержимое загруженного файла"""
25
+ try:
26
+ with open(file_obj.name, 'r', encoding='utf-8') as f:
27
+ return f.read()
28
+ except UnicodeDecodeError:
29
+ return None # Пропускаем бинарники
30
+
31
+ def generate_dataset(files):
32
+ if not files:
33
+ return None, "Пожалуйста, загрузите файлы."
34
+
35
+ dataset = []
36
+
37
+ for file_obj in files:
38
+ content = read_file_content(file_obj)
39
+ if not content:
40
+ continue
41
+
42
+ filename = os.path.basename(file_obj.name)
43
+
44
+ # Эвристика для создания промпта на основе типа файла
45
+ system_instruction = "You are an expert developer for ExteraGram and MandreLib."
46
+ user_instruction = ""
47
+
48
+ if filename.endswith(".plugin.py") or filename == "main.py":
49
+ user_instruction = f"Analyze the following ExteraGram plugin code named '{filename}'. Explain its structure and functionality."
50
+ elif "MandreLib" in filename:
51
+ user_instruction = f"Provide documentation and usage examples for the MandreLib library file '{filename}'."
52
+ elif filename.endswith(".md"):
53
+ user_instruction = f"Summarize the documentation provided in '{filename}'."
54
+ elif filename.endswith(".java"):
55
+ user_instruction = f"Explain the Java hooks or utils implemented in '{filename}' for ExteraGram."
56
+ else:
57
+ user_instruction = f"Analyze the content of the file '{filename}' related to MandreAI development."
58
+
59
+ # Формат Alpaca/ShareGPT для обучения
60
+ entry = {
61
+ "instruction": user_instruction,
62
+ "input": "", # Можно оставить пустым или добавить метаданные
63
+ "output": content, # Нейронка учится воспроизводить код/доки
64
+ "source": filename
65
+ }
66
+ dataset.append(entry)
67
+
68
+ # Сохраняем во временный файл
69
+ output_filename = "mandre_training_data.json"
70
+ with open(output_filename, 'w', encoding='utf-8') as f:
71
+ json.dump(dataset, f, indent=4, ensure_ascii=False)
72
+
73
+ info = f"Сгенерировано {len(dataset)} примеров обучения. Скачайте файл и используйте его для обучения QLoRA (например, в Colab)."
74
+ return output_filename, info
75
+
76
+ # ==========================================
77
+ # ЧАСТЬ 2: ЧАТ С QLORA (Инференс)
78
+ # ==========================================
79
+
80
+ def load_model():
81
+ global model, tokenizer
82
+ if model is not None:
83
+ return "Модель уже загружена."
84
+
85
+ try:
86
+ status = "Загрузка токенизатора..."
87
+ tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL_ID)
88
+
89
+ status = "Загрузка модели (это может занять время на CPU)..."
90
+
91
+ # Конфиг для экономии памяти (если есть GPU, иначе float32 для CPU)
92
+ if torch.cuda.is_available():
93
+ bnb_config = BitsAndBytesConfig(
94
+ load_in_4bit=True,
95
+ bnb_4bit_quant_type="nf4",
96
+ bnb_4bit_compute_dtype=torch.float16
97
+ )
98
+ device_map = "auto"
99
+ else:
100
+ # CPU Config - очень медленно для 7B
101
+ bnb_config = None
102
+ device_map = "cpu"
103
+
104
+ model = AutoModelForCausalLM.from_pretrained(
105
+ BASE_MODEL_ID,
106
+ quantization_config=bnb_config,
107
+ device_map=device_map,
108
+ torch_dtype=torch.float32 if not torch.cuda.is_available() else torch.float16
109
+ )
110
+
111
+ # Пытаемся загрузить адаптер, если он существует
112
+ if os.path.exists(ADAPTER_PATH):
113
+ status = "Подключение MandreAI QLoRA адаптера..."
114
+ model = PeftModel.from_pretrained(model, ADAPTER_PATH)
115
+ return f"Модель {BASE_MODEL_ID} + Adapter загружены успешно!"
116
+
117
+ return f"Базовая модель {BASE_MODEL_ID} загружена (Адаптер не найден, используйте вкладку 1 для генерации данных)."
118
+
119
+ except Exception as e:
120
+ return f"Ошибка загрузки: {str(e)}"
121
+
122
+ def chat_response(message, history, attached_file):
123
+ if model is None:
124
+ load_model()
125
+
126
+ # 1. Обработка файла
127
+ file_context = ""
128
+ if attached_file is not None:
129
+ content = read_file_content(attached_file)
130
+ if content:
131
+ file_context = f"\n\n--- ATTACHED FILE: {os.path.basename(attached_file.name)} ---\n{content}\n--- END FILE ---\n"
132
+ else:
133
+ file_context = "\n[Ошибка чтения файла: бинарный или некорректная кодировка]\n"
134
+
135
+ # 2. Формирование промпта
136
+ # Системный промпт для MandreAI
137
+ system_prompt = "You are MandreAI, an expert coding assistant specializing in ExteraGram plugins, MandreLib, Chaquopy, and Aliuhook. Use the provided context to answer questions."
138
+
139
+ full_prompt = f"{system_prompt}\n\nUser: {message}{file_context}\n\nMandreAI:"
140
+
141
+ # 3. Токенизация
142
+ inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
143
+
144
+ # 4. Генерация
145
+ # Streamer можно добавить для красоты, но на CPU это будет рвано
146
+ outputs = model.generate(
147
+ **inputs,
148
+ max_new_tokens=512,
149
+ do_sample=True,
150
+ temperature=0.7,
151
+ top_p=0.9,
152
+ )
153
+
154
+ response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
155
+
156
+ # Очистка от промпта (простая эвристика)
157
+ if "MandreAI:" in response_text:
158
+ response_text = response_text.split("MandreAI:")[-1].strip()
159
+
160
+ return response_text
161
+
162
+ # ==========================================
163
+ # ИНТЕРФЕЙС GRADIO
164
+ # ==========================================
165
+
166
+ custom_css = """
167
+ #header {text-align: center; margin-bottom: 20px;}
168
+ """
169
+
170
+ with gr.Blocks(css=custom_css, title="MandreAI Space") as demo:
171
+ gr.Markdown("# 🦎 MandreAI: ExteraGram Plugin Assistant", elem_id="header")
172
+ gr.Markdown("Инструмент для создания датасетов и общения с моделью, обученной на MandreLib/ExteraGram.")
173
+
174
+ with gr.Tabs():
175
+ # --- ВКЛАДКА 1: DATA PREP ---
176
+ with gr.Tab("🛠️ Создание датасета (Qlora)"):
177
+ gr.Markdown("### Шаг 1: Загрузите исходники плагинов (.py, .java, .md)")
178
+ gr.Markdown("Скрипт сконвертирует файлы в JSON-формат, пригодный для обучения Gemma-7b.")
179
+
180
+ file_input = gr.File(label="Загрузить файлы плагинов", file_count="multiple")
181
+ generate_btn = gr.Button("Генерировать JSON", variant="primary")
182
+
183
+ output_json = gr.File(label="Скачать готовый датасет")
184
+ status_text = gr.Textbox(label="Статус", interactive=False)
185
+
186
+ generate_btn.click(
187
+ generate_dataset,
188
+ inputs=[file_input],
189
+ outputs=[output_json, status_text]
190
+ )
191
+
192
+ # --- ВКЛАДКА 2: CHAT ---
193
+ with gr.Tab("💬 Чат с MandreAI"):
194
+ gr.Markdown(f"**Текущая модель:** {BASE_MODEL_ID} (CPU Mode)")
195
+ gr.Markdown("*Примечание: На CPU генерация будет медленной. Для реальной работы клонируйте Space на GPU.*")
196
+
197
+ load_status = gr.Textbox(label="Статус системы", value="Нажмите отправить сообщение для инициализации...")
198
+
199
+ chatbot = gr.Chatbot(height=400)
200
+ with gr.Row():
201
+ msg_input = gr.Textbox(scale=4, placeholder="Как создать плагин на MandreLib?", label="Ваш вопрос")
202
+ attach_input = gr.File(scale=1, label="Прикрепить код/лог")
203
+
204
+ submit_btn = gr.Button("Отправить")
205
+ clear_btn = gr.Button("Очистить")
206
+
207
+ def user(user_message, history):
208
+ return "", history + [[user_message, None]]
209
+
210
+ def bot(history, attached_file):
211
+ user_message = history[-1][0]
212
+ bot_message = chat_response(user_message, history[:-1], attached_file)
213
+ history[-1][1] = bot_message
214
+ return history
215
+
216
+ # Цепочка событий
217
+ msg_input.submit(user, [msg_input, chatbot], [msg_input, chatbot], queue=False).then(
218
+ bot, [chatbot, attach_input], chatbot
219
+ )
220
+ submit_btn.click(user, [msg_input, chatbot], [msg_input, chatbot], queue=False).then(
221
+ bot, [chatbot, attach_input], chatbot
222
+ )
223
+
224
+ clear_btn.click(lambda: None, None, chatbot, queue=False)
225
+
226
+ if __name__ == "__main__":
227
+ demo.queue().launch()