v2.4.0 评测站同步:新增📤发送到评测站(16066 model-eval-site),左侧配置评测站地址+账号,历史/详情一键发送测试结果
This commit is contained in:
@@ -116,6 +116,20 @@
|
||||
<button class="btn danger block" id="btn-cancel" disabled>■ 停止</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="card">
|
||||
<h2>📤 评测站同步</h2>
|
||||
<div class="field">
|
||||
<label>评测站地址</label>
|
||||
<div class="icon-input"><span class="icon">🌐</span><input id="eval-url" placeholder="http://127.0.0.1:16066"></div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>评测站账号(结果挂到该账号下)</label>
|
||||
<div class="icon-input"><span class="icon">👥</span><input id="eval-account" placeholder="如:性能评测组"></div>
|
||||
</div>
|
||||
<button class="btn block" id="btn-eval-test">🔗 测试连接</button>
|
||||
<div class="conn-result" id="eval-result" hidden></div>
|
||||
<div class="hint" style="margin-top:6px">测试完成后,在历史列表或详情里点「📤 发送到评测站」,一键把结果发布到模型评测网站(端口 16066)对应账号下。</div>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<!-- 右侧:指标 + 日志 + 历史 -->
|
||||
@@ -170,6 +184,7 @@
|
||||
<div class="modal-head">
|
||||
<h2>测试详情 #<span id="dt-id"></span></h2>
|
||||
<div>
|
||||
<button class="btn small primary" id="dt-send-eval">📤 发送到评测站</button>
|
||||
<button class="btn small primary" id="dt-export-xlsx">⬇ 导出 Excel</button>
|
||||
<button class="btn small primary" id="dt-export">导出 JSON</button>
|
||||
<button class="btn small" id="dt-close">✕</button>
|
||||
|
||||
@@ -383,6 +383,7 @@ async function loadHistory() {
|
||||
<td><span class="status-pill ${esc(t.status)}">${STATUS_LABEL[t.status] || t.status}</span></td>
|
||||
<td>
|
||||
<button class="btn small" data-view="${t.id}">查看</button>
|
||||
<button class="btn small" data-send="${t.id}" title="发送到评测站对应账号下">📤 发送</button>
|
||||
<button class="btn small" data-xlsx="${t.id}">Excel</button>
|
||||
<button class="btn small danger" data-del="${t.id}">删除</button>
|
||||
</td>`;
|
||||
@@ -824,6 +825,92 @@ function exportCurrentLog() {
|
||||
});
|
||||
}
|
||||
|
||||
/* ───────────────────────── 评测站同步(一键发送) ───────────────────────── */
|
||||
|
||||
const EVAL_TOKEN = "meval16066"; // 与评测站 config.SUBMIT_TOKEN 一致
|
||||
|
||||
function evalSettings() {
|
||||
return {
|
||||
url: (localStorage.getItem("evalUrl") || "http://127.0.0.1:16066").replace(/\/+$/, ""),
|
||||
account: localStorage.getItem("evalAccount") || "默认账号",
|
||||
};
|
||||
}
|
||||
|
||||
function loadEvalSettings() {
|
||||
const s = evalSettings();
|
||||
const u = $("#eval-url"), a = $("#eval-account");
|
||||
if (u) u.value = s.url;
|
||||
if (a) a.value = s.account;
|
||||
}
|
||||
|
||||
function saveEvalSettings() {
|
||||
localStorage.setItem("evalUrl", $("#eval-url").value.trim() || "http://127.0.0.1:16066");
|
||||
localStorage.setItem("evalAccount", $("#eval-account").value.trim() || "默认账号");
|
||||
}
|
||||
|
||||
async function testEvalConn() {
|
||||
saveEvalSettings();
|
||||
const s = evalSettings();
|
||||
const box = $("#eval-result");
|
||||
box.hidden = false; box.className = "conn-result";
|
||||
box.textContent = "⏳ 正在连接评测站...";
|
||||
try {
|
||||
const resp = await fetch(s.url + "/api/health");
|
||||
if (!resp.ok) throw new Error("HTTP " + resp.status);
|
||||
const j = await resp.json();
|
||||
const st = j.stats || {};
|
||||
box.className = "conn-result ok";
|
||||
box.textContent = `✅ 连接成功(端口 ${j.port})| 模型 ${st.models} · 提交 ${st.submissions} · 账号 ${st.accounts}`;
|
||||
} catch (e) {
|
||||
box.className = "conn-result fail";
|
||||
box.textContent = "❌ 连接失败:" + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function sendToEval(id) {
|
||||
saveEvalSettings();
|
||||
const s = evalSettings();
|
||||
if (!confirm(`将测试 #${id} 发送到评测站(${s.url})的账号「${s.account}」下?`)) return;
|
||||
let t;
|
||||
try {
|
||||
const resp = await fetch(`/api/tests/${id}/export.json`);
|
||||
if (!resp.ok) throw new Error("读取测试数据失败");
|
||||
t = await resp.json();
|
||||
} catch (e) {
|
||||
toast("读取失败:" + e.message); return;
|
||||
}
|
||||
if (!(t.summary && t.summary.samples_ok > 0)) {
|
||||
toast("该测试没有成功采样数据,无法发送"); return;
|
||||
}
|
||||
const payload = {
|
||||
account: s.account,
|
||||
source_test_id: id,
|
||||
source_site: "llm-speed-tester",
|
||||
provider: t.provider || "",
|
||||
model: t.model || "",
|
||||
test_name: t.name || "",
|
||||
summary: t.summary || {},
|
||||
gen: t.gen || {},
|
||||
config: t.config || {},
|
||||
runs: t.runs || [],
|
||||
};
|
||||
try {
|
||||
const resp = await fetch(s.url + "/api/submit", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "X-Token": EVAL_TOKEN },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const j = await resp.json().catch(() => ({}));
|
||||
if (!j.ok) throw new Error(j.error || "发送失败(HTTP " + resp.status + ")");
|
||||
toast(`✅ 已发送!评测站提交 #${j.id}(账号 ${j.account} / ${j.model})`);
|
||||
setTimeout(() => {
|
||||
toast(`📤 查看详情:${s.url}${j.url}`);
|
||||
}, 3600);
|
||||
} catch (e) {
|
||||
toast("❌ 发送失败:" + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/* ───────────────────────── 提示 ───────────────────────── */
|
||||
|
||||
function toast(msg) {
|
||||
@@ -863,6 +950,7 @@ function bind() {
|
||||
});
|
||||
|
||||
$("#btn-test-conn").addEventListener("click", testConnection);
|
||||
$("#btn-eval-test").addEventListener("click", testEvalConn);
|
||||
$("#btn-start").addEventListener("click", startTest);
|
||||
$("#btn-cancel").addEventListener("click", stopTest);
|
||||
$("#btn-context-add").addEventListener("click", addContextLength);
|
||||
@@ -877,9 +965,11 @@ function bind() {
|
||||
|
||||
$("#history tbody").addEventListener("click", (e) => {
|
||||
const v = e.target.closest("[data-view]");
|
||||
const s = e.target.closest("[data-send]");
|
||||
const x = e.target.closest("[data-xlsx]");
|
||||
const d = e.target.closest("[data-del]");
|
||||
if (v) viewDetail(Number(v.dataset.view));
|
||||
if (s) sendToEval(Number(s.dataset.send));
|
||||
if (x) exportXlsx(Number(x.dataset.xlsx));
|
||||
if (d) {
|
||||
const id = Number(d.dataset.del);
|
||||
@@ -911,6 +1001,7 @@ function bind() {
|
||||
$("#cmp-mask").addEventListener("click", (e) => { if (e.target === $("#cmp-mask")) closeCompare(); });
|
||||
$("#dt-export").addEventListener("click", exportDetail);
|
||||
$("#dt-export-xlsx").addEventListener("click", () => { if (window.__detail) exportXlsx(window.__detail.id); });
|
||||
$("#dt-send-eval").addEventListener("click", () => { if (window.__detail) sendToEval(window.__detail.id); });
|
||||
|
||||
document.addEventListener("keydown", (e) => { if (e.key === "Escape") { closeDetail(); closeCompare(); } });
|
||||
}
|
||||
@@ -924,5 +1015,6 @@ function bind() {
|
||||
updateDefaultUrlHint();
|
||||
loadConfigs();
|
||||
loadHistory();
|
||||
loadEvalSettings();
|
||||
setInterval(() => { if (!currentTestId) loadHistory(); }, 30000); // 空闲时定期刷新历史
|
||||
})();
|
||||
Reference in New Issue
Block a user