Files
jerryandClaude Fable 5 1e94de7464 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>
2026-08-25 20:47:52 +08:00

84 lines
3.4 KiB
JavaScript

// deploy/m2_probe4.js —— Vue store 内部状态探针(PM 使用,在远端运行)
// 通过 __vueParentComponent 链拿到 pinia,直读 chat store 的消息状态,
// 区分「store 未更新」(handler 问题)与「store 已更新但 DOM 未渲染」(响应式问题)
const { chromium } = require('playwright');
async function dumpStore(page) {
return page.evaluate(() => {
const findRoot = (el) => {
let node = el;
while (node) {
if (node.__vue_app__) return node.__vue_app__;
node = node.__vueParentComponent?.parent || null;
}
return null;
};
const rootEl = document.querySelector('#app') || document.querySelector('.app-shell');
const app = rootEl ? findRoot(rootEl) : null;
if (!app) return { err: '未找到 vue app' };
const pinia = app.config.globalProperties.$pinia;
if (!pinia) return { err: '未找到 pinia' };
const chat = pinia._s.get('chat');
if (!chat) return { err: '未找到 chat store' };
const msgs = chat.messages.map((m) => ({
role: m.role,
status: m.status,
id: m.id,
blocks: (m.blocks || []).map((b) =>
b.type === 'pipeline'
? { type: 'pipeline', status: b.status, steps: b.steps.map((s) => `${s.phase}:${s.state}`) }
: { type: b.type, len: b.type === 'text' ? (b.html || '').length : b.type === 'stream' ? (b.text || '').length : undefined },
),
}));
return {
busy: chat.busy,
completedOnce: chat.completedOnce,
currentEvents: chat.currentEvents.map((e) => e.stage),
sessionId: chat.sessionId,
msgs,
};
});
}
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
const page = await ctx.newPage();
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 });
await page.fill('.composer textarea', 'SPL gene family in rice');
await page.click('.send-btn');
const t0 = Date.now();
let last = '';
while (Date.now() - t0 < 150000) {
const snap = await dumpStore(page);
const msgs = snap.msgs || [];
const lastAssist = msgs.filter((m) => m.role === 'assistant').pop();
const pipe = lastAssist?.blocks?.find((b) => b.type === 'pipeline');
const key = JSON.stringify({ busy: snap.busy, done: snap.completedOnce, steps: pipe?.steps, nEv: snap.currentEvents?.length });
if (key !== last) {
last = key;
const el = Math.round((Date.now() - t0) / 1000);
console.log(`T+${el}s busy=${snap.busy} completedOnce=${snap.completedOnce} events=${snap.currentEvents?.length} 步骤=${JSON.stringify(pipe?.steps)}`);
console.log(` 事件序列: ${(snap.currentEvents || []).slice(-5).join(', ')}`);
}
if (!snap.busy && snap.completedOnce && Date.now() - t0 > 30000) break;
await new Promise((r) => setTimeout(r, 2000));
}
console.log('\n=== 最终 store 状态 ===');
console.log(JSON.stringify(await dumpStore(page), null, 2));
console.log('\n=== 最终 DOM 流水线 ===');
const dom = await page.evaluate(() => {
const card = document.querySelector('.pipeline-card');
if (!card) return '无卡片';
return card.textContent.slice(0, 300);
});
console.log(dom);
await browser.close();
})();