mihailik commited on
Commit
f761e54
·
1 Parent(s): 720d8eb

Extracting chat-full.js out now.

Browse files
Files changed (2) hide show
  1. chat-full.html +1 -150
  2. chat-full.js +148 -0
chat-full.html CHANGED
@@ -231,155 +231,6 @@
231
 
232
  <!-- Transformers.js через CDN (без фреймворків) -->
233
  <script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/transformers.min.js"></script>
234
- <script>
235
- (() => {
236
- const $ = (s) => document.querySelector(s);
237
- const messagesEl = $("#messages");
238
- const statusEl = $("#status");
239
- const statusText = $("#status-text");
240
- const modelSel = $("#model");
241
- const promptEl = $("#prompt");
242
- const sendBtn = $("#send");
243
- const form = $("#composer");
244
-
245
- // Режими/стан
246
- let busy = false;
247
- let currentModel = modelSel.value || "Xenova/distilgpt2";
248
- const cache = new Map(); // model -> { pipe, task }
249
- const chatHistory = []; // { role: 'user'|'assistant', content: string }
250
-
251
- // Зручні хелпери UI
252
- function setStatus(text, isBusy) {
253
- statusText.textContent = text;
254
- messagesEl.setAttribute("aria-busy", isBusy ? "true" : "false");
255
- busy = !!isBusy;
256
- promptEl.disabled = !!isBusy;
257
- sendBtn.disabled = !!isBusy;
258
- modelSel.disabled = !!isBusy;
259
- }
260
- function pushMsg(role, text) {
261
- const div = document.createElement("div");
262
- div.className = `msg ${role}`;
263
- div.textContent = text;
264
- messagesEl.appendChild(div);
265
- messagesEl.scrollTop = messagesEl.scrollHeight;
266
- }
267
-
268
- // Визначення задачі для моделі
269
- function taskForModel(model) {
270
- if ((model || "").toLowerCase().includes("t5")) return "text2text-generation";
271
- return "text-generation";
272
- }
273
-
274
- // Побудова простого prompt’а (мінімально універсальний)
275
- function buildPrompt(model, history) {
276
- const task = taskForModel(model);
277
- const lastUser = history.slice().reverse().find(m => m.role === "user");
278
- const userText = lastUser ? lastUser.content : "";
279
-
280
- if (task === "text2text-generation") {
281
- // T5 зазвичай у форматі інструкції
282
- return `Instruction: Answer briefly.\nInput: ${userText}\nOutput:`;
283
- } else {
284
- // GPT-подібні моделі — простий діалоговий патерн
285
- const sys = "You are a helpful assistant. Answer briefly.";
286
- const turns = history.map(m => (m.role === "user" ? `User: ${m.content}` : `Assistant: ${m.content}`));
287
- return `${sys}\n${turns.join("\n")}\nAssistant:`;
288
- }
289
- }
290
-
291
- // Завантаження/кешування пайплайну
292
- async function ensurePipeline(model) {
293
- if (cache.has(model)) return cache.get(model);
294
- const task = taskForModel(model);
295
- setStatus(`Завантаження моделі (${task})…`, true);
296
- try {
297
- // Опційно: можна налаштувати шляхи для wasm:
298
- // window.transformers.env.backends.onnx.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/wasm/";
299
-
300
- const pipe = await window.transformers.pipeline(task, model);
301
- const entry = { pipe, task };
302
- cache.set(model, entry);
303
- setStatus("Готово", false);
304
- return entry;
305
- } catch (err) {
306
- setStatus("Помилка завантаження моделі", false);
307
- pushMsg("sys", `Помилка при завантаженні '${model}': ${String(err && err.message || err)}`);
308
- throw err;
309
- }
310
- }
311
-
312
- async function generateAndReply() {
313
- const model = currentModel;
314
- const { pipe, task } = await ensurePipeline(model);
315
- const prompt = buildPrompt(model, chatHistory);
316
-
317
- setStatus("Генерація відповіді…", true);
318
- try {
319
- // Параметри генерації обрані консервативно для мобільних пристроїв
320
- const genOpts = {
321
- max_new_tokens: 64,
322
- temperature: 0.8,
323
- top_p: 0.9,
324
- do_sample: true
325
- };
326
- const out = await pipe(prompt, genOpts);
327
-
328
- // Узгодження виходу для різних пайплайнів
329
- let fullText = "";
330
- if (Array.isArray(out) && out.length) {
331
- // text-generation: [{ generated_text }]
332
- // text2text-generation: [{ generated_text }]
333
- fullText = out[0].generated_text ?? out[0].summary_text ?? String(out[0].text || "");
334
- } else if (typeof out === "string") {
335
- fullText = out;
336
- } else {
337
- fullText = JSON.stringify(out);
338
- }
339
-
340
- // Для text-generation відповідь часто містить prompt + продовженн�� — відтягуємо лише продовження
341
- let reply = fullText;
342
- if (task === "text-generation" && fullText.startsWith(prompt)) {
343
- reply = fullText.slice(prompt.length);
344
- }
345
-
346
- reply = (reply || "").trim();
347
- if (!reply) reply = "(порожня відповідь)";
348
-
349
- chatHistory.push({ role: "assistant", content: reply });
350
- pushMsg("bot", reply);
351
- } catch (err) {
352
- pushMsg("sys", `Помилка генерації: ${String(err && err.message || err)}`);
353
- } finally {
354
- setStatus("Готово", false);
355
- }
356
- }
357
-
358
- // Обробники подій
359
- modelSel.addEventListener("change", async () => {
360
- currentModel = modelSel.value;
361
- // Не завантажуємо модель негайно — лише коли треба згенерувати.
362
- // Але повідомимо користувача:
363
- pushMsg("sys", `Обрано модель: ${currentModel}`);
364
- });
365
-
366
- form.addEventListener("submit", async (e) => {
367
- e.preventDefault();
368
- if (busy) return;
369
- const text = (promptEl.value || "").trim();
370
- if (!text) return;
371
-
372
- chatHistory.push({ role: "user", content: text });
373
- pushMsg("me", text);
374
-
375
- promptEl.value = "";
376
- await generateAndReply();
377
- promptEl.focus();
378
- });
379
-
380
- // Стартовий стан
381
- promptEl.focus();
382
- })();
383
- </script>
384
  </body>
