@@ -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 ) ; // 空闲时定期刷新历史
} ) ( ) ;