Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -352,12 +352,22 @@ async function generate(system, user, maxTokens, onDelta) {
|
|
| 352 |
return out.replace(/^\s*(?:\*\*)?[A-Za-zÀ-ɏ0-9_ ]{1,30}:\s*/, '').trim();
|
| 353 |
}
|
| 354 |
function extractJSON(text) {
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
}
|
| 362 |
return null;
|
| 363 |
}
|
|
@@ -525,6 +535,11 @@ async function runJudging() {
|
|
| 525 |
try {
|
| 526 |
const out = await generate(system, user, CONFIG.judgeTokens);
|
| 527 |
parsed = extractJSON(out);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 528 |
} catch (e) { console.warn(j.name, e); }
|
| 529 |
// fallback heurístico si el JSON no es parseable
|
| 530 |
ps.forEach(a => {
|
|
@@ -554,6 +569,10 @@ async function runJudging() {
|
|
| 554 |
`Responde SOLO con JSON válido: {"winner":"<nombre exacto del ganador o Empate>","justification":"2-4 frases","decisive":["argumento decisivo 1","2"],"mistakes":["error detectado 1","2"],"best":["mejor intervención 1"]}`;
|
| 555 |
const out = await generate(system, user, CONFIG.judgeTokens + 50);
|
| 556 |
verdict = extractJSON(out);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
} catch (e) { console.warn('head judge', e); }
|
| 558 |
finalizeBattle(verdict);
|
| 559 |
}
|
|
@@ -602,12 +621,26 @@ function renderResults(totals, winner, verdict) {
|
|
| 602 |
<div class="crown">${heur ? '⚠️' : '👑'}</div>
|
| 603 |
<h1 style="font-size:1.8rem;font-weight:900">${heur ? '⚠️ Evaluación no fiable' : (winner ? `<span style="color:${winner.color}">${winner.emoji} ${esc(winner.name)}</span> gana la batalla` : '🤝 EMPATE')}</h1>
|
| 604 |
<p style="color:var(--muted);margin-top:8px;font-size:.9rem">${esc(battle.topic)}</p>
|
| 605 |
-
${heur ? `<p style="max-width:660px;margin:12px auto 0;font-size:.88rem;line-height:1.6;color:#f59e0b">El modelo no produjo evaluaciones estructuradas
|
| 606 |
${!heur && verdict?.justification ? `<p style="max-width:640px;margin:12px auto 0;font-size:.9rem;line-height:1.6">${esc(verdict.justification)}</p>` : ''}`;
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 611 |
$('#judge-cards').innerHTML = battle.judgeReports.map(r => `
|
| 612 |
<div class="judge-card"><h4>${r.emoji} ${r.judge}</h4><p>${r.parsed ? esc(JSON.stringify(r.parsed)) : 'Evaluación heurística (respuesta no estructurada).'}</p></div>`).join('') +
|
| 613 |
`<div class="judge-card"><h4>👑 Head Judge</h4><p>${verdict?.justification ? esc(verdict.justification) : 'Veredicto por puntuación ponderada.'}</p>
|
|
|
|
| 352 |
return out.replace(/^\s*(?:\*\*)?[A-Za-zÀ-ɏ0-9_ ]{1,30}:\s*/, '').trim();
|
| 353 |
}
|
| 354 |
function extractJSON(text) {
|
| 355 |
+
if (!text) return null;
|
| 356 |
+
const cleaned = String(text).replace(/```(?:json)?/gi, ' ');
|
| 357 |
+
const tryParse = s => {
|
| 358 |
+
try { return JSON.parse(s); } catch { /* sigue */ }
|
| 359 |
+
try { return JSON.parse(s.replace(/,\s*([}\]])/g, '$1')); } catch { /* comas colgantes */ }
|
| 360 |
+
return null;
|
| 361 |
+
};
|
| 362 |
+
for (let start = cleaned.indexOf('{'); start >= 0; start = cleaned.indexOf('{', start + 1)) {
|
| 363 |
+
let depth = 0;
|
| 364 |
+
for (let i = start; i < cleaned.length; i++) {
|
| 365 |
+
if (cleaned[i] === '{') depth++;
|
| 366 |
+
else if (cleaned[i] === '}') {
|
| 367 |
+
depth--;
|
| 368 |
+
if (depth === 0) { const r = tryParse(cleaned.slice(start, i + 1)); if (r) return r; break; }
|
| 369 |
+
}
|
| 370 |
+
}
|
| 371 |
}
|
| 372 |
return null;
|
| 373 |
}
|
|
|
|
| 535 |
try {
|
| 536 |
const out = await generate(system, user, CONFIG.judgeTokens);
|
| 537 |
parsed = extractJSON(out);
|
| 538 |
+
if (!parsed) {
|
| 539 |
+
// un reintento con instrucción más estricta; si sigue fallando, queda como "no evaluable"
|
| 540 |
+
const out2 = await generate(system, user + '\n\nIMPORTANTE: responde ÚNICAMENTE con el JSON válido pedido, sin explicaciones, sin markdown y sin texto adicional.', CONFIG.judgeTokens);
|
| 541 |
+
parsed = extractJSON(out2);
|
| 542 |
+
}
|
| 543 |
} catch (e) { console.warn(j.name, e); }
|
| 544 |
// fallback heurístico si el JSON no es parseable
|
| 545 |
ps.forEach(a => {
|
|
|
|
| 569 |
`Responde SOLO con JSON válido: {"winner":"<nombre exacto del ganador o Empate>","justification":"2-4 frases","decisive":["argumento decisivo 1","2"],"mistakes":["error detectado 1","2"],"best":["mejor intervención 1"]}`;
|
| 570 |
const out = await generate(system, user, CONFIG.judgeTokens + 50);
|
| 571 |
verdict = extractJSON(out);
|
| 572 |
+
if (!verdict) {
|
| 573 |
+
const out2 = await generate(system, user + '\n\nIMPORTANTE: responde ÚNICAMENTE con el JSON válido pedido, sin explicaciones, sin markdown y sin texto adicional.', CONFIG.judgeTokens + 50);
|
| 574 |
+
verdict = extractJSON(out2);
|
| 575 |
+
}
|
| 576 |
} catch (e) { console.warn('head judge', e); }
|
| 577 |
finalizeBattle(verdict);
|
| 578 |
}
|
|
|
|
| 621 |
<div class="crown">${heur ? '⚠️' : '👑'}</div>
|
| 622 |
<h1 style="font-size:1.8rem;font-weight:900">${heur ? '⚠️ Evaluación no fiable' : (winner ? `<span style="color:${winner.color}">${winner.emoji} ${esc(winner.name)}</span> gana la batalla` : '🤝 EMPATE')}</h1>
|
| 623 |
<p style="color:var(--muted);margin-top:8px;font-size:.9rem">${esc(battle.topic)}</p>
|
| 624 |
+
${heur ? `<p style="max-width:660px;margin:12px auto 0;font-size:.88rem;line-height:1.6;color:#f59e0b">El modelo no produjo evaluaciones estructuradas: no hay puntuaciones fiables para esta batalla. Solo se muestran métricas mecánicas (número y longitud de intervenciones), que NO son una valoración del debate. Esta batalla no cuenta para el Hall of Fame ni otorga premios.</p>` : ''}
|
| 625 |
${!heur && verdict?.justification ? `<p style="max-width:640px;margin:12px auto 0;font-size:.9rem;line-height:1.6">${esc(verdict.justification)}</p>` : ''}`;
|
| 626 |
+
document.querySelector('#mech-metrics')?.remove();
|
| 627 |
+
if (heur) {
|
| 628 |
+
// sin evaluación real no se presentan notas por criterio: solo "no disponible" + métricas mecánicas separadas
|
| 629 |
+
$('#score-table').innerHTML = `
|
| 630 |
+
<tr><th>Criterio (peso)</th>${totals.map(t => `<th style="color:${t.a.color}">${t.a.emoji} ${esc(t.a.name)}</th>`).join('')}</tr>
|
| 631 |
+
${WEIGHTS.map(([k, label, w]) => `<tr><td>${label} <span style="color:var(--muted)">(${w}%)</span></td>${totals.map(() => `<td style="color:var(--muted)">no disponible</td>`).join('')}</tr>`).join('')}
|
| 632 |
+
<tr style="font-weight:800"><td>TOTAL</td>${totals.map(() => `<td style="color:var(--muted)">—</td>`).join('')}</tr>`;
|
| 633 |
+
const mech = totals.map(t => {
|
| 634 |
+
const mine = battle.events.filter(e => e.agentId === t.a.id);
|
| 635 |
+
return `${t.a.emoji} ${esc(t.a.name)}: ${mine.length} intervenciones · ${mine.reduce((s, e) => s + e.content.length, 0)} caracteres`;
|
| 636 |
+
}).join(' · ');
|
| 637 |
+
$('#score-table').insertAdjacentHTML('afterend', `<p id="mech-metrics" style="color:var(--muted);font-size:.8rem;margin-top:8px">Métricas mecánicas (NO son una evaluación del debate): ${mech}</p>`);
|
| 638 |
+
} else {
|
| 639 |
+
$('#score-table').innerHTML = `
|
| 640 |
+
<tr><th>Criterio (peso)</th>${totals.map(t => `<th style="color:${t.a.color}">${t.a.emoji} ${esc(t.a.name)}</th>`).join('')}</tr>
|
| 641 |
+
${WEIGHTS.map(([k, label, w]) => `<tr><td>${label} <span style="color:var(--muted)">(${w}%)</span></td>${totals.map(t => `<td>${battle.scores[t.a.id][k] ?? '—'}</td>`).join('')}</tr>`).join('')}
|
| 642 |
+
<tr style="font-weight:800"><td>TOTAL</td>${totals.map(t => `<td style="color:${t.a.color}">${t.total}</td>`).join('')}</tr>`;
|
| 643 |
+
}
|
| 644 |
$('#judge-cards').innerHTML = battle.judgeReports.map(r => `
|
| 645 |
<div class="judge-card"><h4>${r.emoji} ${r.judge}</h4><p>${r.parsed ? esc(JSON.stringify(r.parsed)) : 'Evaluación heurística (respuesta no estructurada).'}</p></div>`).join('') +
|
| 646 |
`<div class="judge-card"><h4>👑 Head Judge</h4><p>${verdict?.justification ? esc(verdict.justification) : 'Veredicto por puntuación ponderada.'}</p>
|