收口 MinerU OCR 任务链与上下文

This commit is contained in:
lix-2026
2026-06-01 10:30:42 +08:00
parent 1481de2018
commit 610b23d3a5
13 changed files with 710 additions and 55 deletions
@@ -102,6 +102,17 @@ async function saveScreenshot(page, name) {
return target;
}
function cleanupRoot(root) {
for (let attempt = 0; attempt < 5; attempt += 1) {
try {
fs.rmSync(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
return;
} catch (error) {
if (attempt === 4) throw error;
}
}
}
async function main() {
fs.mkdirSync(OUT_DIR, { recursive: true });
const actorId = "mnote-e2e";
@@ -109,7 +120,9 @@ async function main() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-task520-raw-target-"));
const rootUri = fileUrl(root);
const pagePath = "Page.md";
const rawPath = "Page/notes.txt";
const expectOcrContext = process.env.TASK520_OCR_CONTEXT === "1";
const rawPath = expectOcrContext ? "Page/photo.png" : "Page/notes.txt";
const ocrPath = "Page.ocr/photo.png.ocr.md";
const documentId = localMdDocumentId(pagePath);
const captured = [];
let caughtError = null;
@@ -117,7 +130,41 @@ async function main() {
fs.mkdirSync(path.join(root, "Page"), { recursive: true });
writeWorkspaceManifest(root, actorId, workspaceId);
fs.writeFileSync(path.join(root, pagePath), "# Page\n\nTask520 page\n", "utf8");
fs.writeFileSync(path.join(root, rawPath), "Task520 raw resource target\n", "utf8");
if (expectOcrContext) {
fs.writeFileSync(path.join(root, rawPath), Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a]));
fs.mkdirSync(path.join(root, "Page.ocr"), { recursive: true });
fs.writeFileSync(
path.join(root, ocrPath),
"---\nmnote_ocr_version: 1\nprovider: mock\nmodel_version: vlm\nowner_document: ../Page.md\nsource_path: ./Page/photo.png\nsource_root_relative_path: Page/photo.png\nsource_size: 6\nsource_mtime_ms: 1\nstatus: done\ncreated_at: 1\nupdated_at: 1\n---\n\nTask520 OCR sidecar context text\n",
"utf8",
);
fs.writeFileSync(
path.join(root, ".mnote", "ocr-index.json"),
`${JSON.stringify({
version: 1,
entries: {
[rawPath]: {
jobId: "ocr_task520",
ownerDocumentId: documentId,
ownerDocumentPath: pagePath,
sourceRootRelativePath: rawPath,
ocrRootRelativePath: ocrPath,
provider: "mock",
modelVersion: "vlm",
status: "done",
sourceSize: 6,
sourceMtimeMs: 1,
createdAtMs: 1,
updatedAtMs: 1,
plainTextPreview: "Task520 OCR sidecar context text",
},
},
}, null, 2)}\n`,
"utf8",
);
} else {
fs.writeFileSync(path.join(root, rawPath), "Task520 raw resource target\n", "utf8");
}
const browser = await chromium.launch({
headless: process.env.HEADFUL !== "1",
@@ -243,7 +290,8 @@ async function main() {
const targetChip = page.locator("[data-page-ai-target-chip]");
await targetChip.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
const chipText = (await targetChip.innerText({ timeout: UI_TIMEOUT_MS })).trim();
assert(chipText.includes("notes.txt"), `raw resource 打开后 target chip 应指向 notes.txt: ${chipText}`);
const expectedName = expectOcrContext ? "photo.png" : "notes.txt";
assert(chipText.includes(expectedName), `raw resource 打开后 target chip 应指向 ${expectedName}: ${chipText}`);
await page.locator("[data-page-ai-input]").fill("Task520 raw target", { timeout: UI_TIMEOUT_MS });
await page.locator('[data-page-ai-action="send"]').click({ timeout: UI_TIMEOUT_MS });
@@ -268,19 +316,26 @@ async function main() {
assert.strictEqual(runBody.targetPackage?.currentFile?.relativePath, rawPath, `targetPackage currentFile 应指向 raw resource: ${JSON.stringify(runBody.targetPackage)}`);
assert.strictEqual(runBody.targetPackage?.currentFile?.objectIdentity, "resource:file:" + rootUri + ":" + rawPath, `currentFile objectIdentity 不应退化为 [object Object]: ${JSON.stringify(runBody.targetPackage?.currentFile)}`);
assert(runBody.targetPackage?.allowedFiles?.includes(rawPath), `allowedFiles 应只包含 raw resource: ${JSON.stringify(runBody.targetPackage?.allowedFiles)}`);
if (expectOcrContext) {
assert.strictEqual(activeEditorRef.ocrContext?.source, "local_ocr_sidecar", `active_editor 应携带 OCR sidecar context: ${JSON.stringify(activeEditorRef)}`);
assert.strictEqual(activeEditorRef.ocrContext?.ocrRootRelativePath, ocrPath, `active_editor OCR path 不正确: ${JSON.stringify(activeEditorRef.ocrContext)}`);
assert(activeEditorRef.ocrContext?.plainTextPreview?.includes("Task520 OCR sidecar context text"), `active_editor OCR preview 缺失: ${JSON.stringify(activeEditorRef.ocrContext)}`);
assert.strictEqual(runBody.targetPackage?.ocrContext?.ocrRootRelativePath, ocrPath, `targetPackage 应携带 OCR sidecar context: ${JSON.stringify(runBody.targetPackage)}`);
assert.strictEqual(runBody.targetPackage?.currentFile?.ocrRootRelativePath, ocrPath, `currentFile 应携带 OCR sidecar path: ${JSON.stringify(runBody.targetPackage?.currentFile)}`);
}
const screenshot = await saveScreenshot(page, "01-raw-resource-target");
const result = { ok: true, task: TASK, baseUrl: BASE_URL, root, rootUri, documentId, rawPath, screenshot, captured };
const result = { ok: true, task: TASK, baseUrl: BASE_URL, root, rootUri, documentId, rawPath, ocrPath: expectOcrContext ? ocrPath : "", screenshot, captured };
fs.writeFileSync(RESULT_PATH, `${JSON.stringify(result, null, 2)}\n`, "utf8");
console.log(JSON.stringify(result, null, 2));
} catch (error) {
caughtError = error;
await saveScreenshot(page, "failure").catch(() => undefined);
} finally {
fs.rmSync(root, { recursive: true, force: true });
await page.close().catch(() => undefined);
await context.close().catch(() => undefined);
await browser.close().catch(() => undefined);
cleanupRoot(root);
}
if (caughtError) {