// score_jobs.js — 评分记录页面(异步 RAGAS 评分任务列表) // 每条评分完成后自动写入标准 Run 产物,点击「查看报告」复用现有报告详情页。 const ScoreJobs = { _pollTimers: {}, // job_id -> setInterval handle async load() { const list = document.getElementById("scorejobs-list"); const empty = document.getElementById("scorejobs-empty"); list.innerHTML = '

加载中…

'; try { const data = await API.listScoreJobs(); const jobs = data.jobs || []; list.innerHTML = ""; if (jobs.length === 0) { empty.hidden = false; return; } empty.hidden = true; jobs.forEach(job => list.appendChild(ScoreJobs.renderCard(job))); // Auto-poll any pending jobs jobs.forEach(job => { if (job.status === "queued" || job.status === "running") { ScoreJobs._startPoll(job.job_id); } }); } catch (err) { list.innerHTML = `

加载失败:${App.escape(err.message)}

`; } }, renderCard(job) { const card = document.createElement("div"); card.className = "run-card"; card.id = `score-job-${job.job_id}`; card.innerHTML = ScoreJobs._cardHtml(job); // Bind report button if already completed ScoreJobs._bindReportBtn(card, job); return card; }, _cardHtml(job) { const time = App.shortTime(job.created_at); const question = App.escape((job.request_summary?.question || "—").slice(0, 60)); const metrics = (job.request_summary?.metrics || []).join(", "); const statusBadge = `${job.status}`; let scoreHtml = ""; if (job.status === "completed") { scoreHtml = Object.entries(job.scores || {}) .map(([k, v]) => { const cls = App.scoreClass(k, v); const text = v === null || v === undefined ? "n/a" : Number(v).toFixed(3); return `${App.escape(App.shortMetric(k))} ${text}`; }) .join(" "); // 综合加权得分(已暂时隐藏) // if (job.weighted_score !== null && job.weighted_score !== undefined) { // const cls = App.scoreClass(job.weighted_score); // scoreHtml += ` 综合 ${Number(job.weighted_score).toFixed(3)}`; // } } else if (job.status === "failed") { scoreHtml = `${App.escape((job.error || "").slice(0, 80))}`; } else { scoreHtml = `评分中,请稍候…`; } const reportBtn = job.status === "completed" && job.run_id ? `` : ""; return `
${question}
${statusBadge}${reportBtn}
指标:${App.escape(metrics)} · ${time} · ${job.latency_ms}ms
${scoreHtml}
`; }, _bindReportBtn(card, job) { const btn = card.querySelector(".score-job-report-btn"); if (!btn) return; btn.addEventListener("click", () => { const runId = btn.dataset.runId; if (runId) { App.enableReportNav(); App.navigate("report", runId); } }); }, _startPoll(jobId) { if (ScoreJobs._pollTimers[jobId]) return; ScoreJobs._pollTimers[jobId] = setInterval(async () => { try { const job = await API.getScoreJob(jobId); const card = document.getElementById(`score-job-${jobId}`); if (card) { card.innerHTML = ScoreJobs._cardHtml(job); ScoreJobs._bindReportBtn(card, job); } if (job.status === "completed" || job.status === "failed") { clearInterval(ScoreJobs._pollTimers[jobId]); delete ScoreJobs._pollTimers[jobId]; // If completed, pre-enable report nav if (job.status === "completed" && job.run_id) { App.enableReportNav(); } } } catch (_e) { clearInterval(ScoreJobs._pollTimers[jobId]); delete ScoreJobs._pollTimers[jobId]; } }, 5000); }, stopAllPolls() { Object.values(ScoreJobs._pollTimers).forEach(t => clearInterval(t)); ScoreJobs._pollTimers = {}; }, };