fivetech commited on
Commit
5e23ca3
·
verified ·
1 Parent(s): a0fe3c1

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +56 -10
app.js CHANGED
@@ -77,6 +77,26 @@ function applyAwardsAndMilestones(ps, winner, winnerTeam) {
77
  stats[winner.id].awards.master_debater = (stats[winner.id].awards.master_debater || 0) + 1;
78
  }
79
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  const ADAPTIVE_CYCLE = ['attack', 'defense', 'evidence', 'socratic', 'counterattack'];
81
  const PHASES = [
82
  { id: 'opening', label: 'Opening' },
@@ -283,6 +303,7 @@ function renderRoster() {
283
  <h3>${esc(a.name)}</h3>
284
  <div class="spec">${esc(a.specialty)}</div>
285
  <div class="style">${esc(a.style)} · estrategia ${a.strategy.toUpperCase()}</div>
 
286
  ${[['Lógica', a.stats.logic], ['Creatividad', a.stats.creativity], ['Agresividad', a.stats.aggression]].map(([k, v]) =>
287
  `<div class="stat"><span>${k}</span><div class="bar"><div style="width:${v}%"></div></div><b>${v}</b></div>`).join('')}
288
  <div class="record">🏆 ${st.wins}V · ${st.losses}D · ${st.draws}E · media ${avg} · ${st.matches} batallas</div>
@@ -512,6 +533,25 @@ function addSystemMsg(text) {
512
  div.innerHTML = `<div class="mbody" style="width:100%"><div class="mtext">${esc(text)}</div></div>`;
513
  log.appendChild(div); log.scrollTop = log.scrollHeight;
514
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  async function agentIntervention(agent, phase, round, extraInstr) {
516
  const strat = strategyOf(agent, round);
517
  const system = agent.system + ' ' + (STRATEGY_TEXT[strat] || '') + ' ' + RULES;
@@ -529,8 +569,9 @@ async function agentIntervention(agent, phase, round, extraInstr) {
529
  const rivals = battle.participants.filter(x => x.team !== agent.team).map(x => x.name);
530
  teamCtx = `\nEstás en el EQUIPO ${agent.team}${mates.length ? ' junto a ' + mates.join(', ') : ''}. Equipo rival (EQUIPO ${agent.team === 'A' ? 'B' : 'A'}): ${rivals.join(', ')}. Coordina con tu equipo: refuerza y extiende los puntos de tus compañeros, no los contradigas; concentra tus ataques en el equipo rival.`;
531
  }
 
532
  const rmCtx = battle.rematch ? `\nREVANCHA: ya se disputó una batalla anterior sobre este tema con estos participantes.\n${battle.rematch}\nMantén tu personalidad y estilo, pero AJUSTA tu estrategia: corrige los errores señalados, refuerza lo que funcionó y no repitas literalmente tus intervenciones anteriores.\n` : '';
533
- const user = `Tema del debate: ${battle.topic}\nRonda ${round} de ${battle.rounds}.\n${phaseInstr}\n${extraInstr || ''}${teamCtx}${rmCtx}\n\n` +
534
  (view ? `Intervenciones hasta ahora:\n${view}\n\n` : 'Aún no hay intervenciones.\n\n') +
535
  `Ahora intervienes tú, ${agent.name}.`;
536
  const div = document.createElement('div');
@@ -815,8 +856,12 @@ function finalizeBattle(verdict) {
815
  battle.winnerTeam = winnerTeam;
816
  battle.verdict = verdict;
817
  battle.finishedAt = new Date().toISOString();
818
- // stats: solo cuentan batallas con evaluación real del modelo (ni heurísticas ni simuladas)
819
- if (!battle.heuristicEval && !MOCK) {
 
 
 
 
820
  ps.forEach(a => {
821
  const st = stats[a.id] = stats[a.id] || { wins: 0, losses: 0, draws: 0, scoreSum: 0, matches: 0, awards: {} };
822
  st.matches++;
@@ -832,7 +877,7 @@ function finalizeBattle(verdict) {
832
  applyAwardsAndMilestones(ps, winner, winnerTeam);
833
  }
834
  // guardar batalla (Arena Memory)
835
- matches.unshift({ id: battle.id, topic: battle.topic, mode: battle.mode, rounds: battle.rounds, participants: ps.map(a => a.id), teams: battle.mode === 'team' ? Object.fromEntries(ps.map(a => [a.id, a.team])) : undefined, events: battle.events, scores: battle.scores, winner: battle.winner, winnerTeam, verdict, heuristic: !!battle.heuristicEval, simulated: MOCK || undefined, engine: MOCK ? 'simulated' : 'wllama', date: battle.finishedAt });
836
  if (matches.length > 50) matches.length = 50;
837
  persistAll();
838
  renderResults(totals, winner, verdict);
@@ -885,11 +930,12 @@ function renderTournamentResults(champ) {
885
  function renderResults(totals, winner, verdict) {
886
  const heur = !!battle.heuristicEval;
887
  $('#verdict-hero').innerHTML = `
888
- <div class="crown">${heur ? '⚠️' : '👑'}</div>
889
- <h1 style="font-size:1.8rem;font-weight:900">${heur ? '⚠️ Evaluación no fiable' : (battle.mode === 'team' ? (battle.winnerTeam ? `🛡️ EQUIPO ${battle.winnerTeam} gana la batalla` : '🤝 EMPATE ENTRE EQUIPOS') : (winner ? `<span style="color:${winner.color}">${winner.emoji} ${esc(winner.name)}</span> gana la batalla` : '🤝 EMPATE'))}</h1>
890
  ${!heur && battle.mode === 'team' ? `<p style="margin-top:6px;font-size:.95rem">Equipo A <b>${teamTotals().A}</b> — Equipo B <b>${teamTotals().B}</b></p>` : ''}
891
  <p style="color:var(--muted);margin-top:8px;font-size:.9rem">${esc(battle.topic)}</p>
892
  ${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>` : ''}
 
893
  ${!heur && verdict?.justification ? `<p style="max-width:640px;margin:12px auto 0;font-size:.9rem;line-height:1.6">${esc(verdict.justification)}</p>` : ''}
894
  ${MOCK ? '<p style="margin-top:10px"><span class="mock-pill">🧪 Resultado SIMULADO (pruebas, sin modelo real)</span></p>' : ''}`;
895
  document.querySelector('#mech-metrics')?.remove();
@@ -918,8 +964,8 @@ function renderResults(totals, winner, verdict) {
918
  ${verdict?.decisive ? `<p style="margin-top:6px"><b>Decisivo:</b> ${verdict.decisive.map(esc).join(' · ')}</p>` : ''}
919
  ${verdict?.mistakes ? `<p style="margin-top:6px"><b>Errores:</b> ${verdict.mistakes.map(esc).join(' · ')}</p>` : ''}</div>`;
920
  // awards por criterio: solo con evaluación real; la heurística no otorga méritos
921
- if (heur) {
922
- $('#awards').innerHTML = '<span class="award">⚠️ Sin premios: evaluación no disponible</span>';
923
  } else {
924
  const awards = [`<span class="award">🏆 ${battle.mode === 'team' ? (battle.winnerTeam ? 'Equipo ' + battle.winnerTeam + ' — Master Debaters' : 'Empate honorable') : (winner ? esc(winner.name) + ' — Master Debater' : 'Empate honorable')}</span>`];
925
  [['logica', '🧠 Logic Master'], ['evidencia', '📚 Evidence Champion'], ['refutacion', '🛡️ Best Refutation']].forEach(([k, label]) => {
@@ -1119,7 +1165,7 @@ function renderHall() {
1119
  return `<div class="match-item" data-match="${m.id}">
1120
  <div class="mtopic">${esc(m.topic)}</div>
1121
  <div class="mmeta">${MODES.find(x => x.id === m.mode)?.name || m.mode} · ${m.events.length} intervenciones · ${new Date(m.date).toLocaleString('es')}</div>
1122
- <div class="mmeta">${m.simulated ? '🧪 Resultado simulado (pruebas, sin modelo real)' : m.heuristic ? '⚠️ sin evaluación fiable (no cuenta para el Hall)' : (m.mode === 'tournament' ? '🏆 Campeón: ' + (w ? esc(w.name) : '—') : m.winnerTeam ? '🛡️ Equipo ' + m.winnerTeam : '👑 ' + (w ? esc(w.name) : 'Empate'))}</div>
1123
  <button class="btn ghost small" data-replay="${m.id}">▶ Replay</button>
1124
  </div>`;
1125
  }).join('') : '<p style="color:var(--muted);font-size:.85rem">Aún no hay batallas guardadas.</p>';
@@ -1180,5 +1226,5 @@ if (params.get('topic')) $('#topic').value = params.get('topic');
1180
 
1181
  // gancho de pruebas (solo modo mock): inspección de estado interno
1182
  IDB.init();
1183
- if (MOCK) window.__arenaTest = { setBattle: b => { battle = b; }, getBattle: () => battle, agents: () => agents, stats: () => stats, finalizeBattle, applyAwardsAndMilestones };
1184
  updateModelInUse();
 
77
  stats[winner.id].awards.master_debater = (stats[winner.id].awards.master_debater || 0) + 1;
78
  }
79
  }
80
+ /* Calidad del veredicto: la RUTA fiable (!heuristic && !MOCK) no acredita CALIDAD.
81
+ Esquema: nota numérica 0-10 para cada participante y criterio.
82
+ Consistencia: el ganador declarado por el veredicto debe estar entre los máximos totales. */
83
+ function evaluationQualityOK(scores, verdict, ps, mode, winner) {
84
+ for (const a of ps) {
85
+ const sa = scores?.[a.id];
86
+ if (!sa) return false;
87
+ for (const [k] of WEIGHTS) {
88
+ const v = sa[k];
89
+ if (typeof v !== 'number' || isNaN(v) || v < 0 || v > 10) return false;
90
+ }
91
+ }
92
+ if (mode !== 'team' && verdict?.winner && verdict.winner !== 'Empate' && winner) {
93
+ const totals = ps.map(a => totalScore(a.id, scores));
94
+ const top = Math.max(...totals);
95
+ const topIds = ps.filter((a, i) => totals[i] === top).map(a => a.id);
96
+ if (!topIds.includes(winner.id)) return false;
97
+ }
98
+ return true;
99
+ }
100
  const ADAPTIVE_CYCLE = ['attack', 'defense', 'evidence', 'socratic', 'counterattack'];
101
  const PHASES = [
102
  { id: 'opening', label: 'Opening' },
 
303
  <h3>${esc(a.name)}</h3>
304
  <div class="spec">${esc(a.specialty)}</div>
305
  <div class="style">${esc(a.style)} · estrategia ${a.strategy.toUpperCase()}</div>
306
+ ${realBattleCount(a.id) ? `<div class="style" style="color:var(--accent2)">🧠 memoria: ${realBattleCount(a.id)} batalla(s) real(es)</div>` : ''}
307
  ${[['Lógica', a.stats.logic], ['Creatividad', a.stats.creativity], ['Agresividad', a.stats.aggression]].map(([k, v]) =>
308
  `<div class="stat"><span>${k}</span><div class="bar"><div style="width:${v}%"></div></div><b>${v}</b></div>`).join('')}
309
  <div class="record">🏆 ${st.wins}V · ${st.losses}D · ${st.draws}E · media ${avg} · ${st.matches} batallas</div>
 
533
  div.innerHTML = `<div class="mbody" style="width:100%"><div class="mtext">${esc(text)}</div></div>`;
534
  log.appendChild(div); log.scrollTop = log.scrollHeight;
535
  }
536
+ function agentMemory(agentId) {
537
+ // memoria persistente: SOLO batallas con motor y evaluación verificados (nunca mock/heurística/inconsistente ni registros legacy sin motor)
538
+ const real = matches.filter(m => m.engine === 'wllama' && !m.heuristic && !m.inconsistent && m.participants.includes(agentId));
539
+ if (!real.length) return '';
540
+ const st = stats[agentId];
541
+ const parts = [];
542
+ if (st && st.matches) parts.push(`${st.matches} batallas evaluadas (${st.wins} victorias, ${st.losses} derrotas, ${st.draws} empates)`);
543
+ const recent = real.slice(0, 3).map(m => {
544
+ const res = m.mode === 'tournament' ? (m.winner === agentId ? 'ganaste el torneo' : 'no ganaste el torneo')
545
+ : m.winnerTeam ? ((m.teams && m.teams[agentId]) === m.winnerTeam ? 'ganó tu equipo' : 'perdió tu equipo')
546
+ : m.winner === agentId ? 'ganaste' : (m.winner ? 'perdiste' : 'empate');
547
+ return `«${m.topic}»: ${res}`;
548
+ });
549
+ if (recent.length) parts.push('últimas: ' + recent.join(' · '));
550
+ return parts.length ? `\nTu historial en esta arena (memoria persistente): ${parts.join('. ')}. Aprende de tus batallas anteriores: mantén lo que te dio victorias y corrige lo que te hizo perder.\n` : '';
551
+ }
552
+ function realBattleCount(agentId) {
553
+ return matches.filter(m => m.engine === 'wllama' && !m.heuristic && !m.inconsistent && m.participants.includes(agentId)).length;
554
+ }
555
  async function agentIntervention(agent, phase, round, extraInstr) {
556
  const strat = strategyOf(agent, round);
557
  const system = agent.system + ' ' + (STRATEGY_TEXT[strat] || '') + ' ' + RULES;
 
569
  const rivals = battle.participants.filter(x => x.team !== agent.team).map(x => x.name);
570
  teamCtx = `\nEstás en el EQUIPO ${agent.team}${mates.length ? ' junto a ' + mates.join(', ') : ''}. Equipo rival (EQUIPO ${agent.team === 'A' ? 'B' : 'A'}): ${rivals.join(', ')}. Coordina con tu equipo: refuerza y extiende los puntos de tus compañeros, no los contradigas; concentra tus ataques en el equipo rival.`;
571
  }
572
+ const memCtx = agentMemory(agent.id);
573
  const rmCtx = battle.rematch ? `\nREVANCHA: ya se disputó una batalla anterior sobre este tema con estos participantes.\n${battle.rematch}\nMantén tu personalidad y estilo, pero AJUSTA tu estrategia: corrige los errores señalados, refuerza lo que funcionó y no repitas literalmente tus intervenciones anteriores.\n` : '';
574
+ const user = `Tema del debate: ${battle.topic}\nRonda ${round} de ${battle.rounds}.\n${phaseInstr}\n${extraInstr || ''}${teamCtx}${rmCtx}${memCtx}\n\n` +
575
  (view ? `Intervenciones hasta ahora:\n${view}\n\n` : 'Aún no hay intervenciones.\n\n') +
576
  `Ahora intervienes tú, ${agent.name}.`;
577
  const div = document.createElement('div');
 
856
  battle.winnerTeam = winnerTeam;
857
  battle.verdict = verdict;
858
  battle.finishedAt = new Date().toISOString();
859
+ // calidad del veredicto: esquema + consistencia; sin ella no cuenta para stats/logros aunque la ruta fuese real
860
+ const qualityOK = !battle.heuristicEval && !MOCK && evaluationQualityOK(battle.scores, verdict, ps, battle.mode, winner);
861
+ battle.evalInconsistent = !battle.heuristicEval && !MOCK && !qualityOK;
862
+ if (battle.evalInconsistent) { winner = null; battle.winner = null; battle.winnerTeam = null; }
863
+ // stats: solo cuentan batallas con evaluación real Y consistente del modelo (ni heurísticas ni simuladas ni inconsistentes)
864
+ if (qualityOK) {
865
  ps.forEach(a => {
866
  const st = stats[a.id] = stats[a.id] || { wins: 0, losses: 0, draws: 0, scoreSum: 0, matches: 0, awards: {} };
867
  st.matches++;
 
877
  applyAwardsAndMilestones(ps, winner, winnerTeam);
878
  }
879
  // guardar batalla (Arena Memory)
880
+ matches.unshift({ id: battle.id, topic: battle.topic, mode: battle.mode, rounds: battle.rounds, participants: ps.map(a => a.id), teams: battle.mode === 'team' ? Object.fromEntries(ps.map(a => [a.id, a.team])) : undefined, events: battle.events, scores: battle.scores, winner: battle.winner, winnerTeam, verdict, heuristic: !!battle.heuristicEval, inconsistent: !!battle.evalInconsistent || undefined, simulated: MOCK || undefined, engine: MOCK ? 'simulated' : 'wllama', date: battle.finishedAt });
881
  if (matches.length > 50) matches.length = 50;
882
  persistAll();
883
  renderResults(totals, winner, verdict);
 
930
  function renderResults(totals, winner, verdict) {
931
  const heur = !!battle.heuristicEval;
932
  $('#verdict-hero').innerHTML = `
933
+ <div class="crown">${heur || battle.evalInconsistent ? '⚠️' : '👑'}</div>
934
+ <h1 style="font-size:1.8rem;font-weight:900">${heur ? '⚠️ Evaluación no fiable' : battle.evalInconsistent ? '⚠️ Veredicto inconsistente (no superó la validación)' : (battle.mode === 'team' ? (battle.winnerTeam ? `🛡️ EQUIPO ${battle.winnerTeam} gana la batalla` : '🤝 EMPATE ENTRE EQUIPOS') : (winner ? `<span style="color:${winner.color}">${winner.emoji} ${esc(winner.name)}</span> gana la batalla` : '🤝 EMPATE'))}</h1>
935
  ${!heur && battle.mode === 'team' ? `<p style="margin-top:6px;font-size:.95rem">Equipo A <b>${teamTotals().A}</b> — Equipo B <b>${teamTotals().B}</b></p>` : ''}
936
  <p style="color:var(--muted);margin-top:8px;font-size:.9rem">${esc(battle.topic)}</p>
937
  ${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>` : ''}
938
+ ${battle.evalInconsistent ? `<p style="max-width:660px;margin:12px auto 0;font-size:.88rem;line-height:1.6;color:#f59e0b">El veredicto del modelo no superó la validación de esquema/consistencia (ganador declarado ≠ puntuaciones o notas fuera de rango): esta batalla no cuenta para el Hall of Fame ni otorga premios.</p>` : ''}
939
  ${!heur && verdict?.justification ? `<p style="max-width:640px;margin:12px auto 0;font-size:.9rem;line-height:1.6">${esc(verdict.justification)}</p>` : ''}
940
  ${MOCK ? '<p style="margin-top:10px"><span class="mock-pill">🧪 Resultado SIMULADO (pruebas, sin modelo real)</span></p>' : ''}`;
941
  document.querySelector('#mech-metrics')?.remove();
 
964
  ${verdict?.decisive ? `<p style="margin-top:6px"><b>Decisivo:</b> ${verdict.decisive.map(esc).join(' · ')}</p>` : ''}
965
  ${verdict?.mistakes ? `<p style="margin-top:6px"><b>Errores:</b> ${verdict.mistakes.map(esc).join(' · ')}</p>` : ''}</div>`;
966
  // awards por criterio: solo con evaluación real; la heurística no otorga méritos
967
+ if (heur || battle.evalInconsistent) {
968
+ $('#awards').innerHTML = '<span class="award">⚠️ Sin premios: evaluación no fiable o inconsistente</span>';
969
  } else {
970
  const awards = [`<span class="award">🏆 ${battle.mode === 'team' ? (battle.winnerTeam ? 'Equipo ' + battle.winnerTeam + ' — Master Debaters' : 'Empate honorable') : (winner ? esc(winner.name) + ' — Master Debater' : 'Empate honorable')}</span>`];
971
  [['logica', '🧠 Logic Master'], ['evidencia', '📚 Evidence Champion'], ['refutacion', '🛡️ Best Refutation']].forEach(([k, label]) => {
 
1165
  return `<div class="match-item" data-match="${m.id}">
1166
  <div class="mtopic">${esc(m.topic)}</div>
1167
  <div class="mmeta">${MODES.find(x => x.id === m.mode)?.name || m.mode} · ${m.events.length} intervenciones · ${new Date(m.date).toLocaleString('es')}</div>
1168
+ <div class="mmeta">${m.simulated ? '🧪 Resultado simulado (pruebas, sin modelo real)' : m.heuristic ? '⚠️ sin evaluación fiable (no cuenta para el Hall)' : m.inconsistent ? '⚠️ veredicto inconsistente (no cuenta para el Hall)' : (m.mode === 'tournament' ? '🏆 Campeón: ' + (w ? esc(w.name) : '—') : m.winnerTeam ? '🛡️ Equipo ' + m.winnerTeam : '👑 ' + (w ? esc(w.name) : 'Empate'))}</div>
1169
  <button class="btn ghost small" data-replay="${m.id}">▶ Replay</button>
1170
  </div>`;
1171
  }).join('') : '<p style="color:var(--muted);font-size:.85rem">Aún no hay batallas guardadas.</p>';
 
1226
 
1227
  // gancho de pruebas (solo modo mock): inspección de estado interno
1228
  IDB.init();
1229
+ if (MOCK) window.__arenaTest = { setBattle: b => { battle = b; }, getBattle: () => battle, agents: () => agents, stats: () => stats, finalizeBattle, applyAwardsAndMilestones, evaluationQualityOK, agentMemory };
1230
  updateModelInUse();