385
 
 
231
 
232
  <!-- Transformers.js через CDN (без фреймворків) -->
233
  <script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/transformers.min.js"></script>
234
+ <script src="chat-full.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  </body>
236
 
chat-full.js ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (() => {
2
+ const $ = (s) => document.querySelector(s);
3
+ const messagesEl = $("#messages");
4
+ const statusEl = $("#status");
5
+ const statusText = $("#status-text");
6
+ const modelSel = $("#model");
7
+ const promptEl = $("#prompt");
8
+ const sendBtn = $("#send");
9
+ const form = $("#composer");
10
+
11
+ // Режими/стан
12
+ let busy = false;
13
+ let currentModel = modelSel.value || "Xenova/distilgpt2";
14
+ const cache = new Map(); // model -> { pipe, task }
15
+ const chatHistory = []; // { role: 'user'|'assistant', content: string }
16
+
17
+ // Зручні хелпери UI
18
+ function setStatus(text, isBusy) {
19
+ statusText.textContent = text;
20
+ messagesEl.setAttribute("aria-busy", isBusy ? "true" : "false");
21
+ busy = !!isBusy;
22
+ promptEl.disabled = !!isBusy;
23
+ sendBtn.disabled = !!isBusy;
24
+ modelSel.disabled = !!isBusy;
25
+ }
26
+ function pushMsg(role, text) {
27
+ const div = document.createElement("div");
28
+ div.className = `msg ${role}`;
29
+ div.textContent = text;
30
+ messagesEl.appendChild(div);
31
+ messagesEl.scrollTop = messagesEl.scrollHeight;
32
+ }
33
+
34
+ // Визначення задачі для моделі
35
+ function taskForModel(model) {
36
+ if ((model || "").toLowerCase().includes("t5")) return "text2text-generation";
37
+ return "text-generation";
38
+ }
39
+
40
+ // Побудова простого prompt’а (мінімально універсальний)
41
+ function buildPrompt(model, history) {
42
+ const task = taskForModel(model);
43
+ const lastUser = history.slice().reverse().find(m => m.role === "user");
44
+ const userText = lastUser ? lastUser.content : "";
45
+
46
+ if (task === "text2text-generation") {
47
+ // T5 зазвичай у форматі інструкції
48
+ return `Instruction: Answer briefly.\nInput: ${userText}\nOutput:`;
49
+ } else {
50
+ // GPT-подібні моделі — простий діалоговий патерн
51
+ const sys = "You are a helpful assistant. Answer briefly.";
52
+ const turns = history.map(m => (m.role === "user" ? `User: ${m.content}` : `Assistant: ${m.content}`));
53
+ return `${sys}\n${turns.join("\n")}\nAssistant:`;
54
+ }
55
+ }
56
+
57
+ // Завантаження/кешування пайплайну
58
+ async function ensurePipeline(model) {
59
+ if (cache.has(model)) return cache.get(model);
60
+ const task = taskForModel(model);
61
+ setStatus(`Завантаження моделі (${task})…`, true);
62
+ try {
63
+ // Опційно: можна налаштувати шляхи для wasm:
64
+ // window.transformers.env.backends.onnx.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/wasm/";
65
+
66
+ const pipe = await window.transformers.pipeline(task, model);
67
+ const entry = { pipe, task };
68
+ cache.set(model, entry);
69
+ setStatus("Готово", false);
70
+ return entry;
71
+ } catch (err) {
72
+ setStatus("Помилка завантаження моделі", false);
73
+ pushMsg("sys", `Помилка при завантаженні '${model}': ${String(err && err.message || err)}`);
74
+ throw err;
75
+ }
76
+ }
77
+
78
+ async function generateAndReply() {
79
+ const model = currentModel;
80
+ const { pipe, task } = await ensurePipeline(model);
81
+ const prompt = buildPrompt(model, chatHistory);
82
+
83
+ setStatus("Генерація відповіді…", true);
84
+ try {
85
+ // Параметри генерації обрані консервативно для мобільних пристроїв
86
+ const genOpts = {
87
+ max_new_tokens: 64,
88
+ temperature: 0.8,
89
+ top_p: 0.9,
90
+ do_sample: true
91
+ };
92
+ const out = await pipe(prompt, genOpts);
93
+
94
+ // Узгодження виходу для різних пайплайнів
95
+ let fullText = "";
96
+ if (Array.isArray(out) && out.length) {
97
+ // text-generation: [{ generated_text }]
98
+ // text2text-generation: [{ generated_text }]
99
+ fullText = out[0].generated_text ?? out[0].summary_text ?? String(out[0].text || "");
100
+ } else if (typeof out === "string") {
101
+ fullText = out;
102
+ } else {
103
+ fullText = JSON.stringify(out);
104
+ }
105
+
106
+ // Для text-generation відповідь часто містить prompt + продовження — відтягуємо лише продовження
107
+ let reply = fullText;
108
+ if (task === "text-generation" && fullText.startsWith(prompt)) {
109
+ reply = fullText.slice(prompt.length);
110
+ }
111
+
112
+ reply = (reply || "").trim();
113
+ if (!reply) reply = "(порожня відповідь)";
114
+
115
+ chatHistory.push({ role: "assistant", content: reply });
116
+ pushMsg("bot", reply);
117
+ } catch (err) {
118
+ pushMsg("sys", `Помилка генерації: ${String(err && err.message || err)}`);
119
+ } finally {
120
+ setStatus("Готово", false);
121
+ }
122
+ }
123
+
124
+ // Обробники подій
125
+ modelSel.addEventListener("change", async () => {
126
+ currentModel = modelSel.value;
127
+ // Не завантажуємо модель негайно — лише коли треба згенерувати.
128
+ // Але повідомимо користувача:
129
+ pushMsg("sys", `Обрано модель: ${currentModel}`);
130
+ });
131
+
132
+ form.addEventListener("submit", async (e) => {
133
+ e.preventDefault();
134
+ if (busy) return;
135
+ const text = (promptEl.value || "").trim();
136
+ if (!text) return;
137
+
138
+ chatHistory.push({ role: "user", content: text });
139
+ pushMsg("me", text);
140
+
141
+ promptEl.value = "";
142
+ await generateAndReply();
143
+ promptEl.focus();
144
+ });
145
+
146
+ // Стартовий стан
147
+ promptEl.focus();
148
+ })();