Kenan023214 commited on
Commit
609f137
·
verified ·
1 Parent(s): 09382f1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -0
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ import os
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer
5
+ from huggingface_hub import hf_hub_download
6
+ from functools import lru_cache
7
+
8
+ # --- Конфигурация Hugging Face Space ---
9
+ # Загрузка модели и токенизатора один раз при запуске приложения
10
+ MODEL_NAME = "Kenan023214/PyroNet-mini"
11
+ DEVICE = "cpu" # Используем CPU, как указано для Basic Space
12
+ MAX_NEW_TOKENS = 256
13
+ MAX_CONTEXT_TOKENS = 2048
14
+
15
+ # Загрузка модели и токенизатора
16
+ @lru_cache(maxsize=1)
17
+ def load_model():
18
+ """Загружает модель и токенайзер, кешируя их для производительности."""
19
+ print("Loading model and tokenizer...")
20
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
21
+ model = AutoModelForCausalLM.from_pretrained(
22
+ MODEL_NAME,
23
+ device_map=DEVICE,
24
+ torch_dtype=torch.float32 # Используем float32 для совместимости с CPU
25
+ )
26
+ print("Model loaded.")
27
+ return tokenizer, model
28
+
29
+ # Загрузка файлов шаблонов из репозитория
30
+ @lru_cache(maxsize=1)
31
+ def download_templates():
32
+ """Скачивает файлы шаблонов из репозитория модели."""
33
+ print("Downloading chat templates...")
34
+ for lang in ["ru", "en", "uk"]:
35
+ hf_hub_download(
36
+ repo_id=MODEL_NAME,
37
+ filename=f"chat_template_{lang}.jinja",
38
+ local_dir=".",
39
+ local_dir_use_symlinks=False
40
+ )
41
+ print("Templates downloaded.")
42
+
43
+ tokenizer, model = load_model()
44
+ download_templates()
45
+
46
+ # --- Утилиты ---
47
+ def num_tokens_of_text(text: str) -> int:
48
+ """Приближённое количество токенов."""
49
+ return len(tokenizer.encode(text, add_special_tokens=False))
50
+
51
+ def trim_history_to_max_tokens(messages, max_tokens):
52
+ """Обрезает историю сообщений."""
53
+ rev = list(reversed(messages))
54
+ total = 0
55
+ kept = []
56
+ for m in rev:
57
+ approx = num_tokens_of_text(m["content"]) + 8
58
+ if total + approx > max_tokens:
59
+ break
60
+ kept.append(m)
61
+ total += approx
62
+ return list(reversed(kept))
63
+
64
+ def build_messages_for_template(history_messages, reasoning: bool, language: str):
65
+ """Подготавливает сообщения для шаблона."""
66
+ if language == 'ru':
67
+ system_message = "Ты — дружелюбный ассистент, который говорит на русском. Отвечай кратко, но по делу."
68
+ reasoning_instruction = ("[REASONING MODE]\n"
69
+ "Когда отвечаешь, сначала представь краткие пронумерованные шаги рассуждения. "
70
+ "Затем на новой строке напиши 'Final:' и дай короткий окончательный ответ. Сохраняй шаги лаконичными.")
71
+ elif language == 'uk':
72
+ system_message = "Ти — дружній асистент, який говорить українською. Відповідай коротко, але по суті."
73
+ reasoning_instruction = ("[REASONING MODE]\n"
74
+ "Коли відповідаєш, спершу представ короткі пронумеровані кроки розмірковування. "
75
+ "Потім на новому рядку напиши 'Final:' і дай коротку остаточну відповідь. Зберігай кроки лаконічними.")
76
+ else: # 'en'
77
+ system_message = "You are a friendly assistant who speaks English. Answer concisely but to the point."
78
+ reasoning_instruction = ("[REASONING MODE]\n"
79
+ "When answering, first present concise numbered reasoning steps. "
80
+ "Then on a new line write 'Final:' and give a short final answer. Keep steps brief.")
81
+
82
+ messages = [{"role": "system", "content": system_message}] + list(history_messages)
83
+
84
+ if reasoning:
85
+ messages.append({"role": "user", "content": reasoning_instruction})
86
+
87
+ return messages
88
+
89
+ def extract_assistant_reply(raw_generated_text: str) -> str:
90
+ """Убирает лишние токены и оставляет только ответ ассистента."""
91
+ text = raw_generated_text
92
+ if "<|assistant|>" in text:
93
+ text = text.split("<|assistant|>")[-1]
94
+ for tag in ["<|end|>", "<|end_of_text|>", "<|end|>"]:
95
+ text = text.replace(tag, "")
96
+ return text.strip()
97
+
98
+ # --- Основная функция для Gradio ---
99
+ def generate_response(user_text: str, history, reasoning: bool, language: str):
100
+ """Обрабатывает пользовательский запрос и генерирует ответ."""
101
+
102
+ # Добавляем user-сообщение в историю
103
+ history.append({"role": "user", "content": user_text})
104
+
105
+ # Подрезаем историю, чтобы вход не стал слишком большим
106
+ trimmed_history = trim_history_to_max_tokens(history, MAX_CONTEXT_TOKENS)
107
+
108
+ # Собираем messages с возможной инструкцией reasoning
109
+ messages_for_template = build_messages_for_template(trimmed_history, reasoning, language)
110
+
111
+ # Выбираем шаблон из локальных файлов
112
+ template_file = f"chat_template_{language}.jinja"
113
+
114
+ # Применяем шаблон и токенизируем
115
+ text = tokenizer.apply_chat_template(
116
+ messages_for_template,
117
+ template_path=template_file,
118
+ tokenize=False,
119
+ add_generation_prompt=True
120
+ )
121
+
122
+ inputs = tokenizer(text, return_tensors="pt").to(DEVICE)
123
+
124
+ # Генерация
125
+ with torch.no_grad():
126
+ outputs = model.generate(
127
+ **inputs,
128
+ max_new_tokens=MAX_NEW_TOKENS,
129
+ do_sample=True,
130
+ top_p=0.9,
131
+ temperature=0.8,
132
+ pad_token_id=tokenizer.eos_token_id
133
+ )
134
+
135
+ # Декодируем и очищаем ответ
136
+ raw = tokenizer.decode(outputs[0], skip_special_tokens=False)
137
+ reply = extract_assistant_reply(raw)
138
+
139
+ # Добавляем ассистента в историю
140
+ history.append({"role": "assistant", "content": reply})
141
+
142
+ # Gradio ожидает возвращение списка [пользователь, ассистент]
143
+ # Мы возвращаем всю историю для корректного отображения
144
+ return "", history
145
+
146
+ # --- Интерфейс Gradio ---
147
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
148
+ gr.Markdown("# PyroNet-mini Chat")
149
+ gr.Markdown("Демонстрация работы PyroNet-mini (на базе Phi-4-mini-instruct) с кастомными шаблонами и режимом рассуждения.")
150
+
151
+ chatbot = gr.Chatbot(height=500)
152
+
153
+ with gr.Row():
154
+ with gr.Column(scale=4):
155
+ msg = gr.Textbox(
156
+ label="Ваш запрос",
157
+ placeholder="Напишите здесь...",
158
+ container=False
159
+ )
160
+ with gr.Column(scale=1, min_width=100):
161
+ language_dropdown = gr.Dropdown(
162
+ choices=["ru", "en", "uk"],
163
+ value="ru",
164
+ label="Язык",
165
+ container=False
166
+ )
167
+ reasoning_checkbox = gr.Checkbox(
168
+ label="Включить режим рассуждения"
169
+ )
170
+
171
+ btn_send = gr.Button("Отправить")
172
+ btn_clear = gr.Button("Очистить")
173
+
174
+ # Обработчики событий
175
+ def reset_history():
176
+ return [], None
177
+
178
+ btn_send.click(
179
+ fn=generate_response,
180
+ inputs=[msg, chatbot, reasoning_checkbox, language_dropdown],
181
+ outputs=[msg, chatbot]
182
+ )
183
+ msg.submit(
184
+ fn=generate_response,
185
+ inputs=[msg, chatbot, reasoning_checkbox, language_dropdown],
186
+ outputs=[msg, chatbot]
187
+ )
188
+ btn_clear.click(
189
+ fn=lambda: ([], None),
190
+ inputs=[],
191
+ outputs=[chatbot, msg]
192
+ )
193
+
194
+ if __name__ == "__main__":
195
+ demo.launch()
196
+