diff --git a/llm_team_ui.py b/llm_team_ui.py index 73c57e2..6539262 100644 --- a/llm_team_ui.py +++ b/llm_team_ui.py @@ -6406,12 +6406,13 @@ def get_runs(): try: with get_db() as conn: with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur: + _cols = "id, mode, prompt, models_used, created_at, archived, quality_score, score_method, tags, notes, config->>'source' as source, config->>'parent_run' as parent_run" if show == "archived": - cur.execute("SELECT id, mode, prompt, models_used, created_at, archived FROM team_runs WHERE archived = true ORDER BY created_at DESC LIMIT 200") + cur.execute(f"SELECT {_cols} FROM team_runs WHERE archived = true ORDER BY created_at DESC LIMIT 200") elif show == "all": - cur.execute("SELECT id, mode, prompt, models_used, created_at, archived FROM team_runs ORDER BY created_at DESC LIMIT 200") + cur.execute(f"SELECT {_cols} FROM team_runs ORDER BY created_at DESC LIMIT 200") else: - cur.execute("SELECT id, mode, prompt, models_used, created_at, archived FROM team_runs WHERE archived = false ORDER BY created_at DESC LIMIT 50") + cur.execute(f"SELECT {_cols} FROM team_runs WHERE archived = false ORDER BY created_at DESC LIMIT 50") runs = cur.fetchall() for r in runs: r["created_at"] = r["created_at"].isoformat() @@ -6746,7 +6747,8 @@ h1 span{color:var(--accent)} .spacer{flex:1} .count-badge{font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--text2)} .run-table{width:100%} -.run-row{display:grid;grid-template-columns:30px 50px 90px 1fr 80px 100px 80px;gap:8px;padding:8px 10px;border-bottom:1px solid rgba(42,45,53,0.3);align-items:center;font-size:11px;cursor:pointer;transition:background 0.1s} +.run-row{display:grid;grid-template-columns:30px 50px 90px 1fr 80px 70px 100px 80px;gap:8px;padding:8px 10px;border-bottom:1px solid rgba(42,45,53,0.3);align-items:center;font-size:11px;cursor:pointer;transition:background 0.1s} +.run-score{font-family:'JetBrains Mono',monospace;font-size:11px;display:flex;align-items:center;gap:3px} .run-row:hover{background:rgba(226,181,90,0.03)} .run-row.archived{opacity:0.4} .run-hdr{font-family:'JetBrains Mono',monospace;font-size:8px;text-transform:uppercase;letter-spacing:1px;color:var(--text2);cursor:default;border-bottom:2px solid var(--border)} @@ -7029,6 +7031,21 @@ function renderTable(runs) { var tagsEl = document.createElement('span'); tagsEl.className = 'run-tags'; (r.tags||[]).forEach(function(t){ var pill = document.createElement('span'); pill.className = 'tag-pill'; pill.textContent = t; tagsEl.appendChild(pill); }); row.appendChild(tagsEl); + // Score + badges + var scoreEl = document.createElement('span'); scoreEl.className = 'run-score'; + if (r.quality_score) { + scoreEl.textContent = r.quality_score.toFixed(1); + scoreEl.style.color = r.quality_score >= 7 ? 'var(--green)' : r.quality_score >= 5 ? 'var(--accent)' : 'var(--red)'; + scoreEl.style.fontWeight = '700'; + } + if (r.source === 'optimize') { + var optPill = document.createElement('span'); + optPill.style.cssText = 'font-size:8px;background:rgba(79,70,229,0.1);color:var(--accent);padding:1px 5px;border-radius:2px;border:1px solid rgba(79,70,229,0.2);margin-left:4px'; + optPill.textContent = 'OPT'; + optPill.title = 'Generated by optimization from run #' + (r.parent_run||'?'); + scoreEl.appendChild(optPill); + } + row.appendChild(scoreEl); // Date var dateEl = document.createElement('span'); dateEl.className = 'run-date'; var dt = new Date(r.created_at); dateEl.textContent = dt.toLocaleDateString()+' '+dt.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); @@ -7056,6 +7073,22 @@ async function openDetail(id) { modeTag.textContent = run.mode + ' #' + id; hdr.appendChild(modeTag); var dateTag = document.createElement('span'); dateTag.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:9px;color:var(--text2)'; dateTag.textContent = new Date(run.created_at).toLocaleString(); hdr.appendChild(dateTag); + // Score badge in header + if (run.quality_score) { + var scoreBadge = document.createElement('span'); + var sc = run.quality_score; + scoreBadge.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:14px;font-weight:700;margin-left:auto;padding:4px 12px;border:2px solid;border-radius:2px;' + + 'color:' + (sc >= 7 ? 'var(--green)' : sc >= 5 ? 'var(--accent)' : 'var(--red)') + ';' + + 'border-color:' + (sc >= 7 ? 'var(--green)' : sc >= 5 ? 'var(--accent)' : 'var(--red)'); + scoreBadge.textContent = sc.toFixed(1) + '/10'; + if (run.score_method) { + var method = document.createElement('span'); + method.style.cssText = 'font-size:8px;text-transform:uppercase;letter-spacing:0.5px;opacity:0.6;margin-left:6px'; + method.textContent = run.score_method === 'auto' ? 'auto' : run.score_method === 'user_up' ? '\u{1F44D}' : '\u{1F44E}'; + scoreBadge.appendChild(method); + } + hdr.appendChild(scoreBadge); + } panel.appendChild(hdr); // Prompt @@ -7107,38 +7140,108 @@ async function openDetail(id) { actions.appendChild(optBtn); panel.appendChild(actions); - // Optimization history - if (isOptimized) { - var optSection = document.createElement('div'); - optSection.style.cssText = 'background:var(--glow);border:1px solid var(--accent);border-radius:2px;padding:12px;margin-bottom:12px'; + // Optimization history — always load, show if data exists + var optSection = document.createElement('div'); + optSection.style.cssText = 'margin-bottom:12px'; + fetch('/api/optimize-history/' + id).then(function(r){return r.json()}).then(function(d){ + var results = d.results || []; + var children = d.children || []; + if (!results.length && !children.length) return; + + optSection.style.cssText = 'background:var(--glow);border:1px solid var(--accent);border-radius:2px;padding:14px;margin-bottom:12px'; + + // Header with count + var optHdr = document.createElement('div'); + optHdr.style.cssText = 'display:flex;align-items:center;gap:8px;margin-bottom:10px;cursor:pointer'; var optTitle = document.createElement('div'); - optTitle.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:9px;text-transform:uppercase;letter-spacing:1.5px;color:var(--accent);margin-bottom:6px;font-weight:700'; - optTitle.textContent = 'Optimization Results'; - optSection.appendChild(optTitle); - var optInfo = document.createElement('div'); - optInfo.style.cssText = 'font-size:12px;color:var(--text);line-height:1.6'; - var bestId = meta.best_variation_run; - var jobId = meta.optimize_job || ''; - optInfo.textContent = 'Best variation: Run #' + (bestId||'?') + ' | Job: ' + jobId; - optSection.appendChild(optInfo); - if (bestId) { - var viewBtn = document.createElement('button'); viewBtn.className = 'tool-btn'; - viewBtn.style.cssText = 'margin-top:8px;font-size:9px'; - viewBtn.textContent = 'View Best Variation (#' + bestId + ')'; - viewBtn.onclick = function(){ openDetail(bestId); }; - optSection.appendChild(viewBtn); - } - // Load pipeline results for more detail - fetch('/api/optimize-history/' + id).then(function(r){return r.json()}).then(function(d){ - if (d.results && d.results.length) { - var count = document.createElement('div'); - count.style.cssText = 'font-size:11px;color:var(--text2);margin-top:6px;font-family:JetBrains Mono,monospace'; - count.textContent = d.results.length + ' optimization(s) run | Best: ' + (d.results[0].best_score||'?') + '/10 | Original: ' + (d.results[0].original_score||'?') + '/10'; - optSection.appendChild(count); + optTitle.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px;text-transform:uppercase;letter-spacing:1.5px;color:var(--accent);font-weight:700'; + optTitle.textContent = '\u26A1 Optimization History (' + results.length + ' run' + (results.length !== 1 ? 's' : '') + ', ' + children.length + ' variation' + (children.length !== 1 ? 's' : '') + ')'; + optHdr.appendChild(optTitle); + optSection.appendChild(optHdr); + + // Each optimization run + results.forEach(function(res, ri) { + var runCard = document.createElement('div'); + runCard.style.cssText = 'background:rgba(0,0,0,0.15);border:1px solid var(--border);border-radius:2px;padding:10px;margin-bottom:6px'; + var runHdr = document.createElement('div'); + runHdr.style.cssText = 'display:flex;align-items:center;gap:8px;font-family:JetBrains Mono,monospace;font-size:10px'; + var runLabel = document.createElement('span'); + runLabel.style.cssText = 'color:var(--accent);font-weight:700'; + runLabel.textContent = 'Run ' + (ri+1); + runHdr.appendChild(runLabel); + var bestBadge = document.createElement('span'); + bestBadge.style.cssText = 'color:var(--green);font-weight:700'; + bestBadge.textContent = 'Best: ' + (res.best_score||'?') + '/10'; + runHdr.appendChild(bestBadge); + var origBadge = document.createElement('span'); + origBadge.style.cssText = 'color:var(--text2)'; + origBadge.textContent = 'Original: ' + (res.original_score||'?') + '/10'; + runHdr.appendChild(origBadge); + if (res.improvement > 0) { + var impBadge = document.createElement('span'); + impBadge.style.cssText = 'color:var(--green)'; + impBadge.textContent = '+' + res.improvement.toFixed(1); + runHdr.appendChild(impBadge); } - }).catch(function(){}); - panel.appendChild(optSection); - } + var callsBadge = document.createElement('span'); + callsBadge.style.cssText = 'color:var(--text2);margin-left:auto'; + callsBadge.textContent = (res.calls_used||'?') + ' calls'; + runHdr.appendChild(callsBadge); + if (res.completed_at) { + var dateBadge = document.createElement('span'); + dateBadge.style.cssText = 'color:var(--text2);font-size:9px'; + dateBadge.textContent = new Date(res.completed_at).toLocaleDateString(); + runHdr.appendChild(dateBadge); + } + runCard.appendChild(runHdr); + + // Ranked variations for this run + var ranked = res.ranked || []; + ranked.forEach(function(v, vi) { + var vRow = document.createElement('div'); + vRow.style.cssText = 'display:flex;align-items:center;gap:8px;padding:4px 0;font-size:11px;border-top:1px solid rgba(42,45,53,0.2);margin-top:4px'; + if (vi === 0) vRow.style.borderLeft = '2px solid var(--green)'; + var rank = document.createElement('span'); + rank.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px;font-weight:700;color:' + (vi === 0 ? 'var(--green)' : 'var(--text2)') + ';min-width:20px'; + rank.textContent = vi === 0 ? '\u2605' : '#' + (vi+1); + vRow.appendChild(rank); + var strat = document.createElement('span'); + strat.style.cssText = 'font-size:9px;color:var(--accent);font-family:JetBrains Mono,monospace;text-transform:uppercase;min-width:60px'; + strat.textContent = v.strategy || '?'; + vRow.appendChild(strat); + var vMode = document.createElement('span'); + vMode.style.cssText = 'font-size:9px;color:var(--text2);font-family:JetBrains Mono,monospace;min-width:70px'; + vMode.textContent = v.mode || ''; + vRow.appendChild(vMode); + var vScore = document.createElement('span'); + vScore.style.cssText = 'font-family:JetBrains Mono,monospace;font-weight:700;color:' + ((v.score||0) >= 7 ? 'var(--green)' : 'var(--accent)'); + vScore.textContent = v.score ? v.score.toFixed(1) : '?'; + vRow.appendChild(vScore); + var vSnippet = document.createElement('span'); + vSnippet.style.cssText = 'flex:1;color:var(--text2);font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'; + vSnippet.textContent = (v.snippet || v.prompt || '').substring(0, 80); + vRow.appendChild(vSnippet); + if (v.run_id) { + var viewBtn = document.createElement('button'); + viewBtn.style.cssText = 'font-size:8px;padding:2px 6px;cursor:pointer;background:none;border:1px solid var(--border);color:var(--text2);font-family:JetBrains Mono,monospace;border-radius:2px'; + viewBtn.textContent = 'View'; + viewBtn.onclick = function(e){ e.stopPropagation(); openDetail(v.run_id); }; + vRow.appendChild(viewBtn); + } + if (vi === 0 && v.prompt) { + var useBtn = document.createElement('button'); + useBtn.style.cssText = 'font-size:8px;padding:2px 6px;cursor:pointer;background:none;border:1px solid var(--accent);color:var(--accent);font-family:JetBrains Mono,monospace;border-radius:2px'; + useBtn.textContent = 'Use'; + useBtn.onclick = function(e){ e.stopPropagation(); sessionStorage.setItem('pending-prompt', v.prompt); window.location.href = '/'; }; + vRow.appendChild(useBtn); + } + runCard.appendChild(vRow); + }); + + optSection.appendChild(runCard); + }); + }).catch(function(){}); + panel.appendChild(optSection); // Responses var responses = run.responses || [];