eval_review.html 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Eval Set Review - __SKILL_NAME_PLACEHOLDER__</title>
  7. <link rel="preconnect" href="https://fonts.googleapis.com">
  8. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  9. <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
  10. <style>
  11. * { box-sizing: border-box; margin: 0; padding: 0; }
  12. body { font-family: 'Lora', Georgia, serif; background: #faf9f5; padding: 2rem; color: #141413; }
  13. h1 { font-family: 'Poppins', sans-serif; margin-bottom: 0.5rem; font-size: 1.5rem; }
  14. .description { color: #b0aea5; margin-bottom: 1.5rem; font-style: italic; max-width: 900px; }
  15. .controls { margin-bottom: 1rem; display: flex; gap: 0.5rem; }
  16. .btn { font-family: 'Poppins', sans-serif; padding: 0.5rem 1rem; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem; font-weight: 500; }
  17. .btn-add { background: #6a9bcc; color: white; }
  18. .btn-add:hover { background: #5889b8; }
  19. .btn-export { background: #d97757; color: white; }
  20. .btn-export:hover { background: #c4613f; }
  21. table { width: 100%; max-width: 1100px; border-collapse: collapse; background: white; border-radius: 6px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
  22. th { font-family: 'Poppins', sans-serif; background: #141413; color: #faf9f5; padding: 0.75rem 1rem; text-align: left; font-size: 0.875rem; }
  23. td { padding: 0.75rem 1rem; border-bottom: 1px solid #e8e6dc; vertical-align: top; }
  24. tr:nth-child(even) td { background: #faf9f5; }
  25. tr:hover td { background: #f3f1ea; }
  26. .section-header td { background: #e8e6dc; font-family: 'Poppins', sans-serif; font-weight: 500; font-size: 0.8rem; color: #141413; text-transform: uppercase; letter-spacing: 0.05em; }
  27. .query-input { width: 100%; padding: 0.4rem; border: 1px solid #e8e6dc; border-radius: 4px; font-size: 0.875rem; font-family: 'Lora', Georgia, serif; resize: vertical; min-height: 60px; }
  28. .query-input:focus { outline: none; border-color: #d97757; box-shadow: 0 0 0 2px rgba(217,119,87,0.15); }
  29. .toggle { position: relative; display: inline-block; width: 44px; height: 24px; }
  30. .toggle input { opacity: 0; width: 0; height: 0; }
  31. .toggle .slider { position: absolute; inset: 0; background: #b0aea5; border-radius: 24px; cursor: pointer; transition: 0.2s; }
  32. .toggle .slider::before { content: ""; position: absolute; width: 18px; height: 18px; left: 3px; bottom: 3px; background: white; border-radius: 50%; transition: 0.2s; }
  33. .toggle input:checked + .slider { background: #d97757; }
  34. .toggle input:checked + .slider::before { transform: translateX(20px); }
  35. .btn-delete { background: #c44; color: white; padding: 0.3rem 0.6rem; border: none; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-family: 'Poppins', sans-serif; }
  36. .btn-delete:hover { background: #a33; }
  37. .summary { margin-top: 1rem; color: #b0aea5; font-size: 0.875rem; }
  38. </style>
  39. </head>
  40. <body>
  41. <h1>Eval Set Review: <span id="skill-name">__SKILL_NAME_PLACEHOLDER__</span></h1>
  42. <p class="description">Current description: <span id="skill-desc">__SKILL_DESCRIPTION_PLACEHOLDER__</span></p>
  43. <div class="controls">
  44. <button class="btn btn-add" onclick="addRow()">+ Add Query</button>
  45. <button class="btn btn-export" onclick="exportEvalSet()">Export Eval Set</button>
  46. </div>
  47. <table>
  48. <thead>
  49. <tr>
  50. <th style="width:65%">Query</th>
  51. <th style="width:18%">Should Trigger</th>
  52. <th style="width:10%">Actions</th>
  53. </tr>
  54. </thead>
  55. <tbody id="eval-body"></tbody>
  56. </table>
  57. <p class="summary" id="summary"></p>
  58. <script>
  59. const EVAL_DATA = __EVAL_DATA_PLACEHOLDER__;
  60. let evalItems = [...EVAL_DATA];
  61. function render() {
  62. const tbody = document.getElementById('eval-body');
  63. tbody.innerHTML = '';
  64. // Sort: should-trigger first, then should-not-trigger
  65. const sorted = evalItems
  66. .map((item, origIdx) => ({ ...item, origIdx }))
  67. .sort((a, b) => (b.should_trigger ? 1 : 0) - (a.should_trigger ? 1 : 0));
  68. let lastGroup = null;
  69. sorted.forEach(item => {
  70. const group = item.should_trigger ? 'trigger' : 'no-trigger';
  71. if (group !== lastGroup) {
  72. const headerRow = document.createElement('tr');
  73. headerRow.className = 'section-header';
  74. headerRow.innerHTML = `<td colspan="3">${item.should_trigger ? 'Should Trigger' : 'Should NOT Trigger'}</td>`;
  75. tbody.appendChild(headerRow);
  76. lastGroup = group;
  77. }
  78. const idx = item.origIdx;
  79. const tr = document.createElement('tr');
  80. tr.innerHTML = `
  81. <td><textarea class="query-input" onchange="updateQuery(${idx}, this.value)">${escapeHtml(item.query)}</textarea></td>
  82. <td>
  83. <label class="toggle">
  84. <input type="checkbox" ${item.should_trigger ? 'checked' : ''} onchange="updateTrigger(${idx}, this.checked)">
  85. <span class="slider"></span>
  86. </label>
  87. <span style="margin-left:8px;font-size:0.8rem;color:#b0aea5">${item.should_trigger ? 'Yes' : 'No'}</span>
  88. </td>
  89. <td><button class="btn-delete" onclick="deleteRow(${idx})">Delete</button></td>
  90. `;
  91. tbody.appendChild(tr);
  92. });
  93. updateSummary();
  94. }
  95. function escapeHtml(text) {
  96. const div = document.createElement('div');
  97. div.textContent = text;
  98. return div.innerHTML;
  99. }
  100. function updateQuery(idx, value) { evalItems[idx].query = value; updateSummary(); }
  101. function updateTrigger(idx, value) { evalItems[idx].should_trigger = value; render(); }
  102. function deleteRow(idx) { evalItems.splice(idx, 1); render(); }
  103. function addRow() {
  104. evalItems.push({ query: '', should_trigger: true });
  105. render();
  106. const inputs = document.querySelectorAll('.query-input');
  107. inputs[inputs.length - 1].focus();
  108. }
  109. function updateSummary() {
  110. const trigger = evalItems.filter(i => i.should_trigger).length;
  111. const noTrigger = evalItems.filter(i => !i.should_trigger).length;
  112. document.getElementById('summary').textContent =
  113. `${evalItems.length} queries total: ${trigger} should trigger, ${noTrigger} should not trigger`;
  114. }
  115. function exportEvalSet() {
  116. const valid = evalItems.filter(i => i.query.trim() !== '');
  117. const data = valid.map(i => ({ query: i.query.trim(), should_trigger: i.should_trigger }));
  118. const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  119. const url = URL.createObjectURL(blob);
  120. const a = document.createElement('a');
  121. a.href = url;
  122. a.download = 'eval_set.json';
  123. document.body.appendChild(a);
  124. a.click();
  125. document.body.removeChild(a);
  126. URL.revokeObjectURL(url);
  127. }
  128. render();
  129. </script>
  130. </body>
  131. </html>