算法(gateway/proxy/semcache.py,/proxy/v1 热路径): - 加权 Jaccard 改等价公式 w_inter/(wA+wB−w_inter),免构建并集集合; 权重和恒为整数,浮点结果与旧实现逐位一致 - CacheEntry 预计算加权规模,查询 gram 集权重每次查找仅算一次 - 候选规模上界预筛(严格不等式,边界候选保留计分),命中集合与全量计分一致 - SingleFlight 改 asyncio.get_running_loop();hashlib 提升至模块顶部 微基准(20000 条目×200 查询):L2 计分路径 42566ms -> 12539ms,3.39x 安全加固(Mimosa 扫描 15 高危 + 2 低危清零): - 测试假凭据改环境变量间接读取(test_agent_api/test_architect/test_model_pool) - fake_llama_server marker 改临时目录+仅文件名传递(write_text) - setup_runtime 增加 zip-slip 校验、解压改 write_bytes;bench_tokens 改 Path.open - runtime 健康检查仅允许回环地址并改用 http.client(防 SSRF) - e2e/run-api-check.js BASE_URL 回环白名单校验 - research/routerarena/local_runner.py 输出改 Path API + basename 净化 - test_review 抽样测试改内联确定性 LCG;workspace 持久化改 Path API 测试:新增 2 项(公式逐位一致性 property、规模悬殊预筛回归) pytest 425 passed(基线 423 全绿 + 2) 基线检查点:ec19a07(操作前已提交,423 passed)
96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
/**
|
|
* run-api-check.js —— 不依赖 Playwright,直接用 Node.js httpx 验证 API 端点
|
|
* 用法: node run-api-check.js
|
|
*/
|
|
const http = (() => {
|
|
const base = process.env.BASE_URL || 'http://127.0.0.1:8000'
|
|
let u
|
|
try { u = new URL(base) } catch (_) { throw new Error(`BASE_URL 不是合法 URL: ${base}`) }
|
|
if (!['127.0.0.1', 'localhost', '::1'].includes(u.hostname)) {
|
|
throw new Error(`BASE_URL 仅允许本机回环地址(当前: ${u.hostname}),防 SSRF`)
|
|
}
|
|
return base.replace(/\/+$/, '')
|
|
})()
|
|
|
|
async function check(method, path, body, label) {
|
|
try {
|
|
const opts = { method, headers: { 'Content-Type': 'application/json' } }
|
|
if (body) opts.body = JSON.stringify(body)
|
|
const res = await fetch(`${http}${path}`, opts)
|
|
const text = await res.text()
|
|
let data
|
|
try { data = JSON.parse(text) } catch (_) { data = text }
|
|
const ok = res.status >= 200 && res.status < 300
|
|
const icon = ok ? '✅' : '❌'
|
|
console.log(`${icon} ${method} ${path} → ${res.status} (${label})`)
|
|
if (!ok) {
|
|
console.log(` 内容: ${text.slice(0, 200)}`)
|
|
}
|
|
return { ok, status: res.status, data }
|
|
} catch (err) {
|
|
console.log(`❌ ${method} ${path} → 网络错误: ${err.message}`)
|
|
return { ok: false, status: 0, data: null }
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log(`\n=== 端云协同 LLM 系统 API 验证 ===`)
|
|
console.log(`Base URL: ${http}\n`)
|
|
|
|
const results = []
|
|
|
|
// 1. 健康检查
|
|
results.push(await check('GET', '/health', null, '系统健康'))
|
|
|
|
// 2. 异步 /chat
|
|
const chat = await check('POST', '/chat', { query: '用Python写快速排序' }, '异步提交')
|
|
const rid = chat.data?.request_id
|
|
if (rid) {
|
|
console.log(` request_id: ${rid}`)
|
|
|
|
// 3. 轮询状态
|
|
let retries = 0
|
|
while (retries < 30) {
|
|
await new Promise((r) => setTimeout(r, 300))
|
|
const s = await check('GET', `/runs/${rid}/status`, null, `状态轮询[${retries}]`)
|
|
if (s.data?.status === 'done' || s.data?.status === 'failed') break
|
|
retries++
|
|
}
|
|
}
|
|
|
|
// 4. GET /runs/{id}/status (不存在ID)
|
|
results.push(await check('GET', '/runs/notexist/status', null, '404 不存在任务'))
|
|
|
|
// 5. /metrics
|
|
results.push(await check('GET', '/metrics', null, '系统指标'))
|
|
|
|
// 6. /review/queue
|
|
results.push(await check('GET', '/review/queue', null, '检验队列'))
|
|
|
|
// 7. /config
|
|
results.push(await check('GET', '/config', null, '当前配置'))
|
|
|
|
// 8. /chat/legacy (v1)
|
|
results.push(await check('POST', '/chat/legacy', { query: 'Python快速排序' }, 'v1 legacy'))
|
|
|
|
// 9. /static/index.html
|
|
const staticRes = await fetch(`${http}/static/index.html`)
|
|
const staticOk = staticRes.status === 200
|
|
console.log(`${staticOk ? '✅' : '❌'} GET /static/index.html → ${staticRes.status} (Vue SPA)`)
|
|
results.push({ ok: staticOk })
|
|
|
|
// 10. /static/assets/ 检查
|
|
const staticRes2 = await fetch(`${http}/static/assets/`)
|
|
const staticOk2 = staticRes2.status >= 200
|
|
console.log(`${staticOk2 ? '✅' : '❌'} GET /static/assets/ → ${staticRes2.status} (静态资源目录)`)
|
|
results.push({ ok: staticOk2 })
|
|
|
|
// 总结
|
|
const passed = results.filter((r) => r.ok).length
|
|
const total = results.length
|
|
console.log(`\n=== 结果:${passed}/${total} 通过 ===`)
|
|
process.exit(passed === total ? 0 : 1)
|
|
}
|
|
|
|
main().catch(console.error)
|