feat(rag): align LightRAG native citations and MCP bridge

This commit is contained in:
lix-2026
2026-06-09 09:20:56 +08:00
parent 0e8b03daf8
commit 8e3be8b0b7
39 changed files with 2255 additions and 2732 deletions
@@ -13,6 +13,7 @@ const { BASE_URL, UI_TIMEOUT_MS } = require(path.join(ROOT, "scripts", "tree-she
const OUTPUT_DIR = path.join(ROOT, "tmp", "task530-knowledge-rag-page-ai-final-answer-smoke");
const RESULT_PATH = path.join(OUTPUT_DIR, "result.json");
const SCREENSHOT_PATH = path.join(OUTPUT_DIR, "page-ai-knowledge-rag-answer.png");
const CITATION_OPEN_SCREENSHOT_PATH = path.join(OUTPUT_DIR, "page-ai-knowledge-rag-citation-open.png");
const CONTROL_PLANE_DB =
process.env.MNOTE_CONTROL_PLANE_DB_PATH || "/mnt/Data1T/Mnote_data/control-plane/control-plane.db";
const CHROME = process.env.MNOTE_PAGE_AI_CHROME || "/usr/bin/google-chrome";
@@ -114,6 +115,60 @@ async function newestAssistantLinks(page, initialCount) {
}, initialCount);
}
async function clickNewestAssistantCitation(page, initialCount, expectedResource) {
const total = await page
.locator('[data-testid="wolai-page-ai-drawer"] .wolai-page-ai-message--assistant')
.count();
const latestIndex = Math.max(initialCount, total - 1);
const latestMessage = page
.locator('[data-testid="wolai-page-ai-drawer"] .wolai-page-ai-message--assistant')
.nth(latestIndex);
const citationLink = latestMessage.locator('a[data-page-ai-citation-link="true"]').first();
await citationLink.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
const popupPromise = page.waitForEvent("popup", { timeout: 5_000 }).catch(() => null);
await citationLink.click({ timeout: UI_TIMEOUT_MS });
const openedPage = (await popupPromise) || page;
await openedPage.waitForLoadState("domcontentloaded", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
await openedPage.waitForFunction(
(resourcePath) => {
const panel = document.querySelector(
`[data-mnote-resource-tab-panel][data-resource-path="${CSS.escape(resourcePath)}"]`,
);
return panel && !panel.hidden;
},
expectedResource,
{ timeout: UI_TIMEOUT_MS },
);
await openedPage.waitForTimeout(1500);
const state = await openedPage.evaluate((resourcePath) => {
const panel = document.querySelector(
`[data-mnote-resource-tab-panel][data-resource-path="${CSS.escape(resourcePath)}"]`,
);
const activeTab = document.querySelector(".mnote-main-tab.is-active, [data-mnote-tab-kind].is-active");
const image = panel ? panel.querySelector("img, [data-mnote-image-viewer], [data-mnote-resource-image]") : null;
return {
url: location.href,
openedInPopup: window.opener != null,
activeTabText: activeTab ? activeTab.textContent.trim().slice(0, 120) : "",
activeTabKind: activeTab ? activeTab.getAttribute("data-mnote-tab-kind") : "",
panelVisible: !!panel && !panel.hidden,
panelResourcePath: panel ? panel.getAttribute("data-resource-path") : "",
panelLocator: panel ? panel.getAttribute("data-mnote-evidence-locator") : "",
panelBlockId: panel ? panel.getAttribute("data-mnote-evidence-block-id") : "",
imageVisible: !!image,
};
}, expectedResource);
await openedPage.screenshot({ path: CITATION_OPEN_SCREENSHOT_PATH, fullPage: true });
assert.equal(state.panelVisible, true, `点击 AI citation 后未打开资源 panel: ${JSON.stringify(state, null, 2)}`);
assert.equal(state.panelResourcePath, expectedResource, `点击 AI citation 后资源路径不匹配: ${JSON.stringify(state, null, 2)}`);
assert(
state.url.includes("resourceTab=") || state.url.includes("resourcePath="),
`点击 AI citation 后 URL 缺少资源定位参数: ${JSON.stringify(state, null, 2)}`,
);
return state;
}
function summarizeRun(body) {
return {
workspaceId: body.workspaceId || "",
@@ -228,6 +283,7 @@ async function main() {
);
await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true });
const citationOpenState = await clickNewestAssistantCitation(page, assistantCount, "新页面233155/image copy 6.png");
const result = {
ok: true,
baseUrl: BASE_URL,
@@ -238,9 +294,13 @@ async function main() {
assistantLinks,
capturedRuns: capturedRuns.map(summarizeRun),
capturedRunsFullPath: path.join(OUTPUT_DIR, "captured-runs-full.json"),
citationOpenState,
pageErrors,
consoleErrors,
screenshot: SCREENSHOT_PATH,
screenshots: {
answer: SCREENSHOT_PATH,
citationOpen: CITATION_OPEN_SCREENSHOT_PATH,
},
};
fs.writeFileSync(
path.join(OUTPUT_DIR, "captured-runs-full.json"),