import os out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "web", "static", "results.js") # Write JS using template with placeholders for regex patterns # This avoids Python escape hell template = """ /* 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 = '
加载失败
'; }); }, renderResults(data) { var container = document.getElementById("results-list"); var countEl = document.getElementById("results-count"); countEl.textContent = data.length + " 条记录"; if (data.length === 0) { container.innerHTML = '
暂无运行记录
'; 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 ? "\u2713 PASS" : "\u2717 FAIL"; var timeText = run.time ? run.time.toFixed(1) + "s" : ""; var costText = run.token_cost ? "$" + run.token_cost.toFixed(3) : ""; html += '
' ; html += '' + run.task_id + ''; html += '' + covText + ''; html += '' + passText + ''; html += '' + run.path + ''; html += '' + timeText + " " + costText + ''; html += "
"; } 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 = "\u52a0\u8f7d\u4e2d..."; 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 = "\u52a0\u8f7d\u5931\u8d25"; }); }, 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 ? "\u2713 Yes" : "\u2717 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 ? "\u2713" : "\u2717", info.Eval0_pass ? "pass" : "fail"], ["Eval1 Pass", info.Eval1_pass ? "\u2713" : "\u2717", info.Eval1_pass ? "pass" : "fail"], ["Eval2 Pass", info.Eval2_pass ? "\u2713" : "\u2717", 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 += '
' + r[0] + '' + r[1] + '
'; } 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 += ''; } 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 = "// \u65e0\u4ee3\u7801\u6587\u4ef6"; } }, 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(REGEX_AMP, "&").replace(REGEX_LT, "<").replace(REGEX_GT, ">"); }, highlightVerilog(code) { var html = this.escapeHtml(code); html = html.replace(REGEX_VCOMMENT, '$1'); html = html.replace(REGEX_VDIRECTIVE, '$1'); html = html.replace(REGEX_VSTRING, '$1'); html = html.replace(REGEX_VNUM, '$1'); 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(REGEX_KW_PRE + kw.join("|") + REGEX_KW_SUF, "g"), '$1'); html = html.replace(REGEX_VSYS, '$1'); return html; }, highlightPython(code) { var html = this.escapeHtml(code); html = html.replace(REGEX_PCOMMENT, '$1'); html = html.replace(REGEX_PDOCSTRING, '$1'); html = html.replace(REGEX_PSTRING, '$1'); 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(REGEX_KW_PRE + kw.join("|") + REGEX_KW_SUF, "g"), '$1'); html = html.replace(REGEX_PNUM, '$1'); html = html.replace(REGEX_PDECORATOR, '$1'); return html; } }; """ # Replace regex placeholders with actual patterns # Use chr() to avoid quote conflicts in raw strings DQ = chr(34) # double quote SQ = chr(39) # single quote BS = chr(92) # backslash replacements = { "REGEX_AMP": "/&/g", "REGEX_LT": "//g", "REGEX_VCOMMENT": "/("+BS+BS+"/"+BS+BS+".*$)/gm", "REGEX_VDIRECTIVE": "/("+BS+"`"+BS+"w+)/g", "REGEX_VSTRING": "/("+DQ+".*?"+DQ+")/g", "REGEX_VNUM": "/"+BS+"b("+BS+"d+"+SQ+"[bBhHdDoO]"+BS+"w+|"+BS+"d+)"+BS+"b/g", "REGEX_VSYS": "/("+BS+"$"+BS+"w+)/g", "REGEX_PCOMMENT": "/(#.*$)/gm", "REGEX_PDOCSTRING": "/("+DQ+DQ+DQ+"["+BS+"s"+BS+"S]*?"+DQ+DQ+DQ+"|"+SQ+SQ+SQ+"["+BS+"s"+BS+"S]*?"+SQ+SQ+SQ+")/g", "REGEX_PSTRING": "/("+DQ+"[^"+DQ+BS+"n]*"+DQ+"|"+SQ+"[^"+SQ+BS+"n]*"+SQ+")/g", "REGEX_PNUM": "/"+BS+"b("+BS+"d+"+BS+"."+BS+"?"+BS+"d*)"+BS+"b/g", "REGEX_PDECORATOR": "/(@"+BS+"w+)/g", "REGEX_KW_PRE": BS+"b(", "REGEX_KW_SUF": ")"+BS+"b", } result = template for key, val in replacements.items(): result = result.replace(key, val) with open(out, "w", encoding="utf-8") as f: f.write(result.strip() + "\n") print("OK: " + out + " (" + str(len(result)) + " chars)")