PlantAI-rag-new 首次提交:文献知识工作台
- BFF 薄适配层(FastAPI,8090):QA 代理/SSE 中继/Neo4j 直连/抽取代理/会话持久化 - Vue3 前端:五相位检索流水线、文献来源与证据链、知识图谱、提取任务四 tab - 四档响应式(桌面/笔记本/平板/手机)、深浅主题 - 81 个 FR 全部实现,测试追溯矩阵 81/81,后端 121 测/前端 156 测 - 真实凭据已脱敏,运行时配置在 backend/.env(不入 git) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// deploy/m2_probe.js —— 诊断探针(PM 使用,在远端运行)
|
||||
// 目的:定位真实浏览器中 SSE progress 未推进流水线相位的原因
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
||||
const page = await ctx.newPage();
|
||||
const logs = [];
|
||||
page.on('console', (m) => {
|
||||
if (m.type() === 'error' || m.type() === 'warning') logs.push(`[${m.type()}] ${m.text().slice(0, 200)}`);
|
||||
});
|
||||
page.on('pageerror', (e) => logs.push('[pageerror] ' + e.message.slice(0, 200)));
|
||||
|
||||
await page.goto('http://127.0.0.1:5180', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForSelector('.app-shell', { timeout: 15000 });
|
||||
await page.click('.new-task-btn');
|
||||
await page.waitForSelector('.composer textarea', { timeout: 15000 });
|
||||
|
||||
// 用「检索」模式(use_llm:false,走 /api/qa/search,更快的真实链路)排除揭示动画干扰
|
||||
// 但 search 无 SSE——这里仍用默认问答走 SSE,只是探针只观察 60s
|
||||
await page.fill('.composer textarea', 'SPL gene family in rice');
|
||||
await page.click('.send-btn');
|
||||
|
||||
const t0 = Date.now();
|
||||
const timeline = [];
|
||||
let pipelineSeen = false;
|
||||
while (Date.now() - t0 < 90000) {
|
||||
const snap = await page.evaluate(() => {
|
||||
const card = document.querySelector('.pipeline-card');
|
||||
const steps = Array.from(document.querySelectorAll('.pipeline-card .p-step'));
|
||||
const msgs = Array.from(document.querySelectorAll('.msg.assistant'));
|
||||
const stream = document.querySelector('.stream-text')?.textContent?.length || 0;
|
||||
const body = document.querySelector('.msg.assistant .msg-body')?.textContent?.length || 0;
|
||||
// 检查正在进行的 fetch 到 stream 端点
|
||||
const perf = performance.getEntriesByType('resource').filter((r) => r.name.includes('chat/stream'));
|
||||
return {
|
||||
card: !!card,
|
||||
stepCount: steps.length,
|
||||
states: steps.map((s) => (s.className.includes('done') ? 'D' : s.className.includes('running') ? 'R' : 'W')).join(''),
|
||||
descs: steps.map((s) => s.querySelector('.p-desc')?.textContent?.slice(0, 20)).join('|'),
|
||||
msgCount: msgs.length,
|
||||
streamLen: stream,
|
||||
bodyLen: body,
|
||||
streamReqs: perf.length,
|
||||
};
|
||||
});
|
||||
const key = JSON.stringify(snap);
|
||||
if (!timeline.length || timeline[timeline.length - 1].key !== key) {
|
||||
timeline.push({ t: Math.round((Date.now() - t0) / 1000), key });
|
||||
console.log(`T+${Math.round((Date.now() - t0) / 1000)}s`, JSON.stringify(snap));
|
||||
}
|
||||
if (snap.card) pipelineSeen = true;
|
||||
if (snap.bodyLen > 30 && snap.streamLen === 0 && snap.states.includes('D')) break;
|
||||
if (snap.bodyLen > 50) break; // 答案已出现,看相位是否 done
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
}
|
||||
|
||||
console.log('\n--- 浏览器日志(error/warning)---');
|
||||
logs.slice(0, 10).forEach((l) => console.log(l));
|
||||
console.log('\n--- 最终 DOM 摘要 ---');
|
||||
const fin = await page.evaluate(() => {
|
||||
const card = document.querySelector('.pipeline-card');
|
||||
return card ? card.outerHTML.slice(0, 1200) : '无 pipeline-card';
|
||||
});
|
||||
console.log(fin);
|
||||
await browser.close();
|
||||
})();
|
||||
Reference in New Issue
Block a user