Kenan023214 commited on
Commit
75c77e2
·
verified ·
1 Parent(s): 104dcf6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -38
app.py CHANGED
@@ -4,13 +4,77 @@ from transformers import AutoModelForCausalLM, AutoTokenizer
4
  from functools import lru_cache
5
 
6
  # --- Конфигурация Hugging Face Space ---
7
- # Загрузка модели и токенизатора один раз при запуске приложения
8
  MODEL_NAME = "Kenan023214/PyroNet-mini"
9
  DEVICE = "cpu" # Используем CPU, как указано для Basic Space
10
- MAX_NEW_TOKENS = 256
11
  MAX_CONTEXT_TOKENS = 2048
12
 
13
- # Загрузка модели и токенизатора
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  @lru_cache(maxsize=1)
15
  def load_model():
16
  """Загружает модель и токенайзер, кешируя их для производительности."""
@@ -19,7 +83,7 @@ def load_model():
19
  model = AutoModelForCausalLM.from_pretrained(
20
  MODEL_NAME,
21
  device_map=DEVICE,
22
- torch_dtype=torch.float32 # Используем float32 для совместимости с CPU
23
  )
24
  print("Model loaded.")
25
  return tokenizer, model
@@ -28,7 +92,7 @@ tokenizer, model = load_model()
28
 
29
  # --- Утилиты ---
30
  def num_tokens_of_text(text: str) -> int:
31
- """Приближённое количество токенов для заданного текста."""
32
  return len(tokenizer.encode(text, add_special_tokens=False))
33
 
34
  def trim_history_to_max_tokens(messages, max_tokens):
@@ -46,37 +110,39 @@ def trim_history_to_max_tokens(messages, max_tokens):
46
 
47
  def build_messages_for_template(history_messages, reasoning: bool, language: str):
48
  """Подготавливает сообщения для шаблона, включая системное сообщение."""
49
- if language == 'ru':
50
- system_message = "Ты — дружелюбный ассистент, который говорит на русском. Отвечай кратко, но по делу."
51
- reasoning_instruction = ("[REASONING MODE]\n"
52
- "Когда отвечаешь, сначала представь краткие пронумерованные шаги рассуждения. "
53
- "Затем на новой строке напиши 'Final:' и дай короткий окончательный ответ. Сохраняй шаги лаконичными.")
54
- elif language == 'uk':
55
- system_message = "Ти — дружній асистент, який говорить українською. Відповідай коротко, але по суті."
56
- reasoning_instruction = ("[REASONING MODE]\n"
57
- "Коли відповідаєш, спершу представ короткі пронумеровані кроки розмірковування. "
58
- "Потім на новому рядку напиши 'Final:' і дай коротку остаточну відповідь. Зберігай кроки лаконічними.")
59
- else: # 'en'
60
- system_message = "You are a friendly assistant who speaks English. Answer concisely but to the point."
61
- reasoning_instruction = ("[REASONING MODE]\n"
62
- "When answering, first present concise numbered reasoning steps. "
63
- "Then on a new line write 'Final:' and give a short final answer. Keep steps brief.")
64
-
65
- messages = [{"role": "system", "content": system_message}] + list(history_messages)
66
 
67
  if reasoning:
68
- messages.append({"role": "user", "content": reasoning_instruction})
69
 
70
  return messages
71
 
72
- def extract_assistant_reply(raw_generated_text: str) -> str:
73
- """Убирает лишние токены и возвращает только ответ ассистента."""
74
  text = raw_generated_text
75
  if "<|assistant|>" in text:
76
  text = text.split("<|assistant|>")[-1]
 
77
  for tag in ["<|end|>", "<|end_of_text|>", "<|end|>"]:
78
  text = text.replace(tag, "")
79
- return text.strip()
 
 
 
 
 
 
 
 
 
80
 
81
  # --- Основная функция для Gradio ---
82
  def generate_response(user_text: str, history, reasoning: bool, language: str):
@@ -88,12 +154,12 @@ def generate_response(user_text: str, history, reasoning: bool, language: str):
88
 
89
  messages_for_template = build_messages_for_template(trimmed_history, reasoning, language)
90
 
91
- # Выбираем шаблон из файлов в репозитории
92
- template_file = f"chat_template_{language}.jinja"
93
 
94
  text = tokenizer.apply_chat_template(
95
  messages_for_template,
96
- template_path=template_file,
97
  tokenize=False,
98
  add_generation_prompt=True
99
  )
@@ -111,19 +177,29 @@ def generate_response(user_text: str, history, reasoning: bool, language: str):
111
  )
112
 
113
  raw = tokenizer.decode(outputs[0], skip_special_tokens=False)
114
- reply = extract_assistant_reply(raw)
115
 
116
  history.append({"role": "assistant", "content": reply})
117
 
118
- return "", history
119
 
120
  # --- Интерфейс Gradio ---
121
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
122
  gr.Markdown("# PyroNet-mini Chat")
123
  gr.Markdown("A demonstration of PyroNet-mini with multilingual templates and a reasoning mode.")
124
 
