fivetech commited on
Commit
6a86352
·
verified ·
1 Parent(s): 32ad57c

Upload 2 files

Browse files
Files changed (2) hide show
  1. 1-index.html +403 -0
  2. 2-README.md +22 -0
1-index.html ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
2-README.md ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Multi-Agent Debate - MiMo WebGPU
3
+ emoji: 🤖
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: static
7
+ app_file: index.html
8
+ pinned: false
9
+ models:
10
+ - bartowski/MiMo-V2.6-Distill-Qwen-9B-GGUF
11
+ - XiaomiMiMo/MiMo-V2.6-Distill-Qwen-9B
12
+ ---
13
+
14
+ # Multi-Agent Debate — MiMo-V2.6-Distill-Qwen-9B (WebGPU)
15
+
16
+ Several configurable agents share one in-browser model and debate a topic you propose,
17
+ round by round. Built with [wllama](https://github.com/ngxson/wllama) (MIT); inspired by the
18
+ [localellm.com](https://localellm.com) WebGPU widget. Model:
19
+ [MiMo-V2.6-Distill-Qwen-9B](https://huggingface.co/XiaomiMiMo/MiMo-V2.6-Distill-Qwen-9B) (MIT, Xiaomi).
20
+
21
+ Inference runs in your browser (WebGPU when available, otherwise CPU/WASM). The browser only
22
+ contacts Hugging Face to download the model (~5.8 GB the first time) and jsDelivr for libraries.