Mrdips commited on
Commit
981f87b
·
verified ·
1 Parent(s): 175f3fe

Deploy from HuggingChat

Browse files
Files changed (2) hide show
  1. README.md +9 -5
  2. index.html +418 -19
README.md CHANGED
@@ -1,10 +1,14 @@
1
  ---
2
- title: Gemini Api
3
- emoji: 📈
4
- colorFrom: yellow
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: "Gemini api"
3
+ emoji: ⚡️
4
+ colorFrom: purple
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - huggingchat
10
  ---
11
 
12
+ # Gemini api
13
+
14
+ Built with [HuggingChat](https://huggingface.co/chat).
index.html CHANGED
@@ -1,19 +1,418 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ru">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Ternary-Bonsai 27B · AWQ-4bit</title>
7
+ <style>
8
+ * { margin:0; padding:0; box-sizing:border-box; }
9
+ :root {
10
+ --bg:#0a0a0f; --bg2:#12121c; --bg3:#1a1a28;
11
+ --border:#2a2a3a; --accent:#7c3aed; --accent2:#a855f7;
12
+ --text:#e2e8f0; --text2:#94a3b8; --green:#22c55e;
13
+ }
14
+ body {
15
+ font-family:'Segoe UI',-apple-system,sans-serif;
16
+ background:var(--bg); color:var(--text); min-height:100vh;
17
+ display:flex; flex-direction:column;
18
+ }
19
+ header {
20
+ background:var(--bg2); border-bottom:1px solid var(--border);
21
+ padding:12px 24px; display:flex; align-items:center;
22
+ justify-content:space-between; position:sticky; top:0; z-index:10;
23
+ }
24
+ header h1 { font-size:1.35rem; font-weight:700; display:flex; align-items:center; gap:10px; }
25
+ header h1 span {
26
+ background:linear-gradient(135deg,var(--accent),var(--accent2));
27
+ -webkit-background-clip:text; -webkit-text-fill-color:transparent; font-size:1.5rem;
28
+ }
29
+ .header-right { display:flex; gap:12px; align-items:center; }
30
+ .status-badge {
31
+ display:flex; align-items:center; gap:6px;
32
+ background:var(--bg3); padding:6px 14px; border-radius:20px;
33
+ font-size:0.82rem; color:var(--text2); border:1px solid var(--border);
34
+ }
35
+ .status-dot { width:8px; height:8px; border-radius:50%; background:var(--red); transition:background 0.3s; }
36
+ .status-dot.connected { background:var(--green); animation:pulse 2s infinite; }
37
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
38
+ .connect-btn {
39
+ background:var(--accent); color:#fff; border:none;
40
+ padding:8px 18px; border-radius:8px; cursor:pointer;
41
+ font-size:0.85rem; font-weight:600; transition:opacity 0.2s;
42
+ white-space:nowrap;
43
+ }
44
+ .connect-btn:hover { opacity:0.85; }
45
+ .connect-btn:disabled { opacity:0.4; cursor:not-allowed; }
46
+ .main { display:flex; flex:1; }
47
+ .settings-panel {
48
+ width:300px; background:var(--bg2); border-right:1px solid var(--border);
49
+ padding:20px; overflow-y:auto; flex-shrink:0;
50
+ }
51
+ .panel-section { margin-bottom:22px; }
52
+ .panel-section h3 { font-size:0.8rem; text-transform:uppercase; letter-spacing:1px; color:var(--text2); margin-bottom:12px; }
53
+ .label { font-size:0.85rem; color:var(--text2); margin-bottom:5px; font-weight:500; }
54
+ input, select, textarea {
55
+ font-family:inherit; font-size:0.88rem; color:var(--text);
56
+ background:var(--bg3); border:1px solid var(--border); border-radius:8px;
57
+ padding:8px 12px;
58
+ }
59
+ textarea { resize:vertical; min-height:80px; line-height:1.5; padding:10px 12px; }
60
+ textarea:focus, input:focus, select:focus { outline:none; border-color:var(--accent); }
61
+ select { cursor:pointer; }
62
+ .mode-toggle { display:flex; gap:0; background:var(--bg3); border-radius:8px; border:1px solid var(--border); overflow:hidden; }
63
+ .mode-toggle button {
64
+ flex:1; padding:8px 12px; border:none; background:transparent;
65
+ color:var(--text2); font-size:0.82rem; cursor:pointer; transition:all 0.2s;
66
+ }
67
+ .mode-toggle button.active { background:var(--accent); color:#fff; }
68
+ .chat-area { flex:1; display:flex; flex-direction:column; min-width:0; }
69
+ .messages { flex:1; overflow-y:auto; padding:20px; display:flex; flex-direction:column; gap:8px; }
70
+ .message { max-width:75%; padding:10px 16px; border-radius:14px; line-height:1.6; word-wrap:break-word; position:relative; }
71
+ .message.user { align-self:flex-end; background:#1e1b4b; border:1px solid #312e81; border-bottom-right-radius:4px; }
72
+ .message.ai { align-self:flex-start; background:#0f172a; border:1px solid #1e293b; border-bottom-left-radius:4px; }
73
+ .message .author { font-size:0.72rem; opacity:0.6; margin-bottom:4px; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; }
74
+ .message .content { font-size:0.92rem; }
75
+ .message .content code { display:inline-block; background:var(--bg3); padding:1px 5px; border-radius:4px; font-size:0.85rem; color:var(--green); font-family:'Consolas',monospace; }
76
+ .message .content pre { background:var(--bg3); border-radius:8px; padding:14px 18px; margin:10px 0; overflow-x:auto; font-size:0.82rem; line-height:1.7; color:var(--text); }
77
+ .message .content pre code { padding:0; display:block; }
78
+ .typing-indicator { padding:8px 16px; align-self:flex-start; font-size:0.85rem; color:var(--text2); }
79
+ .typing-dots span { display:inline-block; width:6px; height:6px; border-radius:50%; background:var(--text2); margin-right:4px; animation:typeBounce 1.4s infinite; }
80
+ .typing-dots span:nth-child(2) { animation-delay:0.2s; }
81
+ .typing-dots span:nth-child(3) { animation-delay:0.4s; }
82
+ @keyframes typeBounce { 0%,80%,100%{transform:translateY(0)} 40%{transform:translateY(-6px)} }
83
+ .input-area { padding:14px 20px; background:var(--bg2); border-top:1px solid var(--border); display:flex; gap:10px; align-items:flex-end; }
84
+ .input-area textarea { flex:1; min-height:42px; max-height:120px; padding:10px 14px; border:1px solid var(--border); border-radius:10px; background:var(--bg3); resize:none; }
85
+ .send-btn {
86
+ background:var(--accent); color:#fff; border:none;
87
+ padding:10px 22px; border-radius:10px; cursor:pointer;
88
+ font-size:0.9rem; font-weight:600; transition:background 0.2s;
89
+ display:flex; align-items:center; gap:6px;
90
+ }
91
+ .send-btn:hover:not(:disabled) { background:var(--accent2); }
92
+ .send-btn:disabled { background:var(--border); cursor:not-allowed; }
93
+ .system-msg { text-align:center; padding:8px 16px; font-size:0.78rem; color:var(--text2); opacity:0.6; }
94
+ .prompt-suggestions { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:14px; padding:0 4px; }
95
+ .prompt-suggestions button {
96
+ background:var(--bg3); color:var(--text2); border:1px solid var(--border);
97
+ padding:7px 12px; border-radius:8px; cursor:pointer;
98
+ font-size:0.78rem; text-align:left; transition:all 0.2s;
99
+ }
100
+ .prompt-suggestions button:hover { background:var(--accent); color:#fff; border-color:var(--accent); }
101
+ .info-tip { font-size:0.72rem; color:var(--text2); margin-top:3px; opacity:0.7; }
102
+ .messages::-webkit-scrollbar { width:6px; }
103
+ .messages::-webkit-scrollbar-track { background:transparent; }
104
+ .messages::-webkit-scrollbar-thumb { background:var(--border); border-radius:3px; }
105
+ footer { text-align:center; padding:8px 20px; font-size:0.75rem; color:var(--text2); opacity:0.4; background:var(--bg2); }
106
+ </style>
107
+ </head>
108
+ <body>
109
+ <header>
110
+ <h1>🌿 Ternary-Bonsai <span>27B</span> · AWQ-4bit</h1>
111
+ <div class="header-right">
112
+ <div class="status-badge">
113
+ <div class="status-dot" id="statusDot"></div>
114
+ <span id="statusText">Отключено</span>
115
+ </div>
116
+ <button class="connect-btn" id="connectBtn" onclick="toggleConnection()">🔌 Подключить</button>
117
+ </div>
118
+ </header>
119
+
120
+ <div class="main">
121
+ <aside class="settings-panel" id="settingsPanel">
122
+ <div class="panel-section">
123
+ <h3>⚙️ Режим</h3>
124
+ <div class="mode-toggle">
125
+ <button class="active" onclick="setSystem('default')">🤖 Assistant</button>
126
+ <button onclick="setSystem('dev')">💻 Developer</button>
127
+ <button onclick="setSystem('creative')">🎨 Creative</button>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="panel-section">
132
+ <h3>⚡ Инференс</h3>
133
+ <div class="label">Max tokens</div>
134
+ <input type="number" id="maxTokens" value="2048" min="1" max="8192">
135
+ <div style="margin-top:8px"><div class="label">Temperature</div>
136
+ <input type="number" id="temperature" value="0.7" min="0" max="2" step="0.1"></div>
137
+ <div style="margin-top:8px"><div class="label">Top P</div>
138
+ <input type="number" id="topP" value="0.9" min="0" max="1" step="0.05"></div>
139
+ <div style="margin-top:8px"><div class="label">Top K</div>
140
+ <input type="number" id="topK" value="50" min="0" max="200"></div>
141
+ </div>
142
+
143
+ <div class="panel-section">
144
+ <h3>🔌 Backend</h3>
145
+ <div class="label">Режим</div>
146
+ <select id="backend" style="cursor:pointer;" onchange="updateBackendInfo()">
147
+ <option value="hf">Hugging Face API</option>
148
+ <option value="vllm">vLLM (локальный)</option>
149
+ </select>
150
+ <div class="info-tip" id="backendInfo">Используйте HF API для быстрого старта</div>
151
+
152
+ <div style="margin-top:12px"><div class="label">API Key / URL</div>
153
+ <input type="password" id="apiKey" placeholder="hf_abc123..."></div>
154
+ <div style="margin-top:12px" id="vllmSection"><div class="label">vLLM Address</div>
155
+ <input type="text" id="vllmUrl" value="http://localhost:8000" placeholder="http://localhost:8000">
156
+ <div class="info-tip">
157
+ <code style="font-size:0.72rem;color:var(--green);">
158
+ python -m vllm.entrypoints.openai.api_server --model prism-ml/Ternary-Bonsai-27B-AWQ-4bit --openai-api-host 0.0.0.0 --openai-api-port 8000
159
+ </code>
160
+ </div>
161
+ </div>
162
+ </div>
163
+
164
+ <div class="panel-section">
165
+ <h3>📜 Системное сообщение</h3>
166
+ <textarea id="systemPrompt" readonly></textarea>
167
+ </div>
168
+
169
+ <div style="margin-top:20px"><button onclick="clearChat()" style="width:100%;background:var(--bg3);border:1px solid var(--border);color:var(--text2);padding:8px;border-radius:8px;cursor:pointer;font-size:0.82rem;">🗑️ Очистить чат</button></div>
170
+ </aside>
171
+
172
+ <div class="chat-area">
173
+ <div class="messages" id="messages"></div>
174
+ <div class="input-area" id="inputArea">
175
+ <div>
176
+ <textarea id="userInput" rows="1" onkeydown="if(event.key==='Enter' && !event.shiftKey) sendMessage();"></textarea>
177
+ <div class="prompt-suggestions" id="suggestions">
178
+ <button onclick="usePrompt('Объясните, как AWQ-4bit квантование сохраняет точность при сжатии до 4 бит?')">AWQ-4bit квантование</button>
179
+ <button onclick="usePrompt('Каковы преимущества и ограничения Ternary-Bonsai 27B по сравнению с Llama 3 70B?')">Сравнение с Llama 3</button>
180
+ <button onclick="usePrompt('Напишите на Python скрипт для локального инференса AWQ-4bit моделей через vLLM с потоковой генерацией.')')">API vLLM (Python)</button>
181
+ <button onclick="usePrompt('Объясните разницу между GPTQ, AWQ, GGUF и QLoRA квантованием.')')">Сравнение методов</button>
182
+ </div>
183
+ </div>
184
+ <button class="send-btn" id="sendBtn" onclick="sendMessage()">
185
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13l-10-13"/></svg>
186
+ Отправить
187
+ </button>
188
+ </div>
189
+ </div>
190
+ </div>
191
+
192
+ <footer>Ternary-Bonsai-27B-AWQ-4bit · prism-ml · AWQ квантование</footer>
193
+
194
+ <script>
195
+ /* ───────────── State ───────────── */
196
+ let connected = false;
197
+ let isProcessing = false;
198
+ let currentMessages = [];
199
+ let typingTimeout = null;
200
+
201
+ /* ───────────── System Prompts ───────────── */
202
+ function setSystem(mode) {
203
+ const SYSTEMS = {
204
+ default: 'Вы — Ter
205
+
206
+
207
+ <|begin_of_
208
+
209
+ <|start_of_1_0_1|begin_1_0_1|>Термине
210
+
211
+ <|end_of_1_0_1|>Bonsai 27B — умный AI-ассистент для программирования, анализа данных и работы с AI-технологиями. Вы speaks Russian, English, French, German, Spanish. Ответы структурированы, точны, с примерами кода когда нужно. Вы умеете писать Python, TypeScript, HTML/CSS/JS.',
212
+
213
+ dev: 'Термине <
214
+
215
+ <|start_of_1_0_1|>Bonsai 27B — разработчик. Готовый писать код, дебагировать, оптимизировать. Ответы кратко, с кодом, с объяснением логики.',
216
+
217
+ creative: 'Термине <
218
+
219
+ <|start_of_1_0_1|>Bonsai 27B — творческий ассистент..Help with writing, brainstorming, creative writing, story writing, poetry, and more. Be imaginative and thoughtful.'
220
+ };
221
+
222
+ document.getElementById('systemPrompt').value = SYSTEMS[mode];
223
+ document.querySelectorAll('.mode-toggle button').forEach(b => b.classList.remove('active'));
224
+ event.target.classList.add('active');
225
+ }
226
+
227
+ /* ───────────── System Prompts ───────────── */
228
+ setSystem('default');
229
+
230
+ /* ───────────── Connection ───────────── */
231
+ function toggleConnection() {
232
+ const backend = document.getElementById('backend').value;
233
+ const api = document.getElementById('apiKey').value;
234
+ const vllm = document.getElementById('vllmUrl').value;
235
+
236
+ if (!api && !vllm) {
237
+ addMessage('system', '⚠️ Укажите API Key или URL сервера');
238
+ return;
239
+ }
240
+
241
+ if (backend === 'vllm' && !vllm) {
242
+ addMessage('system', '⚠️ Укажите URL vLLM');
243
+ return;
244
+ }
245
+
246
+ // Check connection
247
+ if (backend === 'hf') {
248
+ fetch('https://huggingface.co/api/v1/models/prism-ml/Ternary-Bonsai-27B-AWQ-4bit', {
249
+ headers: { 'Authorization': 'Token ' + api }
250
+ })
251
+ .then(r => {
252
+ if (r.ok) { connected = true; setConnected(true); }
253
+ else { addMessage('system', '❌ Ошибка подключения к HF API. Проверьте ключ.'); connected = false; }
254
+ })
255
+ .catch(() => { addMessage('system', '❌ Сетевая ошибка. Проверьте интернет.'); connected = false; });
256
+ } else {
257
+ fetch(vllm + '/v1/health', { headers: { 'Authorization': 'Bearer ' + api } })
258
+ .then(r => {
259
+ if (r.ok) { connected = true; setConnected(true); }
260
+ else { addMessage('system', '❌ vLLM не достиг. Проверьте адрес.'); connected = false; }
261
+ })
262
+ .catch(() => { addMessage('system', '❌ Сетевая ошибка. Проверьте адрес vLLM.'); connected = false; });
263
+ }
264
+ }
265
+
266
+ function setConnected(v) {
267
+ connected = v;
268
+ const dot = document.getElementById('statusDot');
269
+ const txt = document.getElementById('statusText');
270
+ const btn = document.getElementById('connectBtn');
271
+ if (v) { dot.classList.add('connected'); txt.textContent = 'Подключено'; btn.textContent = '🔌 Подключено'; btn.disabled = true; }
272
+ else { dot.classList.remove('connected'); txt.textContent = 'Отключено'; btn.textContent = '🔌 Подключить'; btn.disabled = false; }
273
+ }
274
+
275
+ function updateBackendInfo() {
276
+ const b = document.getElementById('backend').value;
277
+ const sec = document.getElementById('vllmSection');
278
+ const tip = document.getElementById('backendInfo');
279
+ if (b === 'vllm') { sec.style.display = 'block'; tip.textContent = 'vLLM: python -m vllm.entrypoints.openai.api_server --model prism-ml/Ternary-Bonsai-27B-AWQ-4bit'; }
280
+ else { sec.style.display = 'block'; tip.textContent = 'HF API: введите ваш HF token в поле "API Key / URL";' }
281
+ }
282
+
283
+ /* ───────────── Messages ───────────── */
284
+ function addMessage(role, text) {
285
+ const el = document.createElement('div');
286
+ el.className = 'message ' + role;
287
+ el.innerHTML = '<div class="author">' + (role === 'user' ? 'Вы' : 'Ternary-Bonsai') + '</div><div class="content">' + text + '</div>';
288
+ document.getElementById('messages').appendChild(el);
289
+ currentMessages.push({ role: role, content: text });
290
+ document.getElementById('messages').scrollTop = document.getElementById('messages').scrollTop + document.getElementById('messages').scrollHeight;
291
+ }
292
+
293
+ /* ───────────── Clear ───────────── */
294
+ function clearChat() {
295
+ currentMessages = [];
296
+ document.getElementById('messages').innerHTML = '';
297
+ setSystem('default');
298
+ }
299
+
300
+ /* ───────────── Send ───────────── */
301
+ function sendMessage() {
302
+ const input = document.getElementById('userInput');
303
+ const text = input.value.trim();
304
+ if (!text || !connected || isProcessing) return;
305
+
306
+ addMessage('user', text);
307
+ input.value = '';
308
+ isProcessing = true;
309
+ document.getElementById('sendBtn').disabled = true;
310
+
311
+ let backend = document.getElementById('backend').value;
312
+ let apiKey = document.getElementById('apiKey').value;
313
+ let vllm = document.getElementById('vllmUrl').value;
314
+ let params = {
315
+ max_new_tokens: +document.getElementById('maxTokens').value,
316
+ temperature: +document.getElementById('temperature').value,
317
+ top_p: +document.getElementById('topP').value,
318
+ top_k: +document.getElementById('topK').value,
319
+ model: 'prism-ml/Ternary-Bonsai-27B-AWQ-4bit'
320
+ };
321
+
322
+ if (backend === 'vllm') {
323
+ let apiEndpoint = vllm + '/v1/chat/completions';
324
+ apiEndpoint = apiEndpoint.replace('localhost:8000', vllm.replace('http://', ''));
325
+ const body = JSON.stringify({ messages: currentMessages, parameters: params });
326
+ fetch(apiEndpoint, {
327
+ method: 'POST',
328
+ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + apiKey },
329
+ body: body
330
+ })
331
+ .then(r => r.text())
332
+ .then(chunk => {
333
+ let reply = '';
334
+ chunk.split('\n\n').forEach(c => {
335
+ if (!c) return;
336
+ let j; try { j = JSON.parse(c); } catch { j = null; }
337
+ if (j && j.choices) {
338
+ let choice = j.choices[0];
339
+ if (choice.delta && choice.delta.content) {
340
+ reply += choice.delta.content;
341
+ } else if (choice.content) {
342
+ reply = choice.content;
343
+ }
344
+ }
345
+ });
346
+ processStreamReply(reply);
347
+ })
348
+ .catch(e => {
349
+ addMessage('system', '❌ Ошибка: ' + e.message);
350
+ isProcessing = false;
351
+ document.getElementById('sendBtn').disabled = false;
352
+ });
353
+ } else {
354
+ const body = JSON.stringify({
355
+ messages: currentMessages,
356
+ parameters: params
357
+ });
358
+ fetch('https://huggingface.co/api/v1/models/prism-ml/Ternary-Bonsai-27B-AWQ-4bit/chat/model-inference', {
359
+ method: 'POST',
360
+ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + apiKey },
361
+ body: body
362
+ })
363
+ .then(r => r.json())
364
+ .then(data => {
365
+ let reply = data.outputs[0].text;
366
+ processStreamReply(reply);
367
+ })
368
+ .catch(e => {
369
+ addMessage('system', '❌ Ошибка: ' + e.message);
370
+ isProcessing = false;
371
+ document.getElementById('sendBtn').disabled = false;
372
+ });
373
+ }
374
+ }
375
+
376
+ /* ───────────── Process Reply ───────────── */
377
+ function processStreamReply(reply) {
378
+ isProcessing = false;
379
+ document.getElementById('sendBtn').disabled = false;
380
+ if (reply && reply.length > 0) {
381
+ addMessage('ai', reply);
382
+ } else {
383
+ addMessage('system', '⚠️ Пустой ответ. Попробуйте ещё раз.');
384
+ }
385
+ }
386
+
387
+ /* ───────────── Prompt ───────────── */
388
+ function usePrompt(text) {
389
+ currentMessages.push({ role: 'user', content: text });
390
+ addMessage('user', text);
391
+ sendMessage();
392
+ }
393
+ </script>
394
+ <script data-huggingchat-badge="script">
395
+ (function () {
396
+ try {
397
+ if (document.querySelector("[data-huggingchat-badge]:not(script)")) return;
398
+ var host = document.createElement("div");
399
+ host.setAttribute("data-huggingchat-badge", "");
400
+ var styles = [["all","initial"],["position","fixed"],["right","12px"],["bottom","12px"],["z-index","2147483647"],["display","block"],["visibility","visible"],["opacity","1"],["pointer-events","auto"],["width","auto"],["height","auto"],["margin","0"],["padding","0"],["max-width","none"],["max-height","none"],["transform","none"],["filter","none"],["clip-path","none"],["color-scheme","light"]];
401
+ for (var i = 0; i < styles.length; i++) {
402
+ host.style.setProperty(styles[i][0], styles[i][1], "important");
403
+ }
404
+ host.attachShadow({ mode: "closed" }).innerHTML = "<style>\n:host { all: initial; }\na {\n display: flex;\n align-items: center;\n gap: 5px;\n box-sizing: border-box;\n height: 26px;\n padding: 0 9px 0 7px;\n border-radius: 999px;\n border: 1px solid rgba(0, 0, 0, 0.07);\n background: rgba(255, 255, 255, 0.92);\n -webkit-backdrop-filter: saturate(180%) blur(8px);\n backdrop-filter: saturate(180%) blur(8px);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 6px 16px rgba(0, 0, 0, 0.06);\n color: #111827;\n font: 500 11px/1 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: 0.01em;\n text-decoration: none;\n white-space: nowrap;\n opacity: 0.72;\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\na:hover, a:focus-visible { opacity: 1; transform: translateY(-1px); }\nsvg { width: 14px; height: 14px; flex: none; }\n@media print { a { display: none; } }\n<\/style><a href=\"https://huggingface.co/chat\" target=\"_blank\" rel=\"noopener noreferrer nofollow\"><svg viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\"><path d=\"M16.0006 25.9992C13.8266 25.999 11.7118 25.2901 9.97686 23.9799C8.2419 22.6698 6.98127 20.8298 6.38599 18.7388C5.79071 16.6478 5.89323 14.4198 6.678 12.3923C7.46278 10.3648 8.88705 8.64837 10.735 7.50308C12.5829 6.35779 14.7538 5.84606 16.9187 6.04544C19.0837 6.24481 21.1246 7.14442 22.7323 8.60795C24.34 10.0715 25.4268 12.0192 25.8281 14.1559C26.2293 16.2926 25.9232 18.5019 24.9561 20.449C24.7703 20.8042 24.7223 21.2155 24.8211 21.604L25.4211 23.8316C25.4803 24.0518 25.4805 24.2837 25.4216 24.5039C25.3627 24.7242 25.2468 24.925 25.0856 25.0862C24.9244 25.2474 24.7235 25.3633 24.5033 25.4222C24.283 25.4811 24.0512 25.4809 23.831 25.4217L21.6034 24.8217C21.2172 24.7248 20.809 24.7729 20.4558 24.9567C19.0683 25.6467 17.5457 26.0068 16.0006 26.0068V25.9992Z\" fill=\"currentColor\"/><path d=\"M9.62598 16.0013C9.62598 15.3799 10.1294 14.8765 10.7508 14.8765C11.3721 14.8765 11.8756 15.3799 11.8756 16.0013C11.8756 17.0953 12.3102 18.1448 13.0838 18.9184C13.8574 19.692 14.9069 20.1266 16.001 20.1267C17.095 20.1267 18.1445 19.692 18.9181 18.9184C19.6918 18.1448 20.1264 17.0953 20.1264 16.0013C20.1264 15.3799 20.6299 14.8765 21.2512 14.8765C21.8725 14.8765 22.3759 15.3799 22.3759 16.0013C22.3759 17.6921 21.7046 19.3137 20.509 20.5093C19.3134 21.7049 17.6918 22.3762 16.001 22.3762C14.3102 22.3762 12.6885 21.7049 11.4929 20.5093C10.2974 19.3137 9.62598 17.6921 9.62598 16.0013Z\" fill=\"#fff\"/><\/svg><span>Made with HuggingChat<\/span><\/a>";
405
+ function attach() {
406
+ var parent = document.body || document.documentElement;
407
+ if (parent && host.parentNode !== parent) parent.appendChild(host);
408
+ }
409
+ attach();
410
+ // Artifacts that rewrite document.body wholesale would drop the badge.
411
+ if (document.body && typeof MutationObserver === "function") {
412
+ new MutationObserver(attach).observe(document.body, { childList: true });
413
+ } else {
414
+ document.addEventListener("DOMContentLoaded", attach);
415
+ }
416
+ } catch (e) {}
417
+ })();
418
+ </script>