Files
mnote/bugs/03-rust-web/done/3-27-mineru-ocr-design-without-runtime-implementation-v1.md

118 lines
9.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 3-27 MinerU OCR 后端 runtime 已落地但缺前端任务链
## 状态
- 状态:done
- Owner03-rust-web / local-folder OCR / MinerU sidecar
- 发现时间:2026-05-31
## 现象
P2 目标中包含 MinerU OCR 能力收口。当前已补后端 mock route、真实 MinerU HTTP client、sidecar 写入、`.mnote/ocr-index.json``includeOcr=true` 搜索命中、resource tab 图片/PDF OCR 工具条、附件/File Tree 资源菜单入口、active job store、`local_ocr.job.updated` realtime event、全局任务栏/任务抽屉、AI OCR sidecar context 和浏览器 UI smoke。
## 证据
- `rust/crates/mnote-web/src/routes/local_ocr.rs` 已注册 `jobs/status/read/insert``provider=mock` 能写入 `{pageStem}.ocr/*.ocr.md``.mnote/ocr-index.json`
- `provider=mineru` 在有 token 时会走真实 HTTP client:申请上传 URL、PUT 上传、轮询结果、下载 zip、提取 Markdown,并写入 `{pageStem}.ocr/*.ocr.md` / `.mnote/ocr-index.json`。该路径已有本地 HTTP mock 单测覆盖,不访问外网。
- `local_search_index` 已支持 `includeOcr=true` 命中 OCR owner page;增量刷新也已排除 hash 后缀 OCR sidecar,不再把 `*.ocr.md` 当普通页面命中。
- `task526-local-folder-ocr-api-smoke.js` 已覆盖浏览器上下文调用 mock OCR API、图片 resource tab 手动 OCR、工具条状态、打开 OCR、搜索命中和显式 insert 链接。
- 活动任务生命周期、`local_ocr.job.updated`、全局任务抽屉和 AI OCR context 已完成并有测试/smoke 覆盖。
## 影响
- 用户已能从本地图片/PDF resource tab、附件菜单和 File Tree 资源菜单触发 OCR;离开当前资源后可通过全局 OCR 任务抽屉查看状态、打开 OCR、对失败/stale 任务重试。
- 搜索的 `includeOcr=true` 已能命中 OCR sidecar`includeOcr=false` 不应命中普通 OCR sidecar;本轮补了增量索引过滤测试。
- AI image/PDF resource context 会优先读取已有 OCR Markdown,并把 OCR context 附到 active_editor contextRef 与 targetPackage。
## 下一步
1. 已完成后端 mock 最小闭环:路径规划、sidecar frontmatter、`.mnote/ocr-index.json`、mock OCR job route、status/read、Page Tree 过滤和 `includeOcr=true` 搜索 owner page。
2. 已补浏览器 API smokemock OCR、status/read/jobs、`includeOcr=true` 搜索 owner page 和显式 insert link。
3. 已接长任务状态、`local_ocr.job.updated` realtime 事件、全局 OCR 任务栏/任务抽屉和 AI 资源上下文读取 OCR sidecar。
## 验收
- [x] Rust route/helper 测试覆盖 sidecar 路径/frontmatter、index stale/error redaction、mock OCR job 写入、status/read、无 token、越界拒绝、非图片/PDF 拒绝、mock 失败 response 脱敏和显式 insert link。
- [x] local search 测试覆盖 OCR 命中返回 owner page,普通与 hash 后缀 OCR sidecar 不作为普通页面。
- [x] Page Tree 测试覆盖 OCR sidecar 可在 File Tree 作为资源文件可见,但不作为普通页面进入 Page Tree。
- [x] Rust route 测试覆盖无 token response 形态。
- [x] Rust route 测试覆盖 `provider=mineru` 后端成功路径:申请上传 URL、PUT 上传、轮询结果、下载 zip、提取 Markdown、写 sidecar/index。
- [x] 浏览器 smoke 覆盖图片/PDF 手动 OCR、任务栏状态、打开 OCR、搜索 OCR 命中和插入 OCR 链接。
## 2026-06-01 后端 mock 闭环记录
新增 `rust/crates/mnote-web/src/routes/local_ocr.rs`,注册:
- `POST /api/local-folder/ocr/jobs`
- `GET /api/local-folder/ocr/jobs`
- `GET /api/local-folder/ocr/status`
- `GET /api/local-folder/ocr/read`
- `POST /api/local-folder/ocr/insert`
当前真实 MinerU HTTP client 仍未接入;`provider=mock` 用于本地后端闭环和测试,`provider=mineru` 在缺少 token 时返回 `mineru_token_missing`,有 token 时返回 `mineru_runtime_not_enabled`,避免伪装真实能力已完成。
已通过验证:
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1`
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_ocr -- --test-threads=1`
2026-06-01 续补 route 边界测试:
- 越界来源路径拒绝:`local_ocr_path_escape`
- 非图片/PDF 来源拒绝:`local_ocr_source_type_unsupported`
- provider `mineru` 且缺 token 时拒绝:`mineru_token_missing`
- mock 失败响应与 `.mnote/ocr-index.json` 失败条目统一脱敏为 `provider_error_redacted`
- 显式 `ocr/insert` link 模式把 `[OCRphoto.png](./Page.ocr/photo.png.ocr.md)` 追加进 owner Markdown;未调用 insert 时不改正文
本文继续保持 `process`:全局任务栏、active job store / realtime event 和 AI 资源上下文尚未完成。
2026-06-01 只读复核更新:
- Subagent 复核确认:route/mock/search 测试已落地,早期“没有实际 OCR route”的描述已经过时。
- 本 bug 不归档的阻塞点更新为:active job store / `local_ocr.job.updated`、全局任务栏/任务抽屉、AI 资源上下文读取 OCR sidecar 和完整 UI browser smoke。
2026-06-01 浏览器 API smoke 续补:
- 新增 `scripts/task526-local-folder-ocr-api-smoke.js`,在真实浏览器登录态页面内通过 UI 和 `fetch` 串起图片 resource tab OCR 工具条、`POST /api/local-folder/ocr/jobs``status``read``jobs``/api/search/documents includeOcr=true``POST /api/local-folder/ocr/insert`
- 已验证 `provider=mock` 会生成 `docs/Page.ocr/photo.png.ocr.md`resource tab 工具条显示 OCR 完成并可打开/插入;`includeOcr=false` 不命中 OCR 文本,`includeOcr=true` 返回 owner page 且带 `hasOcr/ocrEvidence`,显式 insert 才向 owner Markdown 追加 `[OCRphoto.png](...)`
- 续补后已验证 OCR sidecar 可作为 Markdown resource tab 打开,截图写入 `tmp/task526-local-folder-ocr-api-smoke/03-ocr-resource-tab.png`OCR 工具条截图写入 `tmp/task526-local-folder-ocr-api-smoke/02-ocr-toolbar.png`
- 已通过:
- `node --check scripts/task526-local-folder-ocr-api-smoke.js`
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task526-local-folder-ocr-api-smoke.js`
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1`
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_ocr -- --test-threads=1`
2026-06-01 MinerU API 口径复核:
- 官方文档当前仍以 `https://mineru.net/api/v4/extract/task``https://mineru.net/api/v4/file-urls/batch` 作为精准解析入口;`model_version` 支持 `pipeline` / `vlm` / `MinerU-HTML`Markdown/JSON 为默认结果格式,图片/PDF/Office 等文档受文件大小与页数限制。
- 当前设计稿中的“申请上传地址 -> PUT 上传 -> 轮询批量结果 -> 下载 zip -> 提取 Markdown”方向仍成立。
- 旧回归测试曾要求 `MNOTE_MINERU_API_TOKEN` 存在且 provider 为 `mineru` 时返回 `mineru_runtime_not_enabled`;该口径已被真实后端 runtime 合同测试替代。
## 2026-06-01 后端 MinerU runtime 补齐记录
- `rust/crates/mnote-web/src/routes/local_ocr.rs` 已补 `mineru_api_base_url``mineru_poll_interval``mineru_max_polls` 配置函数。
- `provider=mineru` 后端路径已接入 `run_mineru_ocr`,执行申请上传 URL、PUT 上传、轮询 batch 结果、下载 zip、提取 Markdown。
- 新增本地 HTTP mock 测试 `local_ocr_jobs_route_runs_mineru_runtime_against_http_mock`,不访问外网,验证真实 client 合同和 sidecar/index 写入。
- 已通过:
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1`
## 2026-06-01 OCR P2 收口记录
- `AppState` 新增 OCR active job store 与 `local_ocr_job_tx``POST /api/local-folder/ocr/jobs` 在 queued / uploading / mineru_processing / downloading / writing_sidecar / done / failed 阶段更新 index 并广播 `local_ocr.job.updated`
- `/api/local-folder/events` 会把同 root 的 OCR job update 作为 SSE 事件透出。
- `document-resource-tab-runtime.js` 新增全局 OCR 任务 dock / drawer,从 jobs API 恢复状态,并通过 `local_ocr.job.updated` 更新;支持打开 OCR sidecar 与失败/stale 重试。
- `sidebar-page-ai-target-runtime.js` / `sidebar-page-ai-runtime.js` 在 Page AI run 前读取 active image/PDF 资源已有 OCR sidecar,把 `mnote.local_ocr_context.v1` 放入 active_editor contextRef、targetPackage、currentFile 和 primary target。
- `task526-local-folder-ocr-api-smoke.js` 覆盖全局 OCR 任务抽屉状态。
- `TASK520_OCR_CONTEXT=1 node scripts/task520-page-ai-raw-resource-target-smoke.js` 覆盖 Page AI OCR sidecar context。
- 已通过:
- `node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js`
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-target-runtime.js`
- `node --check scripts/task526-local-folder-ocr-api-smoke.js`
- `node --check scripts/task520-page-ai-raw-resource-target-smoke.js`
- `cargo fmt --check --all`
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1`
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_ocr -- --test-threads=1`
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3000 node scripts/task526-local-folder-ocr-api-smoke.js`
- `TASK520_OCR_CONTEXT=1 MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3000 node scripts/task520-page-ai-raw-resource-target-smoke.js`