Files
CGA-bench/web/static/results.js
2026-05-22 10:02:42 +08:00

213 lines
10 KiB
JavaScript

/* CorrectBench Results Browser */
const ResultsApp = {
data: [],
currentDetail: null,
init() {
var self = this;
document.querySelectorAll(".tab-btn").forEach(function(btn) {
btn.addEventListener("click", function(e) {
self.switchTab(e.target.dataset.tab);
});
});
document.getElementById("results-search").addEventListener("input", function(e) {
self.filterResults(e.target.value);
});
document.getElementById("btn-refresh-results").addEventListener("click", function() {
self.loadResults();
});
document.getElementById("btn-back-results").addEventListener("click", function() {
self.showResultsList();
});
},
switchTab(tabName) {
document.querySelectorAll(".tab-btn").forEach(function(btn) {
btn.classList.toggle("active", btn.dataset.tab === tabName);
});
document.querySelectorAll(".tab-content").forEach(function(el) {
el.classList.toggle("active", el.id === "tab-" + tabName);
});
if (tabName === "results" && ResultsApp.data.length === 0) {
ResultsApp.loadResults();
}
},
loadResults() {
var container = document.getElementById("results-list");
container.innerHTML = "";
fetch("/api/results/runs")
.then(function(resp) { return resp.json(); })
.then(function(data) {
ResultsApp.data = data;
ResultsApp.renderResults(data);
})
.catch(function() {
container.innerHTML = '<div class="no-results">加载失败</div>';
});
},
renderResults(data) {
var container = document.getElementById("results-list");
var countEl = document.getElementById("results-count");
countEl.textContent = data.length + " 条记录";
if (data.length === 0) {
container.innerHTML = '<div class="no-results">暂无运行记录</div>';
return;
}
var html = "";
for (var i = 0; i < data.length; i++) {
var run = data[i];
var cov = run.coverage;
var covClass = "low";
if (cov !== null && cov !== undefined) {
if (cov >= 80) covClass = "high";
else if (cov >= 50) covClass = "medium";
}
var covText = (cov !== null && cov !== undefined) ? cov + "%" : "-";
var passClass = run.full_pass ? "pass" : "fail";
var passText = run.full_pass ? "✓ PASS" : "✗ FAIL";
var timeText = run.time ? run.time.toFixed(1) + "s" : "";
var costText = run.token_cost ? "$" + run.token_cost.toFixed(3) : "";
html += '<div class="run-card" onclick="ResultsApp.showTaskDetail('' + run.path + '')">' ;
html += '<span class="task-id">' + run.task_id + '</span>';
html += '<span class="coverage ' + covClass + '">' + covText + '</span>';
html += '<span class="pass-badge ' + passClass + '">' + passText + '</span>';
html += '<span class="run-path">' + run.path + '</span>';
html += '<span class="run-meta">' + timeText + " " + costText + '</span>';
html += "</div>";
}
container.innerHTML = html;
},
filterResults(query) {
var q = query.toLowerCase().trim();
if (!q) { this.renderResults(this.data); return; }
var filtered = this.data.filter(function(r) {
return r.task_id.toLowerCase().indexOf(q) >= 0 || r.path.toLowerCase().indexOf(q) >= 0;
});
this.renderResults(filtered);
},
showResultsList() {
document.getElementById("results-list-view").style.display = "";
document.getElementById("task-detail-view").style.display = "none";
this.currentDetail = null;
},
showTaskDetail(taskPath) {
var self = this;
document.getElementById("results-list-view").style.display = "none";
document.getElementById("task-detail-view").style.display = "";
document.getElementById("detail-task-title").textContent = "加载中...";
document.getElementById("detail-info").innerHTML = "";
document.getElementById("code-tabs").innerHTML = "";
document.getElementById("code-viewer").textContent = "";
fetch("/api/results/task/" + encodeURIComponent(taskPath))
.then(function(resp) { return resp.json(); })
.then(function(data) {
self.currentDetail = data;
self.renderTaskDetail(data);
})
.catch(function() {
document.getElementById("detail-task-title").textContent = "加载失败";
});
},
renderTaskDetail(data) {
document.getElementById("detail-task-title").textContent = data.task_id;
var info = data.run_info || {};
var infoContainer = document.getElementById("detail-info");
var rows = [
["Task ID", info.task_id || data.task_id, ""],
["Coverage", info.coverage !== undefined ? info.coverage + "%" : "-", info.coverage >= 80 ? "pass" : (info.coverage >= 50 ? "" : "fail")],
["Full Pass", info.full_pass ? "✓ Yes" : "✗ No", info.full_pass ? "pass" : "fail"],
["Circuit Type", info.circuit_type || "-", ""],
["Time", info.time ? info.time.toFixed(1) + "s" : "-", ""],
["Token Cost", info.token_cost ? "$" + info.token_cost.toFixed(3) : "-", ""],
["Prompt Tokens", info.prompt_tokens || "-", ""],
["Completion Tokens", info.completion_tokens || "-", ""],
["Max Iter", info.max_iter || "-", ""],
["Scenario Num", info.scenario_num || "-", ""],
["Reboot Times", info.reboot_times || "0", ""],
["Eval0 Pass", info.Eval0_pass ? "✓" : "✗", info.Eval0_pass ? "pass" : "fail"],
["Eval1 Pass", info.Eval1_pass ? "✓" : "✗", info.Eval1_pass ? "pass" : "fail"],
["Eval2 Pass", info.Eval2_pass ? "✓" : "✗", info.Eval2_pass ? "pass" : "fail"],
["Eval2 Ratio", info.Eval2_ratio || "-", ""],
["TB Corrected", info.TB_corrected ? "Yes" : "No", ""],
["Incomplete", info["ERROR(incomplete)"] ? "Yes" : "No", ""]
];
var infoHtml = "";
for (var i = 0; i < rows.length; i++) {
var r = rows[i];
infoHtml += '<div class="detail-info-row"><span class="label">' + r[0] + '</span><span class="value ' + (r[2] || "") + '">' + r[1] + '</span></div>';
}
infoContainer.innerHTML = infoHtml;
var tabs = [];
if (data.final_tb) tabs.push({id:"final_tb", label:"final_TB.v", code:data.final_tb, lang:"verilog"});
if (data.dut) tabs.push({id:"dut", label:"DUT.v", code:data.dut, lang:"verilog"});
if (data.final_tb_py) tabs.push({id:"final_tb_py", label:"final_TB.py", code:data.final_tb_py, lang:"python"});
var tabsContainer = document.getElementById("code-tabs");
var tabsHtml = "";
for (var j = 0; j < tabs.length; j++) {
tabsHtml += '<button class="code-tab ' + (j === 0 ? "active" : "") + '" data-code-id="' + tabs[j].id + '">' + tabs[j].label + '</button>';
}
tabsContainer.innerHTML = tabsHtml;
var self = this;
tabsContainer.querySelectorAll(".code-tab").forEach(function(tab) {
tab.addEventListener("click", function(e) {
tabsContainer.querySelectorAll(".code-tab").forEach(function(t) { t.classList.remove("active"); });
e.target.classList.add("active");
var codeId = e.target.dataset.codeId;
var tabData = tabs.find(function(t) { return t.id === codeId; });
if (tabData) self.renderCode(tabData.code, tabData.lang);
});
});
if (tabs.length > 0) {
this.renderCode(tabs[0].code, tabs[0].lang);
} else {
document.getElementById("code-viewer").textContent = "// 无代码文件";
}
},
renderCode(code, lang) {
var viewer = document.getElementById("code-viewer");
if (lang === "verilog") viewer.innerHTML = this.highlightVerilog(code);
else if (lang === "python") viewer.innerHTML = this.highlightPython(code);
else viewer.textContent = code;
},
escapeHtml(text) {
return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\>/g, "&gt;");
},
highlightVerilog(code) {
var html = this.escapeHtml(code);
html = html.replace(/(\\/\\.*$)/gm, '<span class="cm">$1</span>');
html = html.replace(/(\`\w+)/g, '<span class="dir">$1</span>');
html = html.replace(/(".*?")/g, '<span class="str">$1</span>');
html = html.replace(/\b(\d+'[bBhHdDoO]\w+|\d+)\b/g, '<span class="num">$1</span>');
var kw = ["module","endmodule","input","output","reg","wire","integer","always","begin","end","if","else","case","endcase","for","while","initial","final","assign","parameter","localparam","function","endfunction","task","endtask","posedge","negedge","or","and","not","xor","forever","repeat","disable","wait","fork","join"];
html = html.replace(new RegExp(\b( + kw.join("|") + )\b, "g"), '<span class="kw">$1</span>');
html = html.replace(/(\$\w+)/g, '<span class="fn">$1</span>');
return html;
},
highlightPython(code) {
var html = this.escapeHtml(code);
html = html.replace(/(#.*$)/gm, '<span class="cm">$1</span>');
html = html.replace(/("""[\s\S]*?"""|'''[\s\S]*?''')/g, '<span class="str">$1</span>');
html = html.replace(/("[^"\n]*"|'[^'\n]*')/g, '<span class="str">$1</span>');
var kw = ["def","class","import","from","return","if","elif","else","for","while","try","except","finally","with","as","yield","lambda","pass","break","continue","raise","assert","global","nonlocal","del","in","not","and","or","is","True","False","None"];
html = html.replace(new RegExp(\b( + kw.join("|") + )\b, "g"), '<span class="kw">$1</span>');
html = html.replace(/\b(\d+\.\?\d*)\b/g, '<span class="num">$1</span>');
html = html.replace(/(@\w+)/g, '<span class="dir">$1</span>');
return html;
}
};