fivetech commited on
Commit
181e292
·
verified ·
1 Parent(s): 9055c9c

Delete 1-index.html

Browse files
Files changed (1) hide show
  1. 1-index.html +0 -403
1-index.html DELETED
@@ -1,403 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="es">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Multi-Agent Debate - MiMo-V2.6-Distill-Qwen-9B</title>
7
- <style>
8
- :root{
9
- --bg:#000000; --surface:#16181c; --surface2:#1d1f24; --border:#2f3336;
10
- --text:#e7e9ea; --muted:#71767b; --accent:#1d9bf0;
11
- }
12
- body.light{ --bg:#ffffff; --surface:#f7f9f9; --surface2:#eff1f1; --border:#cfd9de; --text:#0f1419; --muted:#536471; }
13
- *{box-sizing:border-box}
14
- html,body{margin:0;padding:0;background:var(--bg);color:var(--text);
15
- font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:15px}
16
- #app{max-width:760px;margin:0 auto;display:flex;flex-direction:column;height:100vh;height:100dvh}
17
-
18
- header{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid var(--border)}
19
- header .logo{width:26px;height:26px;border-radius:8px;background:linear-gradient(135deg,var(--accent),#7856ff);
20
- display:flex;align-items:center;justify-content:center;font-size:14px;color:#fff;flex:none}
21
- header .title{font-weight:700;font-size:16px;letter-spacing:.2px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
22
- header .subtitle{font-size:12px;color:var(--muted);font-weight:400}
23
- #settings-btn{background:none;border:1px solid var(--border);color:var(--muted);border-radius:50%;
24
- width:34px;height:34px;cursor:pointer;font-size:15px;display:flex;align-items:center;justify-content:center;transition:.15s}
25
- #settings-btn:hover{color:var(--text);border-color:var(--muted)}
26
-
27
- #chat{flex:1;overflow-y:auto;padding:20px 18px;display:flex;flex-direction:column;gap:20px}
28
- .msg{display:flex;gap:12px;animation:fadein .3s ease;max-width:100%}
29
- .msg .avatar{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;
30
- font-size:15px;color:#fff;flex:none;margin-top:2px}
31
- .msg .content{flex:1;min-width:0}
32
- .msg .who{font-size:13px;font-weight:700;margin-bottom:3px}
33
- .msg .who .round{font-weight:400;color:var(--muted);margin-left:6px;font-size:12px}
34
- .msg .body{line-height:1.55;word-wrap:break-word;color:var(--text)}
35
- .msg .body p{margin:.35em 0}
36
- .msg.user{justify-content:flex-end}
37
- .msg.user .content{flex:none;max-width:80%;background:var(--surface2);border:1px solid var(--border);
38
- border-radius:18px 18px 4px 18px;padding:10px 14px}
39
- .msg.user .who{display:none}
40
- .msg.err{color:#f4212e;font-size:14px}
41
- @keyframes fadein{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
42
- @keyframes pulse{0%,100%{opacity:.3}50%{opacity:1}}
43
- .msg.streaming .body::after{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;
44
- background:var(--accent);margin-left:5px;animation:pulse 1s infinite}
45
- details.think{margin:2px 0 8px;font-size:13px;color:var(--muted)}
46
- details.think summary{cursor:pointer;font-size:12px}
47
- details.think .t{white-space:pre-wrap;border-left:2px solid var(--border);padding-left:10px;margin-top:5px;font-size:12.5px}
48
-
49
- #controls{padding:12px 18px;border-top:1px solid var(--border);display:flex;gap:10px;align-items:center}
50
- #topic{flex:1;min-width:0;background:var(--surface);border:1px solid var(--border);color:var(--text);
51
- border-radius:999px;padding:12px 18px;font-size:15px;outline:none;transition:border-color .15s}
52
- #topic:focus{border-color:var(--accent)}
53
- #topic::placeholder{color:var(--muted)}
54
- #start-btn{flex:none;background:var(--text);color:var(--bg);border:none;border-radius:999px;padding:12px 16px;
55
- cursor:pointer;font-weight:700;font-size:14px;transition:opacity .15s;white-space:nowrap}
56
- #start-btn:hover{opacity:.85}
57
- #start-btn:disabled{opacity:.35;cursor:default}
58
- #stop-btn{flex:none;background:none;border:1px solid #f4212e;color:#f4212e;border-radius:999px;
59
- padding:11px 18px;cursor:pointer;font-weight:700;font-size:14px;display:none}
60
-
61
- #settings{display:none;margin:0 18px 12px;padding:16px;border:1px solid var(--border);
62
- border-radius:16px;background:var(--surface)}
63
- #settings.open{display:block}
64
- #settings label{display:block;font-size:12px;color:var(--muted);margin:10px 0 4px}
65
- #settings input,#settings textarea{width:100%;background:var(--bg);color:var(--text);
66
- border:1px solid var(--border);border-radius:10px;padding:9px 12px;font-size:14px;font-family:inherit;outline:none}
67
- #settings input:focus,#settings textarea:focus{border-color:var(--accent)}
68
- #settings textarea{min-height:150px;font-family:ui-monospace,"SF Mono",Menlo,monospace;font-size:12px}
69
- #settings .row{display:flex;gap:10px;flex-wrap:wrap}
70
- #settings .row>div{flex:1;min-width:120px}
71
- #apply-settings{margin-top:12px;background:var(--accent);border:none;color:#fff;border-radius:999px;
72
- padding:10px 18px;cursor:pointer;font-weight:700}
73
-
74
- #status{padding:7px 18px;font-size:12px;color:var(--muted)}
75
- #progress{height:2px;background:var(--surface)}
76
- #progress>div{height:100%;width:0;background:linear-gradient(90deg,var(--accent),#7856ff);transition:width .25s}
77
- footer{padding:10px 18px;font-size:11px;color:var(--muted);border-top:1px solid var(--border);
78
- display:flex;gap:8px 16px;flex-wrap:wrap;justify-content:center;text-align:center}
79
- footer a{color:var(--accent);text-decoration:none}
80
-
81
- @media (max-width:640px){
82
- #chat{padding:14px 12px;gap:16px}
83
- #controls{padding:10px 12px}
84
- header{padding:12px}
85
- header .subtitle{display:none}
86
- .msg .avatar{width:28px;height:28px;font-size:13px}
87
- #settings{margin:0 12px 10px}
88
- }
89
- </style>
90
- </head>
91
- <body>
92
- <div id="app">
93
- <header>
94
- <span class="logo">&#10022;</span>
95
- <span class="title" id="title">Multi-Agent Debate <span class="subtitle">&middot; MiMo-V2.6-Distill-Qwen-9B</span></span>
96
- <button id="settings-btn" title="Ajustes">&#9881;</button>
97
- </header>
98
-
99
- <div id="settings">
100
- <div class="row">
101
- <div><label>Repositorio Hugging Face</label><input id="set-repo"></div>
102
- <div><label>Archivo GGUF</label><input id="set-file"></div>
103
- </div>
104
- <div class="row">
105
- <div><label>Contexto (tokens)</label><input id="set-ctx" type="number" step="512" min="2048"></div>
106
- <div><label>M&aacute;x. tokens por turno</label><input id="set-maxtokens" type="number" step="32" min="64"></div>
107
- <div><label>Temperatura</label><input id="set-temp" type="number" step="0.05" min="0" max="2"></div>
108
- <div><label>Rondas</label><input id="set-rounds" type="number" step="1" min="1" max="20"></div>
109
- </div>
110
- <label>Agentes (JSON: name, emoji, color, system)</label>
111
- <textarea id="set-agents" spellcheck="false"></textarea>
112
- <button id="apply-settings">Aplicar</button>
113
- </div>
114
-
115
- <div id="chat"></div>
116
-
117
- <div id="controls">
118
- <input id="topic" placeholder="Propón un tema y los agentes debaten&hellip;">
119
- <button id="start-btn" disabled>Cargando modelo&hellip;</button>
120
- <button id="stop-btn">Detener</button>
121
- </div>
122
-
123
- <div id="status">Iniciando&hellip;</div>
124
- <div id="progress"><div></div></div>
125
- <footer>
126
- <span>La conversación se procesa en tu navegador (WebGPU si está disponible; si no, CPU/WASM). Solo se conecta a Hugging Face para descargar el modelo (5,8 GB la primera vez) y a jsDelivr para las librerías.</span>
127
- <span>Motor: <a href="https://github.com/ngxson/wllama" target="_blank" rel="noopener">wllama</a> (MIT) &middot; inspirado en el widget de <a href="https://localellm.com" target="_blank" rel="noopener">localellm.com</a> &middot; modelo <a href="https://huggingface.co/XiaomiMiMo/MiMo-V2.6-Distill-Qwen-9B" target="_blank" rel="noopener">MiMo-V2.6-Distill-Qwen-9B</a> (MIT, Xiaomi)</span>
128
- </footer>
129
- </div>
130
-
131
- <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
132
- <script type="module">
133
- import { Wllama } from 'https://cdn.jsdelivr.net/npm/@wllama/wllama@3.5.1/esm/index.js';
134
-
135
- const params = new URLSearchParams(location.search);
136
-
137
- const DEFAULT_AGENTS = [
138
- { name: 'Ada', emoji: '\u2726', color: '#58a6ff',
139
- system: 'Eres Ada, la analista entusiasta del grupo. Defiendes las ideas con argumentos concretos, propones caminos a seguir y reconoces los puntos fuertes de los dem\u00e1s.' },
140
- { name: 'Bruno', emoji: '\u25B2', color: '#f0883e',
141
- system: 'Eres Bruno, el esc\u00e9ptico del grupo. Cuestionas supuestos, se\u00f1alas riesgos y fallos l\u00f3gicos, y pides evidencia. Eres duro con las ideas y respetuoso con las personas.' },
142
- { name: 'Carla', emoji: '\u25CF', color: '#3fb950',
143
- system: 'Eres Carla, la moderadora. Resume lo dicho, integra los desacuerdos y propone s\u00edntesis o el siguiente paso. Cierra cada ronda con una conclusi\u00f3n breve.' },
144
- ];
145
-
146
- const CONFIG = {
147
- modelUrl: params.get('modelUrl') || '',
148
- repo: params.get('repo') || 'bartowski/MiMo-V2.6-Distill-Qwen-9B-GGUF',
149
- file: params.get('file') || 'MiMo-V2.6-Distill-Qwen-9B-Q4_K_M.gguf',
150
- nCtx: parseInt(params.get('ctx')) || 8192,
151
- maxTokens: parseInt(params.get('maxTokens')) || 384,
152
- temperature: parseFloat(params.get('temp')) || 0.7,
153
- rounds: parseInt(params.get('rounds')) || 2,
154
- autoload: params.get('autoload') !== 'false',
155
- };
156
- if (params.get('theme') === 'light') document.body.classList.add('light');
157
- let agents = DEFAULT_AGENTS;
158
- if (params.get('agents')) {
159
- try { agents = JSON.parse(params.get('agents')); } catch (e) { console.warn('agents JSON inv\u00e1lido', e); }
160
- }
161
-
162
- const chatEl = document.getElementById('chat');
163
- const statusEl = document.getElementById('status');
164
- const progressEl = document.getElementById('progress').firstElementChild;
165
- const topicEl = document.getElementById('topic');
166
- const startBtn = document.getElementById('start-btn');
167
- const stopBtn = document.getElementById('stop-btn');
168
-
169
- let wllama = null;
170
- let running = false;
171
- let stopRequested = false;
172
- let transcript = []; // {name|null(user), text}
173
- let topic = '';
174
-
175
- function setStatus(t) { statusEl.textContent = t; }
176
- function setProgress(p) { progressEl.style.width = Math.max(0, Math.min(100, p)) + '%'; }
177
-
178
- function esc(s){ const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
179
-
180
- function addMsg(agent, isUser = false) {
181
- const d = document.createElement('div');
182
- d.className = 'msg' + (isUser ? ' user' : '');
183
- const content = document.createElement('div');
184
- content.className = 'content';
185
- const who = document.createElement('div');
186
- who.className = 'who';
187
- const body = document.createElement('div');
188
- body.className = 'body';
189
- if (isUser) { who.textContent = 'T\u00fa'; }
190
- else {
191
- const av = document.createElement('div');
192
- av.className = 'avatar';
193
- av.style.background = agent.color;
194
- av.textContent = agent.emoji;
195
- d.appendChild(av);
196
- who.textContent = agent.name;
197
- who.style.color = agent.color;
198
- }
199
- content.appendChild(who); content.appendChild(body);
200
- d.appendChild(content);
201
- chatEl.appendChild(d);
202
- chatEl.scrollTop = chatEl.scrollHeight;
203
- return { el: d, body, content };
204
- }
205
-
206
- function addErr(text) {
207
- const d = document.createElement('div');
208
- d.className = 'msg err';
209
- d.textContent = text;
210
- chatEl.appendChild(d);
211
- }
212
-
213
- async function loadModel(repo, file) {
214
- const url = CONFIG.modelUrl || `https://huggingface.co/${repo}/resolve/main/${file}`;
215
- startBtn.disabled = true;
216
- setProgress(0);
217
- setStatus(`Descargando ${file} (ctx ${CONFIG.nCtx})\u2026`);
218
- try {
219
- if (wllama) await wllama.exit();
220
- wllama = new Wllama({
221
- 'default': 'https://cdn.jsdelivr.net/npm/@wllama/wllama@3.5.1/esm/wasm/wllama.wasm',
222
- }, { parallelDownloads: 5 });
223
- await wllama.loadModelFromUrl(url, {
224
- n_gpu_layers: 999,
225
- n_ctx: CONFIG.nCtx,
226
- n_batch: 512,
227
- n_threads: Math.max(1, Math.min(8, navigator.hardwareConcurrency || 4)),
228
- progressCallback: ({ loaded, total }) => {
229
- if (total > 0) {
230
- const pct = Math.round((loaded / total) * 100);
231
- setProgress(pct);
232
- setStatus(`Descargando\u2026 ${pct}% (${(loaded / 1048576).toFixed(0)} MB)`);
233
- }
234
- },
235
- });
236
- setProgress(100);
237
- setStatus(`Modelo listo. ${agents.length} agentes: ${agents.map(a => a.name).join(', ')}.`);
238
- startBtn.disabled = false;
239
- startBtn.textContent = 'Iniciar debate';
240
- topicEl.focus();
241
- } catch (e) {
242
- console.error(e);
243
- setStatus('Error al cargar el modelo');
244
- addErr('No se pudo cargar el modelo: ' + (e?.message || e) +
245
- '. Prueba a recargar la p\u00e1gina o a cambiar el archivo en Ajustes.');
246
- }
247
- }
248
-
249
- const RULES = 'Est\u00e1s en una conversaci\u00f3n entre varios agentes. ' +
250
- 'Responde SOLO con tu intervenci\u00f3n: 2-5 frases, directas, en el idioma de la conversaci\u00f3n. ' +
251
- 'No repitas lo ya dicho; reacciona a la \u00faltima intervenci\u00f3n. ' +
252
- 'No escribas tu nombre ni ning\u00fan prefijo como "Nombre:". No uses listas largas.';
253
-
254
- function transcriptText(maxChars = 9000) {
255
- const lines = transcript.map(m => `${m.name || 'Usuario'}: ${m.text}`);
256
- let out = lines.join('\n\n');
257
- while (out.length > maxChars && lines.length > 2) {
258
- lines.splice(1, 1); // conserva el tema (primera l\u00ednea) y lo m\u00e1s reciente
259
- out = lines.join('\n\n');
260
- }
261
- return out;
262
- }
263
-
264
- async function agentTurn(agent) {
265
- const view = transcriptText();
266
- const messages = [
267
- { role: 'system', content: agent.system + ' ' + RULES },
268
- { role: 'user', content:
269
- `Tema del debate: ${topic}\n\n` +
270
- (view ? `Conversaci\u00f3n hasta ahora:\n${view}\n\n` : 'A\u00fan no hay intervenciones.\n\n') +
271
- `Ahora hablas t\u00fa, ${agent.name}. Escribe tu intervenci\u00f3n.` },
272
- ];
273
- const { el, body } = addMsg(agent);
274
- el.classList.add('streaming');
275
- let think = null;
276
- let answer = '', reasoning = '', count = 0;
277
- setStatus(`${agent.name} est\u00e1 escribiendo\u2026`);
278
- try {
279
- const stream = await wllama.createChatCompletion({
280
- messages,
281
- max_tokens: CONFIG.maxTokens,
282
- temperature: CONFIG.temperature,
283
- stream: true,
284
- });
285
- for await (const chunk of stream) {
286
- if (stopRequested) break;
287
- const delta = chunk.choices?.[0]?.delta || {};
288
- const tk = delta.content || '';
289
- const rs = delta.reasoning_content || '';
290
- if (rs) {
291
- reasoning += rs;
292
- if (!think) {
293
- think = document.createElement('details');
294
- think.className = 'think';
295
- think.innerHTML = '<summary>Pensamiento</summary><div class="t"></div>';
296
- body.parentNode.insertBefore(think, body);
297
- }
298
- think.querySelector('.t').textContent = reasoning;
299
- }
300
- if (tk) { answer += tk; body.textContent = answer; }
301
- if (tk || rs) { count++; if (count % 10 === 0) setStatus(`${agent.name}\u2026 ${count} tokens`); }
302
- chatEl.scrollTop = chatEl.scrollHeight;
303
- }
304
- } catch (e) {
305
- console.error(e);
306
- el.classList.add('err');
307
- body.textContent = 'Error: ' + (e?.message || e);
308
- el.classList.remove('streaming');
309
- return;
310
- }
311
- el.classList.remove('streaming');
312
- if (stopRequested) { el.remove(); setStatus('Detenido'); return; }
313
- answer = answer.replace(/^\s*(?:\*\*)?[A-Za-z\u00c0-\u024f]+:\s*/, '').trim();
314
- if (!answer) { el.remove(); setStatus('Listo'); return; }
315
- body.innerHTML = (typeof marked !== 'undefined') ? marked.parse(answer) : esc(answer).replace(/\n/g, '<br>');
316
- transcript.push({ name: agent.name, text: answer });
317
- setStatus('Listo');
318
- }
319
-
320
- let resume = { round: 1, agent: 0 };
321
-
322
- async function runDebate() {
323
- running = true;
324
- stopRequested = false;
325
- startBtn.style.display = 'none';
326
- stopBtn.style.display = 'inline-block';
327
- topicEl.disabled = true;
328
- try {
329
- for (let r = resume.round; r <= CONFIG.rounds && !stopRequested; r++) {
330
- setStatus(`Ronda ${r}/${CONFIG.rounds}`);
331
- for (let i = (r === resume.round ? resume.agent : 0); i < agents.length; i++) {
332
- if (stopRequested) { resume = { round: r, agent: i }; break; }
333
- await agentTurn(agents[i]);
334
- if (stopRequested) { resume = { round: r, agent: i }; break; }
335
- }
336
- if (!stopRequested) resume = { round: r + 1, agent: 0 };
337
- }
338
- if (!stopRequested) resume = { round: 1, agent: 0 };
339
- } finally {
340
- running = false;
341
- stopBtn.style.display = 'none';
342
- startBtn.style.display = 'inline-block';
343
- startBtn.textContent = 'Continuar debate';
344
- startBtn.disabled = false;
345
- topicEl.disabled = false;
346
- setStatus(stopRequested ? 'Detenido. Puedes continuar o cambiar de tema.' : 'Debate terminado. Puedes continuar.');
347
- }
348
- }
349
-
350
- startBtn.addEventListener('click', () => {
351
- if (!wllama || running) return;
352
- const newTopic = topicEl.value.trim();
353
- if (newTopic && newTopic !== topic) {
354
- topic = newTopic;
355
- transcript = [];
356
- chatEl.innerHTML = '';
357
- resume = { round: 1, agent: 0 };
358
- const m = addMsg(null, true);
359
- m.body.textContent = newTopic;
360
- transcript.push({ name: null, text: 'Tema propuesto: ' + newTopic });
361
- } else if (!topic) {
362
- topic = 'Tema libre';
363
- }
364
- runDebate();
365
- });
366
- stopBtn.addEventListener('click', () => { stopRequested = true; setStatus('Deteniendo\u2026'); });
367
-
368
- // --- Settings ---
369
- const settingsPanel = document.getElementById('settings');
370
- document.getElementById('settings-btn').addEventListener('click', () => {
371
- if (!settingsPanel.classList.contains('open')) {
372
- document.getElementById('set-repo').value = CONFIG.repo;
373
- document.getElementById('set-file').value = CONFIG.file;
374
- document.getElementById('set-ctx').value = CONFIG.nCtx;
375
- document.getElementById('set-maxtokens').value = CONFIG.maxTokens;
376
- document.getElementById('set-temp').value = CONFIG.temperature;
377
- document.getElementById('set-rounds').value = CONFIG.rounds;
378
- document.getElementById('set-agents').value = JSON.stringify(agents, null, 2);
379
- }
380
- settingsPanel.classList.toggle('open');
381
- });
382
- document.getElementById('apply-settings').addEventListener('click', async () => {
383
- try { agents = JSON.parse(document.getElementById('set-agents').value); }
384
- catch (e) { alert('JSON de agentes inv\u00e1lido: ' + e.message); return; }
385
- if (!Array.isArray(agents) || agents.length < 2) { alert('Se necesitan al menos 2 agentes.'); return; }
386
- CONFIG.maxTokens = parseInt(document.getElementById('set-maxtokens').value) || CONFIG.maxTokens;
387
- CONFIG.temperature = parseFloat(document.getElementById('set-temp').value) || CONFIG.temperature;
388
- CONFIG.rounds = parseInt(document.getElementById('set-rounds').value) || CONFIG.rounds;
389
- const newCtx = parseInt(document.getElementById('set-ctx').value) || CONFIG.nCtx;
390
- const newRepo = document.getElementById('set-repo').value.trim();
391
- const newFile = document.getElementById('set-file').value.trim();
392
- const reload = newCtx !== CONFIG.nCtx || newRepo !== CONFIG.repo || newFile !== CONFIG.file;
393
- CONFIG.nCtx = newCtx; CONFIG.repo = newRepo; CONFIG.file = newFile;
394
- settingsPanel.classList.remove('open');
395
- if (reload || !wllama) { transcript = []; chatEl.innerHTML = ''; await loadModel(CONFIG.repo, CONFIG.file); }
396
- else setStatus(`Ajustes aplicados. Agentes: ${agents.map(a => a.name).join(', ')}.`);
397
- });
398
-
399
- if (CONFIG.autoload) loadModel(CONFIG.repo, CONFIG.file);
400
- else setStatus('Modo manual: carga el modelo desde Ajustes.');
401
- </script>
402
- </body>
403
- </html>