Improve LightRAG knowledge search locator alignment
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { chromium } = require("playwright");
|
||||
|
||||
const BASE_URL = (process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
|
||||
const ROOT_URI = process.env.MNOTE_KNOWLEDGE_RAG_ROOT_URI || "file:///mnt/Data1T/Mnote_data/users/mnote-e2e/workspaces/my-space";
|
||||
const WORKSPACE_ID = process.env.MNOTE_KNOWLEDGE_RAG_WORKSPACE_ID || "local-ws:mnote-e2e:my-space";
|
||||
const QUERY = process.env.MNOTE_KNOWLEDGE_RAG_OFFICE_QUERY || "三乙基硅";
|
||||
const EXPECTED_RESOURCE = process.env.MNOTE_KNOWLEDGE_RAG_EXPECTED_RESOURCE
|
||||
|| "有机合成中的保护基/[OCR]_有机合成中的保护基-酚羰基羧基巯基的保护_20250201_1908.layered_删减-2025-02-04 18-59-42.docx";
|
||||
const EXPECTED_BLOCK_ID = process.env.MNOTE_KNOWLEDGE_RAG_EXPECTED_BLOCK_ID || "0ace5daa070f0911e09d8ab37c64eeea";
|
||||
const EXPECTED_HIGHLIGHT = process.env.MNOTE_KNOWLEDGE_RAG_EXPECTED_HIGHLIGHT || "三乙基硅酯";
|
||||
const OUTPUT_DIR = path.join(process.cwd(), "tmp", "task540-knowledge-rag-office-result-open-locator-smoke");
|
||||
const RESULT_PATH = path.join(OUTPUT_DIR, "result.json");
|
||||
const SEARCH_SCREENSHOT_PATH = path.join(OUTPUT_DIR, "office-search-result.png");
|
||||
const OPEN_SCREENSHOT_PATH = path.join(OUTPUT_DIR, "office-open-locator.png");
|
||||
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
||||
const CHROMIUM_EXECUTABLE_PATH = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
|
||||
|| ["/usr/bin/google-chrome-stable", "/usr/bin/google-chrome", "/snap/bin/chromium"]
|
||||
.find((candidate) => fs.existsSync(candidate));
|
||||
|
||||
async function main() {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
const browser = await chromium.launch({ headless: true, executablePath: CHROMIUM_EXECUTABLE_PATH });
|
||||
const context = await browser.newContext({ viewport: { width: 1360, height: 900 } });
|
||||
try {
|
||||
const auth = await context.request.post(`${BASE_URL}/api/auth`, {
|
||||
data: {
|
||||
action: "auth:signIn",
|
||||
args: {
|
||||
provider: "password",
|
||||
params: {
|
||||
email: "mnote.e2e@example.com",
|
||||
password: process.env.MNOTE_E2E_PASSWORD || "MnoteE2E123!",
|
||||
flow: "signIn",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
assert(auth.ok(), `登录失败: ${auth.status()} ${await auth.text()}`);
|
||||
|
||||
const apiSearch = await context.request.post(`${BASE_URL}/api/knowledge-rag/search`, {
|
||||
data: {
|
||||
rootUri: ROOT_URI,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
query: QUERY,
|
||||
mode: "mix",
|
||||
topK: 8,
|
||||
chunkTopK: 8,
|
||||
includeChunkContent: true,
|
||||
sourcePaths: [EXPECTED_RESOURCE],
|
||||
},
|
||||
});
|
||||
assert(apiSearch.ok(), `knowledge-rag search 失败: ${apiSearch.status()} ${await apiSearch.text()}`);
|
||||
const apiPayload = await apiSearch.json();
|
||||
const apiFirst = apiPayload.results?.[0];
|
||||
assert(apiFirst, `API 未返回资料库结果: ${JSON.stringify(apiPayload, null, 2).slice(0, 3000)}`);
|
||||
assert.equal(apiFirst.locator?.blockId, EXPECTED_BLOCK_ID, `API locator blockId 不匹配: ${JSON.stringify(apiFirst, null, 2)}`);
|
||||
assert(String(apiFirst.snippet || "").includes(EXPECTED_HIGHLIGHT), `API snippet 未命中 ${EXPECTED_HIGHLIGHT}: ${apiFirst.snippet}`);
|
||||
|
||||
const page = await context.newPage();
|
||||
const pageErrors = [];
|
||||
const consoleErrors = [];
|
||||
page.on("pageerror", (error) => pageErrors.push(String(error?.stack || error?.message || error)));
|
||||
page.on("console", (message) => {
|
||||
if (["error", "warning"].includes(message.type())) consoleErrors.push(`${message.type()}: ${message.text()}`);
|
||||
});
|
||||
await page.goto(`${BASE_URL}/`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
await page.evaluate(() => {
|
||||
window.localStorage?.setItem("mnote.search.collapseSourcesDefault.v1", "0");
|
||||
});
|
||||
await page.waitForSelector('[data-mnote-action="open-search-modal"]', { timeout: UI_TIMEOUT_MS });
|
||||
await page.click('[data-mnote-action="open-search-modal"]');
|
||||
await page.waitForSelector('[data-testid="wolai-search-input"]', { timeout: UI_TIMEOUT_MS });
|
||||
await page.fill('[data-testid="wolai-search-input"]', QUERY);
|
||||
await page.evaluate(() => {
|
||||
const button = document.querySelector('[data-search-switch="knowledge"]');
|
||||
if (button instanceof HTMLElement) {
|
||||
button.setAttribute("aria-checked", "true");
|
||||
button.classList.add("is-on");
|
||||
}
|
||||
document.querySelector('[data-testid="wolai-search-input"]')
|
||||
?.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
});
|
||||
await page.waitForFunction(
|
||||
(expected) => {
|
||||
const first = document.querySelector('[data-testid="wolai-search-result-row"]');
|
||||
return first && first.textContent.includes(expected);
|
||||
},
|
||||
EXPECTED_HIGHLIGHT,
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
await page.screenshot({ path: SEARCH_SCREENSHOT_PATH, fullPage: true });
|
||||
await page.click('[data-testid="wolai-search-result-row"]');
|
||||
await page.waitForFunction(
|
||||
(expectedResource) => document.querySelector(`[data-mnote-resource-tab-panel][data-resource-path="${CSS.escape(expectedResource)}"]`),
|
||||
EXPECTED_RESOURCE,
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
await page.waitForTimeout(8_000);
|
||||
|
||||
const state = await page.evaluate((expectedResource) => {
|
||||
const panel = document.querySelector(`[data-mnote-resource-tab-panel][data-resource-path="${CSS.escape(expectedResource)}"]`);
|
||||
const frame = panel?.querySelector("iframe.mnote-resource-tab-frame");
|
||||
const result = {
|
||||
url: location.href,
|
||||
panelVisible: !!panel && !panel.hidden,
|
||||
panelResourcePath: panel ? panel.getAttribute("data-resource-path") : "",
|
||||
panelBlockId: panel ? panel.getAttribute("data-mnote-evidence-block-id") : "",
|
||||
panelEvidenceText: panel ? panel.getAttribute("data-mnote-evidence-text") : "",
|
||||
frameSrc: frame ? frame.getAttribute("src") : "",
|
||||
};
|
||||
if (frame?.contentDocument) {
|
||||
const doc = frame.contentDocument;
|
||||
const viewer = doc.querySelector("#mnote-office-viewer");
|
||||
const highlighted = doc.querySelector('[data-mnote-office-evidence-target="true"]');
|
||||
result.iframe = {
|
||||
readyState: doc.readyState,
|
||||
status: doc.documentElement.getAttribute("data-mnote-office-preview-status") || "",
|
||||
applied: doc.documentElement.getAttribute("data-mnote-office-evidence-applied") || "",
|
||||
bodyEvidenceText: doc.body?.dataset?.evidenceText || "",
|
||||
textHasQuery: (viewer?.textContent || "").includes("三乙基硅"),
|
||||
scrollY: frame.contentWindow?.scrollY || 0,
|
||||
highlightedText: highlighted?.textContent?.slice(0, 160) || "",
|
||||
highlightedTop: highlighted ? Math.round(highlighted.getBoundingClientRect().top) : null,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}, EXPECTED_RESOURCE);
|
||||
await page.screenshot({ path: OPEN_SCREENSHOT_PATH, fullPage: true });
|
||||
|
||||
assert.equal(state.panelVisible, true, `搜索结果未打开资源标签: ${JSON.stringify(state, null, 2)}`);
|
||||
assert.equal(state.panelResourcePath, EXPECTED_RESOURCE, `资源路径不匹配: ${JSON.stringify(state, null, 2)}`);
|
||||
assert.equal(state.panelBlockId, EXPECTED_BLOCK_ID, `资源标签 blockId 不匹配: ${JSON.stringify(state, null, 2)}`);
|
||||
assert.equal(state.iframe?.applied, "true", `Office preview 未应用 evidence locator: ${JSON.stringify(state, null, 2)}`);
|
||||
assert(String(state.iframe?.highlightedText || "").includes(EXPECTED_HIGHLIGHT), `Office preview 未高亮 ${EXPECTED_HIGHLIGHT}: ${JSON.stringify(state, null, 2)}`);
|
||||
|
||||
const result = {
|
||||
ok: true,
|
||||
baseUrl: BASE_URL,
|
||||
rootUri: ROOT_URI,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
query: QUERY,
|
||||
expectedResource: EXPECTED_RESOURCE,
|
||||
expectedBlockId: EXPECTED_BLOCK_ID,
|
||||
apiFirst: {
|
||||
snippet: apiFirst.snippet,
|
||||
blockId: apiFirst.locator?.blockId,
|
||||
citationUrl: apiFirst.citationUrl,
|
||||
},
|
||||
state,
|
||||
pageErrors,
|
||||
consoleErrors,
|
||||
screenshots: {
|
||||
search: SEARCH_SCREENSHOT_PATH,
|
||||
open: OPEN_SCREENSHOT_PATH,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(RESULT_PATH, `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} finally {
|
||||
await browser.close().catch(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(OUTPUT_DIR, "failure.json"),
|
||||
`${JSON.stringify({ ok: false, error: error.stack || error.message || String(error) }, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
console.error(error.stack || error.message || String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user