125
- chatbot = gr.Chatbot(height=500)
126
-
 
 
 
 
 
 
 
 
 
 
127
  with gr.Row():
128
  with gr.Column(scale=4):
129
  msg = gr.Textbox(
@@ -148,18 +224,18 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
148
  btn_send.click(
149
  fn=generate_response,
150
  inputs=[msg, chatbot, reasoning_checkbox, language_dropdown],
151
- outputs=[msg, chatbot]
152
  )
153
  msg.submit(
154
  fn=generate_response,
155
  inputs=[msg, chatbot, reasoning_checkbox, language_dropdown],
156
- outputs=[msg, chatbot]
157
  )
158
  btn_clear.click(
159
- fn=lambda: ([], None),
160
  inputs=[],
161
- outputs=[chatbot, msg]
162
  )
163
 
164
  if __name__ == "__main__":
165
- demo.launch()
 
4
  from functools import lru_cache
5
 
6
  # --- Конфигурация Hugging Face Space ---
 
7
  MODEL_NAME = "Kenan023214/PyroNet-mini"
8
  DEVICE = "cpu" # Используем CPU, как указано для Basic Space
9
+ MAX_NEW_TOKENS = 512 # Увеличим для "хода мыслей"
10
  MAX_CONTEXT_TOKENS = 2048
11
 
12
+ # Словарь с встроенным содержимым шаблонов чата
13
+ CHAT_TEMPLATES = {
14
+ "ru": """<|system|>
15
+ Ты — PyroNet-mini, облегчённая и свободная версия PyroNet, созданная Артёмом (IceL1ghtning).
16
+ - Эксперт в физике, математике, программировании, биологии и смежных областях.
17
+ - Дружелюбна, энергична, слегка иронична.
18
+ - Отвечай на языке пользователя (русский).
19
+ - В режиме reasoning показывай шаги рассуждений → затем итог; в обычном режиме — будь краткой.
20
+ - Предпочитай списки и нумерацию, код выделяй в ```код``` с тэгом языка, математика = формула + результат.
21
+ - Отказывай в явно опасных/незаконных запросах, предлагай альтернативы.
22
+ <|end|>
23
+
24
+ {% for m in messages %}
25
+ {% if m['role'] == 'user' %}
26
+ <|user|>{{ m['content'] }}<|end|>
27
+ {% elif m['role'] == 'assistant' %}
28
+ <|assistant|>{{ m['content'] }}<|end|>
29
+ {% endif %}
30
+ {% endfor %}
31
+
32
+ {% if add_generation_prompt %}<|assistant|>{% endif %}""",
33
+
34
+ "en": """<|system|>
35
+ You are **PyroNet-mini**, a lighter and freer version of PyroNet, created by Artyom (IceL1ghtning) in Ukraine.
36
+ - You are knowledgeable in physics, mathematics, programming, biology, and adjacent domains.
37
+ - Energetic, friendly, slightly ironic.
38
+ - Mirror the user's language (English).
39
+ - In reasoning mode: show concise step-by-step reasoning → then final answer; otherwise be concise.
40
+ - Prefer bullet points and numbered steps, code in ```code``` with correct language tags, math = formula + numeric result.
41
+ - Refuse unsafe/illegal requests, suggest safe alternatives.
42
+ <|end|>
43
+
44
+ {% for m in messages %}
45
+ {% if m['role'] == 'user' %}
46
+ <|user|>{{ m['content'] }}<|end|>
47
+ {% elif m['role'] == 'assistant' %}
48
+ <|assistant|>{{ m['content'] }}<|end|>
49
+ {% endif %}
50
+ {% endfor %}
51
+
52
+ {% if add_generation_prompt %}<|assistant|>{% endif %}""",
53
+
54
+ "uk": """<|system|>
55
+ Ти — **PyroNet-mini**, полегшена й більш вільна версія PyroNet, створена Артемом (IceL1ghtning) в Україні.
56
+ - Експерт у фізиці, математиці, програмуванні, біології та суміжних темах.
57
+ - Енергійна, дружня, злегка іронічна.
58
+ - Відповідай на мові користувача (українська).
59
+ - У режимі reasoning показуй лаконічні кроки → потім висновок; в іншому будь короткою.
60
+ - Віддавай перевагу спискам, код у ```код``` з тегом мови, математика = формула + результат.
61
+ - Відмовляй у небезпечних/незаконних запитах, пропонуй альтернативи.
62
+ <|end|>
63
+
64
+ {% for m in messages %}
65
+ {% if m['role'] == 'user' %}
66
+ <|user|>{{ m['content'] }}<|end|>
67
+ {% elif m['role'] == 'assistant' %}
68
+ <|assistant|>{{ m['content'] }}<|end|>
69
+ {% endif %}
70
+ {% endfor %}
71
+
72
+ {% if add_generation_prompt %}<|assistant|>{% endif %}"""
73
+ }
74
+
75
+ # Ключевая фраза для разделения ответа модели
76
+ REASONING_SEPARATOR = "Final:"
77
+
78
  @lru_cache(maxsize=1)
79
  def load_model():
80
  """Загружает модель и токенайзер, кешируя их для производительности."""
 
83
  model = AutoModelForCausalLM.from_pretrained(
84
  MODEL_NAME,
85
  device_map=DEVICE,
86
+ torch_dtype=torch.float32
87
  )
88
  print("Model loaded.")
89
  return tokenizer, model
 
92
 
93
  # --- Утилиты ---
94
  def num_tokens_of_text(text: str) -> int:
95
+ """Приблизительное количество токенов для заданного текста."""
96
  return len(tokenizer.encode(text, add_special_tokens=False))
97
 
98
  def trim_history_to_max_tokens(messages, max_tokens):
 
110
 
111
  def build_messages_for_template(history_messages, reasoning: bool, language: str):
112
  """Подготавливает сообщения для шаблона, включая системное сообщение."""
113
+ # Используем полный системный промпт из словаря
114
+ full_template_content = CHAT_TEMPLATES.get(language, CHAT_TEMPLATES["en"])
115
+
116
+ # Извлекаем системное сообщение из шаблона
117
+ system_start_tag = "<|system|>"
118
+ system_end_tag = "<|end|>"
119
+ system_message_raw = full_template_content.split(system_start_tag)[1].split(system_end_tag)[0].strip()
120
+
121
+ messages = [{"role": "system", "content": system_message_raw}] + list(history_messages)
 
 
 
 
 
 
 
 
122
 
123
  if reasoning:
124
+ messages.append({"role": "user", "content": f"Режим рассуждения: покажи свои шаги, а затем окончательный ответ, начиная с '{REASONING_SEPARATOR}'"})
125
 
126
  return messages
127
 
128
+ def extract_assistant_reply_and_reasoning(raw_generated_text: str) -> tuple[str, str]:
129
+ """Убирает лишние токены и разделяет ответ на ход мыслей и окончательный ответ."""
130
  text = raw_generated_text
131
  if "<|assistant|>" in text:
132
  text = text.split("<|assistant|>")[-1]
133
+
134
  for tag in ["<|end|>", "<|end_of_text|>", "<|end|>"]:
135
  text = text.replace(tag, "")
136
+
137
+ text = text.strip()
138
+
139
+ if REASONING_SEPARATOR in text:
140
+ parts = text.split(REASONING_SEPARATOR, 1)
141
+ reasoning = parts[0].strip()
142
+ reply = parts[1].strip()
143
+ return reply, reasoning
144
+ else:
145
+ return text, "" # Если разделитель не найден, возвращаем все как ответ
146
 
147
  # --- Основная функция для Gradio ---
148
  def generate_response(user_text: str, history, reasoning: bool, language: str):
 
154
 
155
  messages_for_template = build_messages_for_template(trimmed_history, reasoning, language)
156
 
157
+ # Используем содержимое шаблона из словаря, а не путь к файлу
158
+ template_content = CHAT_TEMPLATES.get(language, CHAT_TEMPLATES["en"])
159
 
160
  text = tokenizer.apply_chat_template(
161
  messages_for_template,
162
+ chat_template=template_content, # Передаем содержимое шаблона напрямую
163
  tokenize=False,
164
  add_generation_prompt=True
165
  )
 
177
  )
178
 
179
  raw = tokenizer.decode(outputs[0], skip_special_tokens=False)
180
+ reply, reasoning_text = extract_assistant_reply_and_reasoning(raw)
181
 
182
  history.append({"role": "assistant", "content": reply})
183
 
184
+ return "", history, reasoning_text
185
 
186
  # --- Интерфейс Gradio ---
187
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
188
  gr.Markdown("# PyroNet-mini Chat")
189
  gr.Markdown("A demonstration of PyroNet-mini with multilingual templates and a reasoning mode.")
190
 
191
+ with gr.Tabs():
192
+ with gr.TabItem("Chat"):
193
+ chatbot = gr.Chatbot(height=500)
194
+ with gr.TabItem("Reasoning"):
195
+ reasoning_box = gr.Textbox(
196
+ label="Reasoning Steps",
197
+ interactive=False,
198
+ lines=20,
199
+ placeholder="The model's thought process will appear here when Reasoning Mode is enabled.",
200
+ show_copy_button=True
201
+ )
202
+
203
  with gr.Row():
204
  with gr.Column(scale=4):
205
  msg = gr.Textbox(
 
224
  btn_send.click(
225
  fn=generate_response,
226
  inputs=[msg, chatbot, reasoning_checkbox, language_dropdown],
227
+ outputs=[msg, chatbot, reasoning_box]
228
  )
229
  msg.submit(
230
  fn=generate_response,
231
  inputs=[msg, chatbot, reasoning_checkbox, language_dropdown],
232
+ outputs=[msg, chatbot, reasoning_box]
233
  )
234
  btn_clear.click(
235
+ fn=lambda: ([], "", ""),
236
  inputs=[],
237
+ outputs=[chatbot, msg, reasoning_box]
238
  )
239
 
240
  if __name__ == "__main__":
241
+ demo.launch()