Add optimization history, reconnect, and duplicate prevention

History detail panel now shows optimization results:
- If a run has been optimized, shows results section with best score,
  original score, and link to view the winning variation
- Fetches full optimization history via GET /api/optimize-history/<id>
- Shows count of optimizations run and child variation count
- Button changes to "Re-Optimize" for already-optimized runs

Reconnect to active optimizations:
- If optimization is already running, returns job_id in error response
- Frontend detects this and reconnects to the SSE stream
- No more losing progress when navigating away and coming back
- Refactored startOptimize() into startOptimize() + _showOptimizeStream()

New endpoint: GET /api/optimize-history/<run_id>
- Returns all pipeline_runs where pipeline='optimize' for that parent
- Returns all child team_runs created by optimization
- Includes scores, strategies, rankings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root 2026-03-29 07:20:01 -05:00
parent bc2ad7c1a9
commit 7b9b7f6641

View File

@ -6485,6 +6485,47 @@ def start_optimize(run_id):
return jsonify({"ok": True, "job_id": job_id})
@app.route("/api/optimize-history/<int:run_id>")
@login_required
def optimize_history(run_id):
"""Get optimization results for a specific parent run."""
try:
with get_db() as conn:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
# Find all optimize pipeline runs for this parent
cur.execute("""
SELECT id, result, duration_ms, completed_at
FROM pipeline_runs
WHERE pipeline = 'optimize' AND result->>'parent_run' = %s
ORDER BY completed_at DESC
""", (str(run_id),))
results = []
for r in cur.fetchall():
res = r.get("result") or {}
results.append({
"id": r["id"],
"best_score": res.get("best_score"),
"original_score": res.get("original_score"),
"improvement": res.get("improvement"),
"variations_tested": res.get("variations_tested"),
"calls_used": res.get("calls_used"),
"ranked": res.get("ranked", [])[:3],
"completed_at": str(r["completed_at"]) if r["completed_at"] else None,
"duration_ms": r["duration_ms"],
})
# Also find child runs
cur.execute("""
SELECT id, mode, quality_score, config->>'strategy' as strategy, config->>'variation' as variation
FROM team_runs
WHERE config->>'parent_run' = %s
ORDER BY quality_score DESC NULLS LAST
""", (str(run_id),))
children = [dict(r) for r in cur.fetchall()]
return jsonify({"results": results, "children": children})
except Exception as e:
return jsonify({"error": str(e), "results": [], "children": []}), 500
@app.route("/api/optimize/<job_id>/stream")
@login_required
def optimize_stream(job_id):
@ -6787,9 +6828,19 @@ function toast(msg, ok) {
async function startOptimize(runId) {
var r = await fetch('/api/runs/'+runId+'/optimize', {method:'POST'});
var data = await r.json();
if (data.error && data.job_id) {
// Already running reconnect to existing stream
toast('Reconnecting to active optimization...', true);
var jobId = data.job_id;
_showOptimizeStream(runId, jobId);
return;
}
if (data.error) { toast(data.error, false); return; }
var jobId = data.job_id;
_showOptimizeStream(runId, jobId);
}
function _showOptimizeStream(runId, jobId) {
var panel = document.getElementById('detail-panel');
panel.textContent = '';
@ -7036,13 +7087,48 @@ async function openDetail(id) {
var delBtn = document.createElement('button'); delBtn.className = 'tool-btn red'; delBtn.textContent = 'Delete';
delBtn.onclick = function(){ if(confirm('Delete permanently?')){fetch('/api/runs/'+id,{method:'DELETE'}).then(function(){toast('Deleted',true);loadRuns();panel.className='detail-panel'})} };
actions.appendChild(delBtn);
var meta = run.score_metadata || {};
var isOptimized = meta.optimized;
var optBtn = document.createElement('button'); optBtn.className = 'tool-btn';
optBtn.style.cssText = 'color:var(--accent);border-color:var(--accent);margin-left:auto';
optBtn.textContent = '\u26A1 Optimize';
optBtn.textContent = isOptimized ? '\u26A1 Re-Optimize' : '\u26A1 Optimize';
optBtn.onclick = function(){ startOptimize(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';
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);
}
}).catch(function(){});
panel.appendChild(optSection);
}
// Responses
var responses = run.responses || [];
var respTitle = document.createElement('div');