mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 14:12:07 +00:00
3e311ec551
整合(3c-integrate) - 公共层:跨天时长(formatSpan / formatOffset / formatLapse)、本机署名(lib/actor)、检索分类色令牌、 数据源目录(useDatasources)收进公共层;失败说明能一次列出多个写坏的自定义工具,查询超时指到数据源的查询时限 - 管理页收尾:设置页接入统一的离开前确认;检查脚本不再把沙箱里的真实值打进输出 - 画布 ↔ 右栏:点出具印章、点节点能跳到右栏对应位置;「定位」直接聚焦到出问题的字段 - 回答流 / 问数据 / 记录页:失败信息带「去哪修」且不再被翻译两遍;协作团队的结局按执行截取; 等审批时断流重连、而运行已在空档里跑完的,补全事件 扫尾(3c-sweep) - 术语统一:开始运行 / 继续运行、工作流最大步数;未保存的运行统一叫「未保存的工作流」(后端新运行同步) - 沙箱报错不再带 Python 异常类名;新增 check-tokens,未定义的 CSS 变量为 0,两个旧别名迁移后删除 检查脚本(3c-checks) - check-ui 跟上新界面(运行列表按 data 属性找行),加上令牌与选中态的回归断言,输出里的名字打码 - 新增 check-all:按顺序跑全部检查、汇总退出码,--repeat 列出偶发项 - 单节出错只记这一节失败、不再带崩整个脚本;外壳遥测浮层「重新检测」时焦点不再掉到 body 网页工具与 fake-ip - Clash / mihomo 一类代理开 TUN + fake-ip 时,查什么都先拿到 198.18 段的假地址,被当成内网拦截。 新增 AGENTLAB_HTTP_TOOL_FAKE_IP_RANGES(默认空):落在段里的解析结果改向公共 DNS(DoH)核实真实地址, 公网放行、内网照拦、核实不了按拦截处理。DoH 默认先问境外的,境内公共 DNS 会对被封锁的域名返回污染结果 - 列表型环境变量接受逗号分隔(原来注释说逗号分隔、实际只认 JSON 数组);域名解析改为异步,不再阻塞事件循环 验证(沙箱):tsc 干净,pytest 866 passed(两遍),检查脚本全绿。
55 lines
3.2 KiB
JavaScript
55 lines
3.2 KiB
JavaScript
// 冒烟测试:真开一个浏览器,跑一遍「打开画布 → 选模板 → 运行 → 看高亮」。
|
|
// 依赖 frontend 里的 playwright-core,并复用系统已装的 Chrome,不额外下载浏览器。
|
|
//
|
|
// 和 check-* 不同,这一个是真的发起运行(模型换成 Mock,不花钱),会在库里留下一条
|
|
// 运行记录;只打印、截图,不断言。所以它不在 check-all 里,要人看着跑。
|
|
// 默认连 5273;对别的实例跑时带上地址:AGENTLAB_WEB=http://localhost:<前端端口> node scripts/e2e-check.mjs
|
|
import { chromium } from '../frontend/node_modules/playwright-core/index.mjs'
|
|
const WEB = process.env.AGENTLAB_WEB ?? 'http://localhost:5273'
|
|
const browser = await chromium.launch({
|
|
executablePath: process.env.CHROME_PATH ?? '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
args: ['--no-sandbox'] })
|
|
const page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
|
|
const errs = []
|
|
page.on('pageerror', e => errs.push(e.message))
|
|
page.on('console', m => m.type() === 'error' && errs.push(m.text()))
|
|
|
|
await page.goto(`${WEB}/studio`, { waitUntil: 'networkidle' })
|
|
await page.waitForSelector('.react-flow__node', { timeout: 15000 })
|
|
|
|
// 切到「最小问答」模板,用 mock 模型跑,验证画布实时高亮
|
|
await page.getByRole('button', { name: /批量处理|最小问答|工作流/ }).first().click()
|
|
await page.waitForTimeout(600)
|
|
const row = page.locator('text=① 最小问答').first()
|
|
if (await row.count()) { await row.click(); await page.waitForTimeout(900) }
|
|
|
|
console.log('当前图节点数:', await page.locator('.react-flow__node').count())
|
|
|
|
// 选中 llm 节点,把模型改成 mock,避免这次验证花真钱
|
|
await page.locator('.react-flow__node').nth(1).click()
|
|
await page.waitForTimeout(500)
|
|
const modelSel = page.locator('select').filter({ hasText: /默认|Mock|Claude/ }).first()
|
|
if (await modelSel.count()) { await modelSel.selectOption({ label: 'Mock(不花钱,用来试编排)' }).catch(()=>{}) }
|
|
await page.screenshot({ path: '/tmp/shot-inspector.png' })
|
|
|
|
// 回到运行面板,填输入并运行
|
|
await page.getByRole('button', { name: '运行', exact: true }).first().click()
|
|
await page.waitForTimeout(300)
|
|
const ta = page.locator('aside textarea').first()
|
|
await ta.fill('可视化 agent 编排解决了什么问题?')
|
|
await page.getByRole('button', { name: /^运行$/ }).last().click()
|
|
|
|
// 等到有节点进入运行态,抓一张"正在跑"的画面
|
|
await page.waitForSelector('.node-running, .node-done', { timeout: 20000 }).catch(()=>{})
|
|
await page.waitForTimeout(1200)
|
|
await page.screenshot({ path: '/tmp/shot-running.png' })
|
|
console.log('运行中高亮节点:', await page.locator('.node-running').count(), '已完成:', await page.locator('.node-done').count())
|
|
|
|
await page.waitForTimeout(6000)
|
|
await page.screenshot({ path: '/tmp/shot-done.png' })
|
|
console.log('完成后 done 节点:', await page.locator('.node-done').count())
|
|
const timeline = await page.locator('aside').last().innerText()
|
|
console.log('--- 右侧面板文本 ---'); console.log(timeline.slice(0, 700))
|
|
console.log('--- 错误 ---'); console.log(errs.slice(0,6).join('\n') || '(无)')
|
|
await browser.close()
|