Conversation
- ask_handler 完成/失败分别落 info/warn 事件,默认日志级别即可见, 事件显式携带 request_id 与各阶段耗时(doc_recall/chunk_dense/merge/answer) - 中间件把 request-id 注入 RequestId 扩展,审计 metadata 增加 request_id 字段 - 新增 extensions 注入端到端测试(http_tests)
- memori-parser: 行结束事件里嵌套 if 折叠为 match guard(行为等价) - memori-core: format! 参数去掉多余引用(行为等价) - 修复后 workspace clippy -D warnings 全绿
- AppSettings 新增 index_filter 字段(此前 serde 静默丢弃该配置) - replace_engine 在 set_indexing_config 后应用筛选配置 - 新增 2 个反序列化单测固定解析行为
- build_openai_url:endpoint 已显式带 /v1(无尾斜杠)时不再追加, 修复拼成 /v1/v1 导致 404 的问题;新增幂等单测 - 回归 harness 探活:单次 10s 超时改为 3 次重试(每次 20s + 5s 间隔), 本地模型冷载(实测 12s+)不再被误判为服务不可用
- 报告入库:retrieval_regression_v2_judge_report.json(106 应答题全部判分) - answer_correct_rate 0.401(受限配置下限:1.5B 作答 + 无 rerank) - RETRIEVAL_BASELINE_V2.md 新增作答层基线章节,含环境、数字与解读
- GET /api/docs:Swagger UI 页面,数据源 /api/openapi.json - CSS/JS 静态资源构建期内置(include_str!/include_bytes!),离线可用, 符合本地优先原则,不引入 utoipa 依赖 - 新增端到端测试:页面 HTML、静态资源 content-type 与非空校验
- ocr.rs:tesseract 外部进程封装(30s 超时强杀、PSM 4 单列排版、 路径探测 OnceLock 缓存、临时文件原子序号防并发冲突) - 扫描 PDF:无文本层回退提取 XObject 图片 OCR,支持 DCTDecode 直写 与 [ASCII85Decode FlateDecode] 链式解码(含 ASCII85/Hex 解码器) - docx 内嵌图片(word/media)OCR 追加,20MB 大小上限 - 独立图片文件(png/jpg/jpeg)进白名单直接 OCR - 找不到 tesseract 时全链路静默降级,既有行为不变 - settings.json 新增 ocr_tesseract_path,server/desktop 启动时注入 环境变量(显式 env 优先);ocr_smoke 调试工具自动读 settings - v2 套件图片/扫描 4 题:文档召回 0/4 → 4/4
- perf_scale.rs: --start-doc 断点续跑参数(50k 压测期间遗留未提交) - perf_scale.rs: --max-contention-factor 断言,超标非零退出(供 CI 门槛,目标小于 2x) - 新增 .github/workflows/perf-scale.yml:手动触发 + 每周日 02:34 UTC 全量 50k,报告 JSON 上传 artifact - README/PERF_SCALE_50K.md:同步 50k 验证结论与 CI 复跑说明
审查者指南本 PR 将 collab 的 OCR 检索、规模性能验证、Swagger UI、LLM-judge 评测和多项服务器/运行时修复整合到同一批次:OCR 通过可选的外部 Tesseract 处理图片及扫描文档并接入 settings,perf harness 支持续跑和 CI 阈值断言,服务器提供离线内置 Swagger UI 与 request-id 关联的 ask 可观测性,同时修正 endpoint、探活和 index_filter 行为并补充基线文档与测试。 OCR 文档索引序列图sequenceDiagram
participant Indexer
participant Parser
participant Tesseract
participant SearchIndex
Indexer->>Parser: extract_document_text(file_path)
alt Image file
Parser->>Parser: ocr_available()
Parser->>Tesseract: ocr_image_file(path)
Tesseract-->>Parser: Recognized text
else Scanned PDF
Parser->>Parser: extract_pdf_images(path)
Parser->>Tesseract: ocr_image_file(image_path)
Tesseract-->>Parser: Recognized text
end
Parser-->>Indexer: Extracted text
Indexer->>SearchIndex: Index OCR text
请求关联的 ask 可观测性序列图sequenceDiagram
participant Client
participant Middleware
participant AskHandler
participant Engine
participant Audit
Client->>Middleware: HTTP ask request
Middleware->>Middleware: request_id_trace_middleware(req, next)
Middleware->>AskHandler: RequestId extension
AskHandler->>Engine: ask request
Engine-->>AskHandler: AskResponseStructured
AskHandler->>Audit: append_audit_event(request_id)
AskHandler-->>Client: Ask response with x-request-id
alt Ask failure
Engine-->>AskHandler: Engine error
AskHandler-->>Client: ApiError
end
文件级变更
提示与命令与 Sourcery 交互
自定义使用体验访问你的控制面板,即可:
获取帮助Original review guide in EnglishReviewer's Guide本 PR 将 collab 的 OCR 检索、规模性能验证、Swagger UI、LLM-judge 评测和多项服务器/运行时修复整合到同一批次:OCR 通过可选的外部 Tesseract 处理图片及扫描文档并接入 settings,perf harness 支持续跑和 CI 阈值断言,服务器提供离线内置 Swagger UI 与 request-id 关联的 ask 可观测性,同时修正 endpoint、探活和 index_filter 行为并补充基线文档与测试。 Sequence diagram for OCR document indexingsequenceDiagram
participant Indexer
participant Parser
participant Tesseract
participant SearchIndex
Indexer->>Parser: extract_document_text(file_path)
alt Image file
Parser->>Parser: ocr_available()
Parser->>Tesseract: ocr_image_file(path)
Tesseract-->>Parser: Recognized text
else Scanned PDF
Parser->>Parser: extract_pdf_images(path)
Parser->>Tesseract: ocr_image_file(image_path)
Tesseract-->>Parser: Recognized text
end
Parser-->>Indexer: Extracted text
Indexer->>SearchIndex: Index OCR text
Sequence diagram for request-correlated ask observabilitysequenceDiagram
participant Client
participant Middleware
participant AskHandler
participant Engine
participant Audit
Client->>Middleware: HTTP ask request
Middleware->>Middleware: request_id_trace_middleware(req, next)
Middleware->>AskHandler: RequestId extension
AskHandler->>Engine: ask request
Engine-->>AskHandler: AskResponseStructured
AskHandler->>Audit: append_audit_event(request_id)
AskHandler-->>Client: Ask response with x-request-id
alt Ask failure
Engine-->>AskHandler: Engine error
AskHandler-->>Client: ApiError
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
嘿——我发现了 4 个问题
面向 AI Agent 的提示
请处理本次代码审查中的评论:
## 单独评论
### 评论 1
<location path="memori-parser/src/lib.rs" line_range="658" />
<code_context>
+ if ocr::ocr_available()
</code_context>
<issue_to_address>
**issue (bug_risk):** DOCX 嵌入图片的 OCR 在全新进程中总是失败,因为代码将临时图片写入 `temp_dir()/memori-ocr`,但没有创建该目录;`std::fs::write` 会返回错误,图片因此被跳过。
**触发条件:** 在任何 PDF OCR 创建共享临时目录之前,为包含嵌入式 PNG/JPEG 图片的 DOCX 建立索引。
**建议修复:** 在写入 DOCX 临时文件前创建 `memori-ocr` 目录,或将临时文件创建逻辑集中到一个会创建该目录的辅助函数中。
```suggestion
let mut ocr_texts = Vec::new();
let _ = std::fs::create_dir_all(std::env::temp_dir().join("memori-ocr"));
```
</issue_to_address>
### 评论 2
<location path="memori-parser/src/ocr.rs" line_range="82-122" />
<code_context>
+ // spawn + 轮询等待实现超时:tesseract 卡死时强制终止,不拖住索引线程。
</code_context>
<issue_to_address>
**issue (bug_risk):** 子进程的 stdout 已通过管道连接,但直到 `try_wait` 报告 tesseract 已退出后才读取。当 OCR 输出超过操作系统管道缓冲区容量时,tesseract 会在写入 stdout 时阻塞,`try_wait` 永远无法观察到进程退出,最终代码会在 30 秒超时后终止该进程。
**触发条件:** tesseract 生成的识别文本足以填满 stdout 管道时。
**建议修复:** 在等待期间并发排空 stdout,或在工作线程/任务中使用带超时的 `wait_with_output`,并在超时时终止子进程。
</issue_to_address>
### 评论 3
<location path="memori-parser/src/ocr.rs" line_range="87" />
<code_context>
+}
+
+/// 对单张图片执行 OCR(chi_sim 中文)。任何失败返回 None,调用方静默降级。
+pub fn ocr_image_file(path: &Path) -> Option<String> {
+ let tesseract = resolve_tesseract()?;
+ let started = std::time::Instant::now();
+ // spawn + 轮询等待实现超时:tesseract 卡死时强制终止,不拖住索引线程。
+ let mut child = match Command::new(&tesseract)
+ .arg(path)
+ .arg("stdout")
+ .arg("-l")
+ .arg("chi_sim")
+ .arg("--psm")
+ .arg(OCR_PSM)
</code_context>
<issue_to_address>
**issue (bug_risk):** 每次 OCR 调用都要求存在 `chi_sim` traineddata 文件,因此安装了 tesseract 可执行文件但缺少中文语言数据的环境会以非零退出状态失败并返回无文本;因此,所宣传的图片/PDF OCR 功能在仅安装英文语言包的标准 tesseract 环境中不可用。
**触发条件:** tesseract 安装时未包含可选的 `chi_sim` 语言包。
**建议修复:** 使 OCR 语言可配置,或回退到已安装的语言(例如 `eng`),而不是硬编码 `chi_sim`。
```suggestion
.arg("eng")
```
</issue_to_address>
### 评论 4
<location path="memori-server/src/routes/ask.rs" line_range="5-8" />
<code_context>
pub(crate) async fn ask_handler(
State(state): State<ServerState>,
+ Extension(request_id): Extension<RequestId>,
headers: HeaderMap,
Json(payload): Json<AskRequest>,
</code_context>
<issue_to_address>
**issue (broader_impact):** 新的失败事件仅在 `ask_structured` 的错误映射中发出。诸如查询为空、策略拒绝、引擎缺失、会话失败或审计/设置错误等提前失败,会在没有预期的 `warn` 事件、也没有具有关联关系的失败审计记录的情况下返回。
**触发条件:** ask 请求在调用 `engine.ask_structured(...).await` 之前失败,或在该调用之外失败时。
**建议修复:** 围绕处理器结果集中记录/记录日志中的 ask 失败,或在每条提前返回的失败路径中添加相同的 request-id/错误/耗时处理。
</issue_to_address>Sourcery 评估
需要人工审查。 需要优先处理 4 个发现;此外,此更改加入了外部进程 OCR 和新的解析器依赖,并且 OCR 输出在回滚后仍可能保留在搜索索引中。错误的提取结果或配置可能需要清除并重建索引,但其影响是有限且可修复的,并非天然不可逆。
阻塞性发现:memori-parser/src/lib.rs:658、memori-parser/src/ocr.rs:122、memori-parser/src/ocr.rs:87、memori-server/src/routes/ask.rs:8
Original comment in English
Hey - I've found 4 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="memori-parser/src/lib.rs" line_range="658" />
<code_context>
+ if ocr::ocr_available()
</code_context>
<issue_to_address>
**issue (bug_risk):** DOCX embedded-image OCR always fails on a fresh process because the code writes the temporary image under `temp_dir()/memori-ocr` without creating that directory; `std::fs::write` returns an error and the image is skipped.
**Triggers:** When a DOCX containing embedded PNG/JPEG images is indexed before any PDF OCR has created the shared temporary directory.
**Suggested fix:** Create the `memori-ocr` directory before writing DOCX temporary files, or centralize temporary-file creation in a helper that creates it.
```suggestion
let mut ocr_texts = Vec::new();
let _ = std::fs::create_dir_all(std::env::temp_dir().join("memori-ocr"));
```
</issue_to_address>
### Comment 2
<location path="memori-parser/src/ocr.rs" line_range="82-122" />
<code_context>
+ // spawn + 轮询等待实现超时:tesseract 卡死时强制终止,不拖住索引线程。
</code_context>
<issue_to_address>
**issue (bug_risk):** The child process's stdout is piped but is not read until after `try_wait` reports that tesseract exited. When OCR output exceeds the OS pipe buffer, tesseract blocks while writing stdout, `try_wait` never observes exit, and the code kills the process at the 30-second timeout.
**Triggers:** When tesseract produces enough recognized text to fill the stdout pipe.
**Suggested fix:** Drain stdout concurrently while waiting, or use `wait_with_output` in a worker thread/task with a timeout and kill the child on timeout.
</issue_to_address>
### Comment 3
<location path="memori-parser/src/ocr.rs" line_range="87" />
<code_context>
+}
+
+/// 对单张图片执行 OCR(chi_sim 中文)。任何失败返回 None,调用方静默降级。
+pub fn ocr_image_file(path: &Path) -> Option<String> {
+ let tesseract = resolve_tesseract()?;
+ let started = std::time::Instant::now();
+ // spawn + 轮询等待实现超时:tesseract 卡死时强制终止,不拖住索引线程。
+ let mut child = match Command::new(&tesseract)
+ .arg(path)
+ .arg("stdout")
+ .arg("-l")
+ .arg("chi_sim")
+ .arg("--psm")
+ .arg(OCR_PSM)
</code_context>
<issue_to_address>
**issue (bug_risk):** Every OCR invocation requires the `chi_sim` traineddata file, so installations that have the tesseract executable but lack Chinese language data fail with a nonzero exit status and return no text; the advertised image/PDF OCR feature is therefore unavailable on standard English-only tesseract installations.
**Triggers:** When tesseract is installed without the optional `chi_sim` language package.
**Suggested fix:** Make the OCR language configurable or fall back to an installed language such as `eng` instead of hardcoding `chi_sim`.
```suggestion
.arg("eng")
```
</issue_to_address>
### Comment 4
<location path="memori-server/src/routes/ask.rs" line_range="5-8" />
<code_context>
pub(crate) async fn ask_handler(
State(state): State<ServerState>,
+ Extension(request_id): Extension<RequestId>,
headers: HeaderMap,
Json(payload): Json<AskRequest>,
</code_context>
<issue_to_address>
**issue (broader_impact):** The new failure event is emitted only inside the `ask_structured` error mapping. Early failures such as an empty query, policy rejection, missing engine, session failure, or audit/setup errors return without the promised `warn` event and without a correlated failure audit record.
**Triggers:** When an ask request fails before or outside the `engine.ask_structured(...).await` call.
**Suggested fix:** Centralize ask failure recording/logging around the handler result, or add the same request-id/error/elapsed handling to each early-return failure path.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 4 findings to address first, and the change adds external-process OCR and new parser dependencies, and OCR output can remain persisted in the search index after a revert. A bad extraction or configuration can require clearing and rebuilding the index, but the impact is bounded and repairable rather than inherently irreversible.
Blocking findings: memori-parser/src/lib.rs:658, memori-parser/src/ocr.rs:122, memori-parser/src/ocr.rs:87, memori-server/src/routes/ask.rs:8
| .then(|| name.to_string()) | ||
| }) | ||
| .collect(); | ||
| let mut ocr_texts = Vec::new(); |
There was a problem hiding this comment.
issue (bug_risk): DOCX 嵌入图片的 OCR 在全新进程中总是失败,因为代码将临时图片写入 temp_dir()/memori-ocr,但没有创建该目录;std::fs::write 会返回错误,图片因此被跳过。
触发条件: 在任何 PDF OCR 创建共享临时目录之前,为包含嵌入式 PNG/JPEG 图片的 DOCX 建立索引。
建议修复: 在写入 DOCX 临时文件前创建 memori-ocr 目录,或将临时文件创建逻辑集中到一个会创建该目录的辅助函数中。
| let mut ocr_texts = Vec::new(); | |
| let mut ocr_texts = Vec::new(); | |
| let _ = std::fs::create_dir_all(std::env::temp_dir().join("memori-ocr")); |
Original comment in English
issue (bug_risk): DOCX embedded-image OCR always fails on a fresh process because the code writes the temporary image under temp_dir()/memori-ocr without creating that directory; std::fs::write returns an error and the image is skipped.
Triggers: When a DOCX containing embedded PNG/JPEG images is indexed before any PDF OCR has created the shared temporary directory.
Suggested fix: Create the memori-ocr directory before writing DOCX temporary files, or centralize temporary-file creation in a helper that creates it.
| let mut ocr_texts = Vec::new(); | |
| let mut ocr_texts = Vec::new(); | |
| let _ = std::fs::create_dir_all(std::env::temp_dir().join("memori-ocr")); |
| // spawn + 轮询等待实现超时:tesseract 卡死时强制终止,不拖住索引线程。 | ||
| let mut child = match Command::new(&tesseract) | ||
| .arg(path) | ||
| .arg("stdout") | ||
| .arg("-l") | ||
| .arg("chi_sim") | ||
| .arg("--psm") | ||
| .arg(OCR_PSM) | ||
| .stdout(std::process::Stdio::piped()) | ||
| .spawn() | ||
| { | ||
| Ok(child) => child, | ||
| Err(_) => { | ||
| warn!(path = %path.display(), "tesseract 启动失败,跳过 OCR"); | ||
| return None; | ||
| } | ||
| }; | ||
| let deadline = Duration::from_secs(OCR_TIMEOUT_SECS); | ||
| loop { | ||
| if let Ok(Some(status)) = child.try_wait() { | ||
| if !status.success() { | ||
| warn!( | ||
| path = %path.display(), | ||
| status = %status, | ||
| "tesseract 识别失败,跳过 OCR" | ||
| ); | ||
| return None; | ||
| } | ||
| break; | ||
| } | ||
| if started.elapsed() > deadline { | ||
| let _ = child.kill(); | ||
| let _ = child.wait(); | ||
| warn!( | ||
| path = %path.display(), | ||
| timeout_secs = OCR_TIMEOUT_SECS, | ||
| "OCR 超时已终止" | ||
| ); | ||
| return None; | ||
| } | ||
| std::thread::sleep(Duration::from_millis(100)); |
There was a problem hiding this comment.
issue (bug_risk): 子进程的 stdout 已通过管道连接,但直到 try_wait 报告 tesseract 已退出后才读取。当 OCR 输出超过操作系统管道缓冲区容量时,tesseract 会在写入 stdout 时阻塞,try_wait 永远无法观察到进程退出,最终代码会在 30 秒超时后终止该进程。
触发条件: tesseract 生成的识别文本足以填满 stdout 管道时。
建议修复: 在等待期间并发排空 stdout,或在工作线程/任务中使用带超时的 wait_with_output,并在超时时终止子进程。
Original comment in English
issue (bug_risk): The child process's stdout is piped but is not read until after try_wait reports that tesseract exited. When OCR output exceeds the OS pipe buffer, tesseract blocks while writing stdout, try_wait never observes exit, and the code kills the process at the 30-second timeout.
Triggers: When tesseract produces enough recognized text to fill the stdout pipe.
Suggested fix: Drain stdout concurrently while waiting, or use wait_with_output in a worker thread/task with a timeout and kill the child on timeout.
| .arg(path) | ||
| .arg("stdout") | ||
| .arg("-l") | ||
| .arg("chi_sim") |
There was a problem hiding this comment.
issue (bug_risk): 每次 OCR 调用都要求存在 chi_sim traineddata 文件,因此安装了 tesseract 可执行文件但缺少中文语言数据的环境会以非零退出状态失败并返回无文本;因此,所宣传的图片/PDF OCR 功能在仅安装英文语言包的标准 tesseract 环境中不可用。
触发条件: tesseract 安装时未包含可选的 chi_sim 语言包。
建议修复: 使 OCR 语言可配置,或回退到已安装的语言(例如 eng),而不是硬编码 chi_sim。
| .arg("chi_sim") | |
| .arg("eng") |
Original comment in English
issue (bug_risk): Every OCR invocation requires the chi_sim traineddata file, so installations that have the tesseract executable but lack Chinese language data fail with a nonzero exit status and return no text; the advertised image/PDF OCR feature is therefore unavailable on standard English-only tesseract installations.
Triggers: When tesseract is installed without the optional chi_sim language package.
Suggested fix: Make the OCR language configurable or fall back to an installed language such as eng instead of hardcoding chi_sim.
| .arg("chi_sim") | |
| .arg("eng") |
| Extension(request_id): Extension<RequestId>, | ||
| headers: HeaderMap, | ||
| Json(payload): Json<AskRequest>, | ||
| ) -> Result<Json<AskResponseStructured>, ApiError> { |
There was a problem hiding this comment.
issue (broader_impact): 新的失败事件仅在 ask_structured 的错误映射中发出。诸如查询为空、策略拒绝、引擎缺失、会话失败或审计/设置错误等提前失败,会在没有预期的 warn 事件、也没有具有关联关系的失败审计记录的情况下返回。
触发条件: ask 请求在调用 engine.ask_structured(...).await 之前失败,或在该调用之外失败时。
建议修复: 围绕处理器结果集中记录 ask 失败并记录日志,或在每条提前返回的失败路径中添加相同的 request-id/错误/耗时处理。
Original comment in English
issue (broader_impact): The new failure event is emitted only inside the ask_structured error mapping. Early failures such as an empty query, policy rejection, missing engine, session failure, or audit/setup errors return without the promised warn event and without a correlated failure audit record.
Triggers: When an ask request fails before or outside the engine.ask_structured(...).await call.
Suggested fix: Centralize ask failure recording/logging around the handler result, or add the same request-id/error/elapsed handling to each early-return failure path.
FPSZ
left a comment
There was a problem hiding this comment.
感谢这批工作量不小的提交。Swagger UI、trace/request-id、index_filter 对齐、clippy 这几块质量不错。但 OCR 部分(69304d8)目前端到端不工作,另外有两处会往知识库写脏数据 / 拖住 async runtime 的问题,所以先打回。下面按严重度列,都带了文件行号。
🔴 阻断项(必须修)
1. 图片 OCR 在真实索引链路里没接上 — 功能实际不生效
memori-vault/src/lib.rs:29 把 png/jpg/jpeg 加进了 SUPPORTED_CONTENT_EXTENSIONS,memori-parser/src/lib.rs:573 也正确 dispatch 到了 ocr::ocr_image_file。
但 memori-core/src/indexing_rebuild.rs:229 的 read_document_text 的二进制格式白名单没有同步加图片:
if matches!(ext.as_str(), "docx"|"pdf"|"pptx"|"xlsx"|"doc"|"ppt"|"xls") {
// → memori_parser::extract_document_text
} else {
tokio::fs::read_to_string(path).await // ← png/jpg 落到这里
}结果是图片被当 UTF-8 文本读 → InvalidData → OCR 永远不会执行;而且每张图片都会写一次 indexing_runtime.last_error,UI 上会持续报错。
请把 "png" | "jpg" | "jpeg" 加进 read_document_text 的 matches 分支,并补一个走真实索引链路(而不是只调 parser)的集成测试。
2. tesseract stdout 管道死锁 — 文字越多越必然触发
memori-parser/src/ocr.rs:101:stdout 用了 Stdio::piped(),但轮询循环只做 try_wait() + sleep(100ms),全程不读管道,要等子进程退出后才 wait_with_output()。
管道缓冲区一满(Windows 匿名管道通常 4KB,中文 UTF-8 约 1300 字就满),tesseract 会阻塞在 write 上永不退出 → try_wait() 永远返回 None → 30s 后被超时 kill → 返回 None。
也就是说文字密集的页面 100% 失败,而这恰恰是 OCR 唯一有价值的场景;同时每次都要白白烧掉 30 秒超时。
建议改成起一个线程读 stdout,或直接用 wait_with_output() 配合独立的超时看门狗线程;别在不读管道的情况下轮询 try_wait。
🟠 严重
3. DOCX 内嵌图 OCR 临时目录没创建
memori-parser/src/lib.rs:674 往 temp_dir()/memori-ocr 写文件,但只有 PDF 那条路径调了 create_dir_all。干净机器上 DOCX 内嵌图 OCR 会全部静默失败。
4. PDF OCR 兜底同步阻塞 tokio worker
memori-parser/src/lib.rs:720 的 PDF OCR 回退可以从 build_reference_excerpt → build_citations 触发,而这条链是同步跑在 async engine_search 里的。一个被引用的扫描件最多会用 std::thread::sleep 轮询阻塞一个 tokio worker 达 20×30s,而且每次 ask 都重来一遍(没有缓存)。
问答链路上不应该做 OCR。建议只在索引期做 OCR 并把结果落库,ask 期直接读已抽取文本。
5. 解压无上限,可 OOM
memori-parser/src/ocr.rs:249 的尺寸上限只卡了压缩流,ZlibDecoder::read_to_end 是无界的。一个 20MB 的 flate 图可以膨胀到几十 GB,直接把索引进程打爆。请用 take(limit) 限制解压后大小。
6. 忽略 /BitsPerComponent,会把 OCR 噪声当文档内容索引
memori-parser/src/ocr.rs:335 没有校验 /BitsPerComponent,16-bit 图能通过长度检查并被当成 8-bit 重新解释,生成一张乱码 PNG。OCR 出来的噪声文本会被当作真实文档内容写进知识库。
这条对本项目是原则性问题——证据链的可信度是核心卖点,宁可跳过也不能索引噪声。请显式只接受 BitsPerComponent == 8,其余跳过。
7. 无文本层 PDF 的返回语义变了
memori-parser/src/lib.rs:721 由 Some("") 改成了 None。没装 tesseract 的环境下,每个扫描件都会报 "文件读取失败(可能被占用)" 并污染 last_error——这个错误信息会误导用户去查文件占用。建议保持 Some(""),或给出"无文本层且未配置 OCR"的专门错误分类。
🟡 次要
memori-parser/src/ocr.rs:41— tesseract 路径锁进OnceLock且apply_ocr_path_to_env不覆盖已有 env,改错了配置必须重启整个应用才能生效;env 为空字符串时还会顺带禁掉 PATH 回退。.github/workflows/perf-scale.yml:66— artifact 上传缺if: always(),恰恰在--max-contention-factor断言 FAIL 时丢掉报告,而那正是唯一需要看报告的时候。memori-parser/src/lib.rs:665—read_to_end失败时会穿过尺寸护栏,对截断的字节做 OCR,应该直接跳过。memori-core/examples/perf_scale.rs:377—--start-doc低于实际进度时 resume 会重复计数,虚报indexed_chunks和 chunks/s,压测数字会偏乐观。
✅ 这些部分没问题
- request-id extension 在
build_router里层级正确,Extension<RequestId>保证存在。 - server 端
index_filter的应用语义与桌面端一致。 build_openai_url的/v1去重逻辑正确,尾斜杠和#的场景都覆盖到了。finish_full_rebuild只改元数据,压测 resume 路径不会误删数据。- Swagger UI(离线资源)、LLM-judge 126 题基线文档、clippy 修复都 OK。
建议的处理方式
把这个 PR 拆开:
- 可以先合:
083c16eSwagger UI、cac8442trace/request-id、f764abeindex_filter、8d7eaa2clippy、3ccae63/v1修复、3fcbbbdLLM-judge 文档。这几块独立且干净。 - OCR(
69304d8)单独开一个 PR 返工,至少修掉 #1、#2、#5、#6,并补一个跑真实索引链路的端到端测试(现在的测试只覆盖到 parser 层,所以 #1 才没被发现)。 - 压测 harness(
af6ba4f) 修掉if: always()和 resume 计数即可合。
另外提醒一下:docs/qa/retrieval_regression_v2_judge_report.json 有 7223 行,vendored 的 swagger-ui 压缩产物也一并入库了。judge 报告建议只留摘要,明细走 CI artifact;swagger-ui 资源如果确定要 vendor,请在 README 或该目录下注明版本号和来源,方便后续跟安全更新。
本 PR 包含 collab 上的 8 个提交:
说明:已合并上游最新 collab(含 vite ^7.3.6 安全修复),不会回退该修复。
Sourcery 总结
提升协作检索覆盖率、API 易用性、可观测性和大规模质量验证能力,同时增强运行时稳定性。
新功能:
Bug 修复:
/v1端点片段,并避免模型冷启动期间出现错误的回归健康检查失败。改进:
CI:
文档:
测试:
杂项:
Original summary in English
Summary by Sourcery
Improve collab retrieval coverage, API usability, observability, and large-scale quality validation while hardening runtime behavior.
New Features:
Bug Fixes:
Enhancements:
CI:
Documentation:
Tests:
Chores: