FrostIce commited on
Commit
5efbcc2
·
verified ·
1 Parent(s): 7fe6dd7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -45
app.py CHANGED
@@ -1,14 +1,50 @@
1
- # app.py
2
- import gradio as gr
3
- from g4f.client import Client
4
-
5
- client = Client()
 
 
 
6
 
7
- # --- Обработчик диалога ---
8
- def respond(message, history):
9
- # Добавляем системное сообщение только при старте истории
10
- messages = [{"role": "system", "content": """
11
- You are a Web‑assistant. For every user request return **exactly one JSON object**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  with the following possible fields:
13
  {
14
  "TEXT": "<optional short explanation>",
@@ -25,72 +61,128 @@ with the following possible fields:
25
  }
26
  }
27
  If you don't need any action, set all fields to null or empty strings.
28
-
29
- """}]
30
-
31
- for human, assistant in history:
32
- messages.append({"role": "user", "content": human})
33
- messages.append({"role": "assistant", "content": assistant})
34
-
35
- messages.append({"role": "user", "content": message})
36
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  try:
38
- response = client.chat.completions.create(
39
- model="HuggingFaceH4/zephyr-7b-beta",
40
- messages=messages
 
 
 
 
 
 
 
 
41
  )
42
- bot_message = response.choices[0].message.content
43
- except Exception as e:
44
- bot_message = f"Ошибка: {str(e)}"
45
-
46
- # Возвращаем обновлённую историю + новый ответ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  return history + [[message, bot_message]]
48
 
49
- # --- Интерфейс ---
50
- with gr.Blocks(title="ESP Brain") as demo:
51
- gr.Markdown("## For api")
52
 
53
- chatbot = gr.Chatbot(
54
- height=600,
55
- )
 
 
 
 
56
 
57
  with gr.Row():
58
  txt = gr.Textbox(
59
- placeholder="Напиши сообщение...",
60
  show_label=False,
61
- scale=8
62
  )
63
  submit_btn = gr.Button("Отправить", scale=2)
64
 
65
  with gr.Row():
66
- retry_btn = gr.Button("🔄 Повторить")
67
- undo_btn = gr.Button("↩️ Отменить")
68
- clear_btn = gr.Button("🗑️ Очистить")
69
 
70
- # Логика
 
 
71
  txt.submit(fn=respond, inputs=[txt, chatbot], outputs=chatbot)
72
  submit_btn.click(fn=respond, inputs=[txt, chatbot], outputs=chatbot)
73
 
74
  def retry_last(history):
 
75
  if history:
76
- last_user_msg = history[-1][0]
77
- return history[:-1] + [[last_user_msg, None]] # очищаем ответ
78
  return history
79
 
80
  retry_btn.click(fn=retry_last, inputs=chatbot, outputs=chatbot, queue=False)
81
 
82
  def undo_last(history):
 
83
  return history[:-1]
84
 
85
  undo_btn.click(fn=undo_last, inputs=chatbot, outputs=chatbot, queue=False)
86
 
87
  clear_btn.click(lambda: [], outputs=chatbot, queue=False)
88
 
89
- # --- Запуск ---
 
 
90
  if __name__ == "__main__":
 
91
  demo.queue()
92
  demo.launch(
93
- share=True,
94
  ssr_mode=False,
95
- debug=True
96
- )
 
1
+ # -------------------------------------------------------------
2
+ # app.py – Gradio‑чат с локальной LLaMA‑моделью
3
+ # -------------------------------------------------------------
4
+ import os
5
+ import json
6
+ import pathlib
7
+ import gc
8
+ from typing import List, Tuple, Any
9
 
10
+ import gradio as gr
11
+ from llama_cpp import Llama
12
+ from huggingface_hub import snapshot_download
13
+
14
+ # ------------------------------------------------------------------
15
+ # 1️⃣ Загрузка модели из HuggingFace (необходимо один раз)
16
+ # ------------------------------------------------------------------
17
+ MODEL_REPO = "neuphonic/neutts-air"
18
+ MODEL_FILE = "neutss-air-BF16.gguf"
19
+ CACHE_DIR = os.getenv("HF_HOME", pathlib.Path.home() / ".cache" / "huggingface" / "hub")
20
+
21
+ print("🔎 Скачиваем модель (может занять несколько минут)...")
22
+ model_path = snapshot_download(
23
+ repo_id=MODEL_REPO,
24
+ revision="main",
25
+ cache_dir=str(CACHE_DIR),
26
+ local_files_only=False, # False → скачивает, если нет локально
27
+ allow_patterns=[MODEL_FILE],
28
+ )
29
+
30
+ gguf_path = os.path.join(model_path, MODEL_FILE)
31
+ print(f"✅ Модель скачана в {gguf_path}")
32
+
33
+ # ------------------------------------------------------------------
34
+ # 2️⃣ Инициализация Llama‑CPP (GPU/CPU зависит от того, как установлен пакет)
35
+ # ------------------------------------------------------------------
36
+ llm = Llama(
37
+ model_path=gguf_path,
38
+ n_ctx=2048, # длина контекста (можно увеличить, если хватает VRAM)
39
+ n_threads=8, # количество CPU‑ядер
40
+ n_gpu_layers=-1, # -1 → попытаться использовать всё доступное GPU (если сборка поддерживает)
41
+ verbose=False,
42
+ )
43
+
44
+ # ------------------------------------------------------------------
45
+ # 3️⃣ Системный промпт – будет всегда первой репликой
46
+ # ------------------------------------------------------------------
47
+ SYSTEM_PROMPT = """You are a Web‑assistant. For every user request return **exactly one JSON object**
48
  with the following possible fields:
49
  {
50
  "TEXT": "<optional short explanation>",
 
61
  }
62
  }
63
  If you don't need any action, set all fields to null or empty strings.
64
+ """
65
+
66
+ # ------------------------------------------------------------------
67
+ # 4️⃣ Вспомогательная функция: формируем запрос в стиле OpenAI‑Chat
68
+ # ------------------------------------------------------------------
69
+ def build_chat(messages: List[Tuple[str, str]]) -> str:
70
+ """
71
+ Преобразуем историю (list of (human,assistant)) в один строковый prompt,
72
+ совместимый с Llama‑CPP, где каждая реплика отделяется тегами <|user|>,
73
+ <|assistant|> и <|system|>.
74
+ """
75
+ prompt = f"<|system|>{SYSTEM_PROMPT}<|end|>"
76
+ for human, assistant in messages:
77
+ prompt += f"<|user|>{human}<|end|>"
78
+ prompt += f"<|assistant|>{assistant}<|end|>"
79
+ return prompt
80
+
81
+
82
+ # ------------------------------------------------------------------
83
+ # 5️⃣ Основная бизнес‑логика – генерация ответа модели
84
+ # ------------------------------------------------------------------
85
+ def respond(message: str, history: List[List[str]]) -> List[List[Any]]:
86
+ """
87
+ Принимает новое сообщение пользователя и текущую историю чата.
88
+ Возвращает обновлённую историю, где второй элемент списка – JSON‑строка
89
+ модели (или сообщение об ошибке).
90
+ """
91
+ # 1️⃣ Преобразуем историю в формат (human,assistant)
92
+ chat_history = [(h, a) for h, a in history] # тип List[Tuple[str,str]]
93
+
94
+ # 2️⃣ Формируем полный prompt
95
+ prompt = build_chat(chat_history + [(message, "")]) # последняя реплика ещё пустая
96
+
97
+ # 3️⃣ Генерируем ответ (при необходимости задаём stop���строку)
98
  try:
99
+ # Параметры генерации можно подкорректировать:
100
+ # temperature – креативность,
101
+ # top_p – сэмплинг,
102
+ # max_tokens – длина ответа.
103
+ out = llm(
104
+ prompt,
105
+ max_tokens=512,
106
+ temperature=0.2,
107
+ top_p=0.95,
108
+ repeat_penalty=1.1,
109
+ stop=["<|assistant|>", "<|user|>", "<|system|>"], # остановка перед новым turn'ом
110
  )
111
+ raw = out["choices"][0]["text"].strip()
112
+
113
+ # Иногда модель генерирует лишний текст (например, объяснение) перед JSON.
114
+ # Попытаемся вырезать первый валидный JSON‑объект.
115
+ try:
116
+ # Находим первую фигурную скобку
117
+ start = raw.find("{")
118
+ json_part = raw[start:] if start != -1 else raw
119
+ parsed = json.loads(json_part)
120
+ except Exception:
121
+ # Если парсинг не удалось – считаем, что модель отдала обычный текст
122
+ parsed = {"TEXT": raw, "WEBSITE": "", "SEARCH": "", "SUGGESTIONS": [], "TOOL": {}}
123
+ except Exception as exc:
124
+ parsed = {"TEXT": f"Ошибка модели: {str(exc)}",
125
+ "WEBSITE": "", "SEARCH": "", "SUGGESTIONS": [], "TOOL": {}}
126
+
127
+ # Приводим к строке, чтобы отобразить в чат‑боте
128
+ bot_message = json.dumps(parsed, ensure_ascii=False, indent=2)
129
+
130
+ # 4️⃣ Возвращаем обновленную историю
131
  return history + [[message, bot_message]]
132
 
 
 
 
133
 
134
+ # ------------------------------------------------------------------
135
+ # 6️⃣ Gradio‑интерфейс (не менялся)
136
+ # ------------------------------------------------------------------
137
+ with gr.Blocks(title="ESP Brain – локальная LLaMA") as demo:
138
+ gr.Markdown("## 🤖 Web‑assistant powered by **neutts‑air** (LLaMA‑CPP)")
139
+
140
+ chatbot = gr.Chatbot(height=600)
141
 
142
  with gr.Row():
143
  txt = gr.Textbox(
144
+ placeholder="Напиши сообщение…",
145
  show_label=False,
146
+ scale=8,
147
  )
148
  submit_btn = gr.Button("Отправить", scale=2)
149
 
150
  with gr.Row():
151
+ retry_btn = gr.Button("🔄Повторить")
152
+ undo_btn = gr.Button("↩️Отменить")
153
+ clear_btn = gr.Button("🗑️Очистить")
154
 
155
+ # -------------------------------------------------------------
156
+ # Логика кнопок
157
+ # -------------------------------------------------------------
158
  txt.submit(fn=respond, inputs=[txt, chatbot], outputs=chatbot)
159
  submit_btn.click(fn=respond, inputs=[txt, chatbot], outputs=chatbot)
160
 
161
  def retry_last(history):
162
+ """Очистить последний ответ, чтобы пользователь мог написать заново."""
163
  if history:
164
+ last_user = history[-1][0]
165
+ return history[:-1] + [[last_user, None]]
166
  return history
167
 
168
  retry_btn.click(fn=retry_last, inputs=chatbot, outputs=chatbot, queue=False)
169
 
170
  def undo_last(history):
171
+ """Удалить последнюю пару (user‑assistant)."""
172
  return history[:-1]
173
 
174
  undo_btn.click(fn=undo_last, inputs=chatbot, outputs=chatbot, queue=False)
175
 
176
  clear_btn.click(lambda: [], outputs=chatbot, queue=False)
177
 
178
+ # ------------------------------------------------------------------
179
+ # 7️⃣ Запуск приложения
180
+ # ------------------------------------------------------------------
181
  if __name__ == "__main__":
182
+ # Включаем очередь, чтобы несколько запросов не конфликтовали с GPU/CPU
183
  demo.queue()
184
  demo.launch(
185
+ share=True, # получить публичный https‑линк (опционально)
186
  ssr_mode=False,
187
+ debug=True,
188
+ )