@dylanl1/dsh-plugin-pdf 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NATIVE_V1.md +3 -1
- package/README.md +54 -114
- package/README.zh.md +61 -121
- package/SPEC.md +46 -51
- package/dist/THIRD_PARTY_NOTICES.md +1 -56
- package/dist/client.js +5045 -2779
- package/dist/embedding.js +34 -9
- package/dist/engines.js +49 -4
- package/dist/image-host.js +837 -505
- package/dist/index.js +6790 -4126
- package/dist/ocr-host.js +1036 -175
- package/dist/ocr.js +4 -1
- package/dist/translation.js +43 -14
- package/dist/types/adapters/host/dsh/agent-tools.d.ts +1 -1
- package/dist/types/adapters/host/dsh/document-status.d.ts +0 -1
- package/dist/types/adapters/host/dsh/form-guidance.d.ts +2 -2
- package/dist/types/adapters/host/dsh/image-backends.d.ts +19 -3
- package/dist/types/adapters/host/dsh/image-tools.d.ts +15 -0
- package/dist/types/adapters/host/dsh/image-transport.d.ts +8 -7
- package/dist/types/adapters/host/dsh/index.d.ts +3 -2
- package/dist/types/adapters/host/dsh/model-cache-identity.d.ts +26 -0
- package/dist/types/adapters/host/dsh/ocr-session-llm.d.ts +2 -3
- package/dist/types/adapters/host/dsh/ocr-tool.d.ts +4 -2
- package/dist/types/adapters/host/dsh/page-content.d.ts +27 -0
- package/dist/types/adapters/host/dsh/pdf-evidence-meta.d.ts +13 -148
- package/dist/types/adapters/host/dsh/retrieval-prefetch.d.ts +49 -0
- package/dist/types/adapters/host/dsh/retrieval.d.ts +132 -25
- package/dist/types/adapters/host/dsh/translation-model.d.ts +3 -5
- package/dist/types/adapters/host/ocr/local-backend.d.ts +4 -0
- package/dist/types/contracts/indexing.d.ts +156 -0
- package/dist/types/embedding/registry.d.ts +2 -1
- package/dist/types/embedding/types.d.ts +10 -2
- package/dist/types/entrypoints/image-understanding-host.d.ts +2 -8
- package/dist/types/host/image-understanding-plugin.d.ts +1 -1
- package/dist/types/modules/document/index.d.ts +7 -0
- package/dist/types/modules/document/page-content-error.d.ts +3 -0
- package/dist/types/modules/document/page-content-native.d.ts +10 -0
- package/dist/types/modules/document/page-content-ocr.d.ts +5 -0
- package/dist/types/modules/document/page-content-text.d.ts +12 -0
- package/dist/types/modules/document/page-content.d.ts +158 -0
- package/dist/types/modules/engines/cache-identity.d.ts +16 -0
- package/dist/types/modules/engines/catalog.d.ts +20 -0
- package/dist/types/modules/engines/directory.d.ts +1 -0
- package/dist/types/modules/engines/index.d.ts +3 -0
- package/dist/types/modules/engines/serialization.d.ts +3 -0
- package/dist/types/modules/engines/types.d.ts +13 -0
- package/dist/types/modules/evidence/agent-page-image.d.ts +9 -112
- package/dist/types/modules/evidence/agent-page-read.d.ts +36 -2
- package/dist/types/modules/evidence/image-capture.d.ts +1 -6
- package/dist/types/modules/evidence/index.d.ts +9 -136
- package/dist/types/modules/evidence/provenance.d.ts +8 -0
- package/dist/types/modules/evidence/reference-store.d.ts +91 -1
- package/dist/types/modules/evidence/status.d.ts +17 -0
- package/dist/types/modules/evidence/text-range.d.ts +28 -0
- package/dist/types/modules/evidence/text-target.d.ts +7 -1
- package/dist/types/modules/forms/index.d.ts +4 -4
- package/dist/types/modules/forms/inspection-references.d.ts +1 -1
- package/dist/types/modules/forms/validation.d.ts +4 -8
- package/dist/types/modules/image-understanding/service.d.ts +24 -42
- package/dist/types/modules/model-write/index.d.ts +1 -2
- package/dist/types/modules/ocr/service.d.ts +51 -3
- package/dist/types/modules/retrieval/cache.d.ts +44 -2
- package/dist/types/modules/retrieval/document-policy.d.ts +83 -0
- package/dist/types/modules/retrieval/index.d.ts +4 -1
- package/dist/types/modules/retrieval/page-content.d.ts +4 -0
- package/dist/types/modules/retrieval/paragraphs.d.ts +1 -12
- package/dist/types/modules/retrieval/shared-source.d.ts +25 -0
- package/dist/types/modules/retrieval/text.d.ts +19 -1
- package/dist/types/modules/retrieval/types.d.ts +83 -9
- package/dist/types/modules/retrieval/vector.d.ts +3 -0
- package/dist/types/modules/working-copy/index.d.ts +1 -1
- package/dist/types/modules/working-copy/ports.d.ts +11 -0
- package/dist/types/modules/working-copy/service.d.ts +13 -1
- package/dist/types/ocr/types.d.ts +2 -1
- package/dist/types/shared/contracts.d.ts +4 -0
- package/dist/types/shared/geometry/native-viewport.d.ts +0 -18
- package/dist/types/shared/geometry/text-target.d.ts +2 -6
- package/dist/types/shared/native.d.ts +2 -1
- package/dist/types/shared/passive-poll.d.ts +4 -0
- package/dist/types/translation/locales.d.ts +4 -3
- package/dist/types/translation/schema.d.ts +14 -0
- package/dist/types/translation/types.d.ts +12 -2
- package/docs/AI_READER_TOOLS.md +87 -210
- package/docs/ARCHITECTURE_V2.md +64 -18
- package/docs/ENGINE_EXTENSIONS.md +295 -25
- package/docs/RETRIEVAL_EXPERIMENT.md +169 -47
- package/docs/UX_TYPOGRAPHY.md +3 -3
- package/package.json +13 -13
- package/dist/types/adapters/host/dsh/image-auxiliary.d.ts +0 -20
package/docs/ARCHITECTURE_V2.md
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
> 状态:源码分层、Client Reader 拆分、旧 V1 草稿读取、普通 PDF 评论回复解析/编辑、AcroForm 用户填写、浏览器保存重开、文字与图像来源引用、解释模式 Host 证据链、模型草稿编辑及经人类审批的保存工具已接入;真实 Desktop、外部阅读器互操作、旧版二进制回退、跨平台 CI 与端到端性能仍需验收。本文保留 2026-10-18 的设计目标与迁移基线;标为“目标”“后续”的条款不代表已实现。工具的逐项状态见 [AI_READER_TOOLS.md](./AI_READER_TOOLS.md)。
|
|
4
4
|
|
|
5
|
-
当前入口位于 `src/entrypoints/`,DSH、文件、PDFium、pdf-lib 和存储接入位于 `src/adapters/`,业务能力位于 `src/modules/`,跨端契约和纯几何/缓存分别位于 `src/contracts/` 与 `src/shared
|
|
5
|
+
当前入口位于 `src/entrypoints/`,DSH、文件、PDFium、pdf-lib 和存储接入位于 `src/adapters/`,业务能力位于 `src/modules/`,跨端契约和纯几何/缓存分别位于 `src/contracts/` 与 `src/shared/`。`src/client/` 负责页面组合与样式,`src/core/pdf-types.ts` 定义共享 PDF 数据,`src/navigation/` 提供纯视口历史,`src/engine/framing.ts` 实现原生协议;这些文件仍有实际职责。`src/host/` 仅保留已发布 OCR/Image SDK 的类型入口。`npm run arch:report` 可重复生成当前行数、复杂度代理值和模块依赖图;测试目录已按 `tests/modules/`、`tests/adapters/`、`tests/contracts/`、`tests/entrypoints/` 组织,浏览器 E2E 与原生集成另测。
|
|
6
|
+
|
|
7
|
+
2026-10-28 清理:删除无生产消费者的 Client 入口和 viewport 转发、Host 工作副本/阅读位置装配包装;测试与 benchmark 的便捷装配归到 `tests/fixtures/`,浏览器启动归到 `tests/browser/`。批注线程只保留生产使用的索引实现,文字定位与图片编码测试也直接覆盖当前范围定位和 Host 验证字节路径。同步清除失效 CSS、重复菜单监听和旧表单 `valueContract`;TypeScript 检查禁止不可达语句和无用标签。
|
|
8
|
+
|
|
9
|
+
Web 回归发现自动适宽的 DOM 锚点会随虚拟页面卸载而失效,现由 `WindowedPages` 统一持有数值布局锚点,尺寸 Hook 仅上报宽高;导航按实时视口和最新数值布局定位,避免侧栏动画期间使用过期尺寸。
|
|
10
|
+
|
|
11
|
+
公开扩展 API、V1 草稿兼容、原生回调测试及检索 benchmark 的历史对照保留。生产 TS/TSX 由 302 个文件、31,411 物理行减至 298 个文件、31,222 行;静态决策复杂度超过 20 的函数仍有 68 个,主要集中在文档写入、批注布局、工作副本与检索生命周期。它们不是死代码,后续应按具体状态与操作边界继续审查,不以拆文件或缩行代替简化。
|
|
12
|
+
|
|
13
|
+
本轮本地复验:`npm run check` 的源码、测试及 benchmark 类型检查、构建均通过,Node 测试 1,504 项通过、1 项浏览器 Worker 因默认安装路径缺失而条件跳过;指定本机 Chrome 补跑 Worker 1 项通过,完整 Web 交互 38 项通过。模块 DAG 与跨进程运行时依赖检查通过。原生取消测试使用真实子进程入站确认和回复放行门闩,另保留跨平台的同步流单测,避免依赖父进程 tick 推断子进程进度。本轮未重新采集项目行覆盖率。
|
|
14
|
+
|
|
15
|
+
当前源码的模型工具按单一职责重新命名并删除重复入口,不注册旧名称别名;完整契约见 [AI_READER_TOOLS.md](./AI_READER_TOOLS.md)。原子动作由模型组合,UI 专属区域 OCR 仍走同一识别服务。此次工具入口调整不改动草稿、文件或会话数据格式。
|
|
6
16
|
|
|
7
17
|
## 设计决定
|
|
8
18
|
|
|
@@ -43,19 +53,37 @@ Cordis `Context`、DSH Remote/Slots/Tools、文件沙箱和原生 worker 的具
|
|
|
43
53
|
|
|
44
54
|
读页、搜索、OCR、图像理解不会暗中移动用户视图;导航是单独的 Client 动作。Host 的“后台打开文档”与 Client 的“在侧栏显示文档”仍是两个操作。
|
|
45
55
|
|
|
46
|
-
###
|
|
56
|
+
### 混合页面内容检索(实验功能)
|
|
57
|
+
|
|
58
|
+
`modules/document` 拥有不可变、可验证的 `PageContent`:源字节摘要、页号、提取身份、页矩形、精确拼接的原生文字/摘要/段落偏移,以及内容块和各块的多种表示。表示区分 `native-text`、`ocr-text` 与 `image-description`,保留文字摘要、生产者/版本/模型/配置身份、实际干净渲染输入摘要、可选 UTF-16 几何 span、完成状态及警告。块 ID 由源摘要、页和提取身份限定,独立于会话证据 `refId`。运行时 Zod 校验拒绝损坏摘要、无效偏移和超限内容;每页文字及表示总文字各限 500,000 UTF-16、总 span 限 10,000,缓存不复制 PNG。
|
|
47
59
|
|
|
48
|
-
`modules/retrieval`
|
|
60
|
+
`modules/retrieval` 只拥有从内容表示投影出的可重建索引,不拥有第二份文档或草稿。Host 从共享工作副本取得已授权快照,组合 PDFium 原生文字与所选 OCR/图像服务,保留原始 UTF-16 范围和 PDF 来源位置;分块、BM25、向量排名与候选合并均在独立模块中。首版固定提供整页 `visual-region` 后备块,不推断独立插图、表格或公式版面;空原生文字层仍是有效页面。`pdf_semantic_search` 的默认范围是全文,内部有界分步处理;`building` 报告覆盖和进度,处理完请求范围后对可用内容统一排名,局部失败仍以 `incomplete` 返回。公开续调仅使用短 `searchId: "rel_…"`;Host 保存初始查询、范围、进度及源/引擎代次约束。旧版逐窗口关键词排序路径已删除。
|
|
61
|
+
|
|
62
|
+
可选 OCR/图像描述失败但已有有效原生或 OCR 文字时,`PageContent`/`RetrievalPage.failure` 保留失败原因和可用表示,索引继续收录这些内容;失败内容模型不写入持久缓存。结果同时列入 `coverage.failedPages` 并保持 `status: incomplete`,可以返回现有内容的排名候选,但 `hasMore: false` 表示没有可继续的构建步骤,不生成虚假续调游标。此状态不能宣称索引整体就绪或覆盖完整,须显式重建该文档以重试失败的预处理。没有任何有效文字的失败页仍遵循原有自动重试流程;成功的空 OCR 输出不属于失败。
|
|
63
|
+
|
|
64
|
+
图片处理只保留 OCR 与图像理解两个执行契约。OCR 接受图像并返回转写文字及可选位置/置信度;图像理解接受图像与提示词,始终返回实际生成的文字及来源。内置 `dsh-vision` 统一当前会话与指定 provider/model 的视觉调用,不再注册 OCR 图像桥接或直接传图后端。直接附图由解释/会话编排负责;文本会话使用配置的图像理解引擎生成派生文字,不静默降级到 OCR。
|
|
65
|
+
|
|
66
|
+
`indexScanPages`(文字识别索引)默认 `true`,仅在原生文字缺失或编码损坏时调用全局 `ocrEngine`;成功空转写是有效结果。`indexImageDescriptions`(视觉描述索引)默认 `false`,使用全局 `imageEngine` 与已生效配置,通过索引提示词生成整页描述再索引,并非嵌入原图。每份 PDF 只独立保存这两个开关,可同时开启;引擎在 PDF 设置中统一选择,复用同一 OCR/图像理解服务目录,不另建视觉描述服务或注册表。描述增加模型调用、耗时及费用,两项均不会开启默认关闭的 embedding。文档的当前生效策略启用语义检索且引擎可用时,打开文档的后台预建与手动/搜索入口共用处理链路;待应用选择须显式更新才会激活。Reader 的本地字符映射修复是另一个固定用途。可运行的第三方扩展及生命周期测试见 [`examples/http-engines`](../examples/http-engines) 与 [`tests/examples/engine-extensions.test.ts`](../tests/examples/engine-extensions.test.ts)。
|
|
49
67
|
|
|
50
68
|
检索分块保留空行和原生排版中可辨认的段落边界,短段落不因整页不足长度上限而合并;长段落才继续按长度与重叠窗口拆分。边界使用原始拼接文字的 UTF-16 偏移,不改写文字或重排多栏。派生缓存包含边界信息,分块版本变化时重建索引;批量向量化、文本去重和原有资源预算继续生效。无法可靠区分段落的版面仍返回原始范围,不能据此声称已经识别用户想标注的子句。
|
|
51
69
|
|
|
52
|
-
检索上下文和标注选段是不同粒度:裸 `refId`
|
|
70
|
+
检索上下文和标注选段是不同粒度:裸 `refId` 始终代表完整快照。模型需要其中一段时先用 `pdf_evidence_read` 在父引用内唯一匹配精确子引文,取得新引用;连续跨页范围也在该入口解析。标注与导航仅消费最终引用,Host 在执行时仍重验来源、整页摘要、连续范围和 native 几何。不得退回全页搜索同文、静默缩短范围或漏掉中间页,所有页标注进入唯一工作副本事务。
|
|
71
|
+
|
|
72
|
+
`pdf_semantic_search` 与 `pdf_keyword_search` 共用初调/续调的对外形态:首调传 `documentId`、`query` 和各自的查询选项,响应包含 `searchId` 与 `hasMore`;`hasMore: true` 时仅以 `{searchId}` 续调同一工具,不再公开或接收搜索游标,也不重复初始参数。Host 按会话和工具隔离句柄,保留授权与源版本重验,重置、TTL 或预算驱逐后失效;未知/过期 ID 要求完整初调重新开始。普通文字搜索的 `txt_…` ID 保存匹配分页位置,相关检索的 `rel_…` ID 保存索引构建进度;两者不互通,也不改变列表工具的游标契约。语义入口要求可用 embedding,关键词入口负责 exact/normalized 匹配。
|
|
73
|
+
|
|
74
|
+
Host 的 `pdfEmbedding` 使用与其他引擎相同的注册/注销和动态设置目录,默认关闭;本地 Qwen 实现通过独立的回环 HTTP 服务运行,不加载聊天模型或修改其数据库。向量缓存按表示文字及 embedding 模型指纹复用;页面内容另按源字节、提取管线、干净图像输入与 OCR/图像生产者身份复用。OCR/图像后端可提供可选 `cacheIdentity`:稳定字符串允许跨重启复用,未提供或明确声明不可持久复用时加入会话及注册期隔离,不能把可变会话模型按同一引擎名永久复用。配置变化、卸载或源变化使旧请求失效,注册代次仍校验异步返回。索引有内存/数量/并发预算,页面、向量的持久派生缓存使用独立的 `pdf_retrieval_<namespace hash>` Storage Domain,不能作为授权或草稿事实;初始化读取同样清理过期与超限数据。
|
|
75
|
+
|
|
76
|
+
索引构建步骤拥有自己的工作副本租约,查询和插件卸载使用取消与排空边界。返回原生候选前重新核对原文,再交给现有 evidence 引用存储;原生引用的精确标注和跳转继续由当前原生字形定位器校验。OCR/描述候选保存派生摘要、生产者和页/区域来源,精度为近似区域或整页,不能用于原生精确文字标注,也不能拿其本地偏移去切原生页文字。写入共享草稿、保存审批和阅读回退均复用既有能力。描述可以让图像内容进入检索,但不保证识别图表/公式结构;历史实验与性能测量见 [RETRIEVAL_EXPERIMENT.md](./RETRIEVAL_EXPERIMENT.md),不能用原生文字基准代替新预处理的实测。
|
|
53
77
|
|
|
54
|
-
|
|
78
|
+
当前 PDF 的手动建立/重建入口由搜索面板承载,细轨仅保留共用任务入口。Host 拥有索引状态、构建次数和取消;Client 的会话共享订阅记录已关闭的完成通知,不改变 Host 的 ready 状态。重复轮询或切换同会话的 PDF 视图不会使同次完成通知重新出现,新一轮构建仍显示。重建只重置相同来源版本与选中引擎的派生索引,排空旧构建后重新读取页面和计算向量,不清空无关文档的缓存。
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
### 文档索引策略与增量更新
|
|
57
81
|
|
|
58
|
-
|
|
82
|
+
`modules/retrieval/document-policy` 在独立的 `pdf_index_policies` Storage Domain 中按规范路径保存每份 PDF 的两个索引开关及当前生效的完整计划。新文档使用 profile patch 的开关默认值,之后独立保存;引擎选择和连接统一来自全局设置,不提供文档级引擎覆盖。读取旧记录时保留两个开关、移除旧目标策略中的引擎覆盖,已生效计划仍作为可复用的冻结快照。保存文件改变源摘要也不重置偏好。同一路径在已授权会话间共享偏好,草稿、权限、查询和引用仍按会话隔离。Client 搜索面板使用修订比较防止覆盖其他视图,只提交两个开关;引擎与连接在设置页自动保存。
|
|
83
|
+
|
|
84
|
+
`adapters/host/dsh/document-indexes` 按不可变配置计划隔离正在查询的索引与候选构建,同计划复用运行时;最多四个运行时,每个运行时最多两个索引、索引总预算 64 MiB、两个并发构建和八个已接纳查询,空闲实例按 LRU 释放。这些是单运行时限额,四个实例合计最多八个索引、256 MiB 索引预算,按实际数据增长而非预先分配;后台文档预建仍一次处理一份。预算不包含全部进程内存。普通配置修改只保存目标策略,显式“更新索引”才开始增量处理;完整构建成功且目标未变才激活。失败或取消保留原计划,停用立即生效且不删除派生缓存。关闭最后一个视图/模型引用后释放路径相关的瞬态状态和偏好内存缓存,持久偏好保留。
|
|
85
|
+
|
|
86
|
+
原生文字、OCR 和图像描述按来源版本及各自生产者身份独立缓存,再组合为 `PageContent`。改变一种增强方式不会使另一种成功结果失效。文档向量使用可选的 `documentFingerprint`;查询指令与执行批大小不必使相同文档向量失效,完整请求配置仍用于并发围栏。未提供文档编码身份的第三方引擎保持保守缓存隔离。显式强制重建旁路所选文档缓存,全局“清除全部索引缓存”仍是独立操作。
|
|
59
87
|
|
|
60
88
|
## 能力探测与提供者
|
|
61
89
|
|
|
@@ -63,21 +91,25 @@ Host 的 `pdfCapabilities` 是进程内能力目录:稳定的操作 ID 描述
|
|
|
63
91
|
|
|
64
92
|
AI 引擎扩展由 `modules/engines` 提供描述、配置校验、类型化目录的共用记账、可取消探测和只读聚合目录。`ocr`、`imageUnderstanding`、`pdfTranslation` 各自拥有唯一实现目录;默认与第三方引擎同样注册并随 Fiber 注销。Reader 配置中的 OCR/图像/翻译选择及各引擎配置通过 Host 绑定到单次请求,人和模型工具复用同一绑定适配器;翻译用请求级实例,不通过修改全局选择路由并发请求。`pdf.engines` 通过认证连接传递有修订的目录快照与长轮询通知,Client 不再只在启动时缓存翻译列表。卸载取消任务,未知选择保留且不自动回退;断线保留上次目录,重新连接后刷新。真实会话模型/凭据可用性仍由 Host 在使用时检查。声明式设置仅接受普通标量及凭据引用,不接收第三方实现函数。详见 [ENGINE_EXTENSIONS.md](./ENGINE_EXTENSIONS.md)。
|
|
65
93
|
|
|
94
|
+
`dsh-vision` 的模型来源由 PDF 设置编辑器拥有:默认跟随会话,指定时写入同一引擎配置的 provider/model/effort,切回跟随清除覆盖。`pdf.model-options` 是无资源会话的只读候选桥接:复用 DSH 的 `sessionController.modelCatalog()`,由 Host 通过 `resolveModelInfo()` 只保留明确支持 image 的模型;Client 按需读取、分组显示和重试,不创建第二套模型注册表,也不调用 `session.selectModel()`。官方会话选择器没有公开独立回调组件,直接使用其会话入口会切换聊天模型,因此这里复用官方目录与现有设置控件样式;目录不是执行授权,真正调用仍由图像引擎重新检查。
|
|
95
|
+
|
|
96
|
+
引擎目录、索引状态、文档修订和导航状态的被动订阅共用 `shared/passive-poll.ts` 的 5 秒等待上限;事件发生时仍立即返回。官方 Desktop 的更新准备阶段会停止接收新请求,并在 10 秒内等待已接收请求结束,所以这些订阅不能等待插件卸载后才取消。此预算仅限制一次状态观察,不限制草稿写入、索引或导航工具的实际工作;更新锁仍须等待已接纳的写入。Client 在正常连接下继续订阅,在断线、取消和卸载时释放订阅。
|
|
97
|
+
|
|
66
98
|
打开 PDF 时先经 `pdf-lib` 检查源文档和只读条件,再由 PDFium 探测页数、表单类型及签名数。`modules/capabilities/pdf-open.ts` 合并两个解析器发现的需求,不能因其中一个漏报而静默忽略表单:普通页面、AcroForm/XFA 外观以及签名读取。当前页面实际由 `pdfium.native` 执行,因此打开检查**只接受这个执行者自身声明的能力**;不能因为另一个插件声称支持 XFA,就让仍由 PDFium 绘制的页面被错误放行。缺失能力时报告 `pdf/native-unsupported` 及所需操作 ID。未来接入另一 PDF 引擎时,须先实现对应的有类型 reader/renderer 端口并让打开、读页和绘制真正路由到该实现,再允许它参与打开规划。
|
|
67
99
|
|
|
68
100
|
当前 PDFium 绘制普通页和 AcroForm 原有外观;Client 按 Host 解析的字段与 widget 坐标覆盖可编辑控件。修改作为 `set-field` 操作进入现有工作副本,支持草稿、撤销/重做,保存时由 `pdf-lib` 更新字段值与外观并走相同的版本检查和原子发布。中文外观使用随包提供的 OFL 字体。阅读器不执行 PDF 脚本、网络动作;带数字签名的 PDF 可只读预览,但不会验证签名真伪,也不能保存;XFA 和加密文档仍不支持。加密文件在探测前需要独立的解锁/授权步骤,不能仅注册一个“支持加密”的布尔能力就绕过密码与源字节转换。能力目录是可扩展的发现与规划层,不代表这些实现已经存在。
|
|
69
101
|
|
|
70
|
-
模型表单入口只有 `
|
|
102
|
+
模型表单入口只有 `pdf_form_inspect` 与 `pdf_form_fill`。检查输入仅 `documentId`、物理 `page` 和可选 `includeValues`;输出分成 JSON 文本块与编号页图。JSON 顶层仅含 `documentId/page/revision/inspectionId/readOnly/readOnlyReason?/fields`;字段仅含 `sharedFieldKey/type/valueType/required`、适用的选项及长度等约束、可选当前值和 `controls: [{label, option?}]`,只有原生单选控件附 `option`。不把原生名称、ID、坐标、widget 清单、示例或固定渲染状态暴露给模型。原生字段关联用同版本内的 `sharedFieldKey` 表达,独立复选框不推断分组。`inspection-references` 保存有界的单页定位映射,绑定会话/文档/原始检查修订/内容版本/源摘要,十分钟过期、最多 128 份,不存图片或填写值,随会话及插件清理。
|
|
71
103
|
|
|
72
|
-
`
|
|
104
|
+
`pdf_form_fill` 输入为 `documentId`、原始检查的 `expectedRevision`、单个 `inspectionId` 和 `fields: [{label, value}]`,只允许写该检查页,页号和真实字段由 Host 恢复;不再接受页号或 ID 数组。可选条件/互斥规则引用 `{label}`(本页)或 `{sharedFieldKey}`(同会话、文档及原始快照中已检查的字段),后者可建立跨页读取依赖,不能扩大写入范围;证据只传 `quote`,页号由 Host 补齐。规则仍由模型提供,Host 不自动推断语义。
|
|
73
105
|
|
|
74
|
-
Host 在文档级队列中串行执行检查记录解析、冲突检查、工作副本 CAS 写入和记录推进,领域操作仍复用 Reader 的类型、选项、长度和身份校验,原子写入同一草稿及撤销历史,不另存模型表单。同版本批量检查后,已知 `
|
|
106
|
+
Host 在文档级队列中串行执行检查记录解析、冲突检查、工作副本 CAS 写入和记录推进,领域操作仍复用 Reader 的类型、选项、长度和身份校验,原子写入同一草稿及撤销历史,不另存模型表单。同版本批量检查后,已知 `pdf_form_fill` 仅修改独立页时,无关页可持原始凭据安全应用到最新草稿;目标页、共享字段或声明的跨页依赖被修改则拒绝。同页修改整页失效;UI 编辑、撤销/重做等未知修订,以及源文件/结构/内容版本变化保守要求重新检查。该有界追踪不是通用并发编辑合并;填写依赖仍须按控制项先行、重新检查后再处理依赖项的顺序执行。`no_changes` 与校验失败不推进修订;草稿填写不要求人工确认,覆盖保存仍需批准。
|
|
75
107
|
|
|
76
|
-
`modules/forms` 与 `forms/inspection-layout` 内部保留控件和编号几何,复用 `NativeViewport` 处理 crop/旋转/UserUnit,将透明底小编号放进真实控件。PDFium 复用 Reader 基线 handle,关闭 widget 和批注外观,以不超过 1024 像素的块组成最长 2048 像素的整页。检查页图的长边由 Host 内部固定,模型工具不暴露尺寸参数。空白外观不表示值为空;`includeValues` 仅通过结构化字段数据返回当前草稿值,关闭时省略 `value`。模型侧读写格式一致:text/radio/dropdown 为字符串(空 `""`),checkbox 为布尔值(未选 `false`),option-list 为字符串数组(空 `[]`);适配器将 dropdown 的内部数组表示规范为单个字符串,不改变 Reader 原有值模型。适配器核对会话、视觉能力、源/修订和附件尺寸。模型根据页图理解印刷标签及条件,使用填写返回状态和显式读回检查结果;普通 `
|
|
108
|
+
`modules/forms` 与 `forms/inspection-layout` 内部保留控件和编号几何,复用 `NativeViewport` 处理 crop/旋转/UserUnit,将透明底小编号放进真实控件。PDFium 复用 Reader 基线 handle,关闭 widget 和批注外观,以不超过 1024 像素的块组成最长 2048 像素的整页。检查页图的长边由 Host 内部固定,模型工具不暴露尺寸参数。空白外观不表示值为空;`includeValues` 仅通过结构化字段数据返回当前草稿值,关闭时省略 `value`。模型侧读写格式一致:text/radio/dropdown 为字符串(空 `""`),checkbox 为布尔值(未选 `false`),option-list 为字符串数组(空 `[]`);适配器将 dropdown 的内部数组表示规范为单个字符串,不改变 Reader 原有值模型。适配器核对会话、视觉能力、源/修订和附件尺寸。模型根据页图理解印刷标签及条件,使用填写返回状态和显式读回检查结果;普通 `pdf_page_render` 仍绘制源基线,不验证未保存草稿外观。Host 不生成语义题目、自动推断规则或自动采集填后图。
|
|
77
109
|
|
|
78
110
|
表单字段以 PDF 对象引用形成 `pdf-field:<object>:<generation>` 身份,`name` 仅用于显示;导入表单可存在同名但独立的字段,不得用名称全局去重或任取第一个字段。`modules/forms/identity` 统一解析 UI、模型、工作副本与 pdf-lib 写入目标。新 `set-field` 写 `fieldId`;旧草稿的 `name` 目标仅在唯一匹配时接受,歧义时明确拒绝,不影响同文档其他目标。多 widget 的同一规范字段只枚举一次,跨页仍共享 ID;对象引用由 pdf-lib 在保存时保留,基线恢复重算同一身份,不重命名用户原字段或重新挂接不明归属的 widget。
|
|
79
111
|
|
|
80
|
-
PDF 内置书签现通过 `outlines` 领域支持新增、重命名、删除子树、修改目标页、排序与父子层级调整。UI 和模型工具 `
|
|
112
|
+
PDF 内置书签现通过 `outlines` 领域支持新增、重命名、删除子树、修改目标页、排序与父子层级调整。UI 和模型工具 `pdf_bookmark_edit` 使用同一套校验与投影,经工作副本的修订检查写入可撤销、可恢复的草稿,保存仍由既有 pdf-lib 事务处理。写入侧使用 PDF 对象身份保留未修改的目的地、命名目标、外链、颜色、字重及其他属性;显式改目标页才替换为页面适合目的地。原生读取结果只用于精确导航,不能作为丢弃未知属性后重建整树的依据。损坏或不支持安全编辑的目录仍可经原生读取展示,但不允许编辑。新增功能的真实 Desktop 交互和外部阅读器互操作仍待验收。
|
|
81
113
|
|
|
82
114
|
导入目录以 `First/Next` 正向链接确定层级;只有正向树有界、无循环、节点唯一且身份稳定时,才允许恢复错误的 `Parent/Prev/Last` 派生链接。反向链接若指向正向树之外的实际书签,必须与对应节点除反向链接外的全部字段完全一致(包括目标、样式、私有数据及正向子节点),否则保持只读,避免把缺失的分支当作冗余数据丢弃。打开与普通批注保存不修改目录;实际目录编辑物化时才重连链接,即使只是改名也要生成有效结构。用户和模型入口共用此规则。
|
|
83
115
|
|
|
@@ -173,7 +205,7 @@ tests/ # 按模块和边界组织测试
|
|
|
173
205
|
| `src/client/Reader.tsx`、`src/client/native/*`、侧栏/工具栏组件 | `viewer` 的 controller、rendering、组件与 feature 样式 |
|
|
174
206
|
| `src/client/native/viewport.ts`、`src/engine/byte-cache.ts` | 跨端纯 `shared/geometry`、`shared/cache` |
|
|
175
207
|
|
|
176
|
-
搜索不能被“统一用例”误合并:Reader 按需遍历全书、忽略大小写、最多展示 500 个结果,包含已缓存 OCR 的页级命中;`
|
|
208
|
+
搜索不能被“统一用例”误合并:Reader 按需遍历全书、忽略大小写、最多展示 500 个结果,包含已缓存 OCR 的页级命中;`pdf_keyword_search` 区分大小写、单次最多十页/20 个命中,扫描当前提取文字(编码修复文字明确标记,不自动 OCR 普通扫描页)。每个工具命中借 `text-evidence` 保存精确匹配偏移和会话限定引用;不把周围 excerpt 作为命中范围。搜索返回分页匹配项及 `searchId/hasMore`,`hasMore: false` 表示该搜索完成,不是页面列表或相关度排名。搜索的内部 `tool-cursor` 由 Host 保留,书签仍公开使用自己的游标;这些版本约束绑定工具、会话、文档、内容版本、修订、源摘要及查询/列表摘要,异步读取后复核,不能把不同版本结果拼起来。两端共享文本/位置内核,保留各自获取、限额、取消与精度契约。
|
|
177
209
|
|
|
178
210
|
## 前后端接口与数据边界
|
|
179
211
|
|
|
@@ -195,7 +227,7 @@ tests/ # 按模块和边界组织测试
|
|
|
195
227
|
| `renderedHash` | 物化后待发布 PDF 的字节摘要,保存前写进草稿;只用于崩溃恢复 | 重启时比对磁盘字节,判定保存是否实际发布;不是通用源版本 |
|
|
196
228
|
| `reader.bytesHash` | 原生阅读器当前基线字节的身份 | 与 `reader.generation`、页/缩放/区域等参数一起校验 PDFium 句柄、位图与 tile;不等于未保存标注投影 |
|
|
197
229
|
|
|
198
|
-
`working-copy` 是唯一写入仲裁者:UI 与模型使用同一个会话副本、每会话串行队列、批注投影和文件适配器。`
|
|
230
|
+
`working-copy` 是唯一写入仲裁者:UI 与模型使用同一个会话副本、每会话串行队列、批注投影和文件适配器。`pdf_annotation_create` 直接以预期修订与源摘要进入一个可撤销操作组;`pdf.watch` 只传轻量修订通知,Reader 随后从已鉴权的工作副本刷新。现有 `pdf.dispatch.v2` 修改/保存请求仍传 `id + revision`,**不在旧 RPC 中强加新必填字段**;未来新写入工具的版本化契约可显式传预期 `contentVersion`。覆盖保存仍须用户批准,再核对沙箱权限、源/目标的已观察字节摘要及覆盖意图,由文件适配器按规范路径串行执行。页文字和不含外观的逻辑锚点缓存可按 `contentVersion` 失效;PDFium bitmap/tile 至少还要按 `reader.bytesHash + reader.generation` 及页/缩放/区域等渲染参数区分,由截图产生的 OCR 也须绑定实际图像输入与引擎配置。带批注的快照、引用和游标还须核对 `revision`;`sourceDigest` 变化后,旧引用即使原文看似相同也必须重新验证,不能按旧坐标盲跳。普通文件系统不能保证与无关外部写入者之间的真正跨进程事务;保留现有最终摘要复核与“不确定”错误语义。
|
|
199
231
|
|
|
200
232
|
打开文档表也由该服务拥有,不另建 UI/模型副本表。具名 UI 引用、模型所有权与操作 pin 独立计数;`background-documents` 只根据登记表的驻留状态执行空闲/容量策略,不再拥有第二份文档生命周期。挂起释放完整快照和 native 资源,保留 ID、路径、修订及恢复记录;恢复用原基线重建草稿,再检查磁盘变化,不用新源文件静默覆盖旧修改。UI 的 `open(referenceId)`、`release` 与不增加引用的 `refresh` 由同一服务处理;引用令牌结束后,迟到的打开请求不得复活它。基线持久缓存仍沿用 V1 存储,未承诺全进程内存受驻留预算约束。
|
|
201
233
|
|
|
@@ -210,7 +242,7 @@ tests/ # 按模块和边界组织测试
|
|
|
210
242
|
| Client 修改/撤销/保存 | 同一 `resourceSessionId` | 上述条件加预期 `revision`;保存/覆盖另需已观察的目标摘要和用户覆盖选择 | 仅凭当前可见标签或路径写文件 |
|
|
211
243
|
| 解释提词、发布当前阅读状态 | UI 的 `containerSessionId` 与资源地址中的 `resourceSessionId` | 两者相等;解释还须使用所属聊天的正式发送接口 | 跨会话把 PDF 内容发入另一个聊天;将带 TTL 的阅读状态当授权 |
|
|
212
244
|
| 模型后台打开/读工具 | `exec.agent.session.id` | 显式地址/文档句柄属于执行会话,并通过文件访问检查 | 根据 UI 当前页猜出可读文件或跨会话复用句柄 |
|
|
213
|
-
| 引用重放、首版 `
|
|
245
|
+
| 引用重放、首版 `pdf_page_jump` | 引用记录的会话 + 当前获授权的会话/附件与可见标签 | 重验来源版本和目标;导航须有匹配的活标签及显式意图,失败要回报 | 用历史 `refId` 或阅读状态直接取得新权限或宣称已经跳转 |
|
|
214
246
|
|
|
215
247
|
这些规则由 Host 授权适配器及相应 Client 会话入口共同执行;失败返回稳定错误/状态,不静默降级到另一会话。`readerContexts` 仅是有过期时间的“当前看到什么”提示。读工具不自动导航,Host 后台 `pdf_open` 不自动创建侧栏标签。
|
|
216
248
|
|
|
@@ -226,9 +258,11 @@ tests/ # 按模块和边界组织测试
|
|
|
226
258
|
|
|
227
259
|
证据的 `refId` 是会话范围内的快照句柄,不等于永久 PDF 元素 ID。已实现的**文字与图像来源**记录存于独立 `pdf_evidence_${suffix}` Storage Domain;每次写入淘汰超过 30 天或总数 256 条以外的记录,查询同时检查所属会话。图像证据只在此域保存受保护的附件句柄、来源/版本、页或 PDF 区域及摘要,或保存有界 OCR/辅助视觉派生文字,**不复制 PNG 字节**。`session/disposed` 只是运行时 detach/HMR/退出信号:它取消并排空该代任务,释放模型引用及不再使用的驻留资源,**不删除逻辑文档身份、草稿、会话日志或持久引用**。Host 重启或同一路径 PDF 重开后,使用文字引用还须先在原会话重新获得文件访问,并核对源路径、整页文字摘要与引文;图像引用另重验源字节、页、附件或派生文字,附件不存在时失败。同字节但临时工作副本/修订变化时可重签发新 `refId`;引用本身不授予文件或附件访问权。解释模式的区域图像经 `pdf.region`、文字选段经 `pdf.selection` 生成会话限定引用;发送区域图像时再经 `pdf.image` `prepare-reference` 核对会话、文档、源文件、附件与摘要,直接图片使用 Host 返回的字节。
|
|
228
260
|
|
|
229
|
-
`tool/result.meta`
|
|
261
|
+
`tool/result.meta` 为基础读取、检索与证据读取保存有界文字快照,Client 的证据卡可重放。`text-evidence` 统一签发读页/搜索/检索的文字证据;`text-target` 是模型标注、精确导航及历史卡片共用的引用解析入口:先授权当前工作副本并重验新鲜源,再核对整页摘要与已存 UTF-16 范围,最后从当前 native 字形重建四边形,不消费缓存的近似 bbox。相同引文按精确偏移区分;OCR 派生文字、缺失/异常几何或并发变化不能降级为猜测坐标。`pdf_annotation_create` 的 reference 目标经该解析器进入同一草稿事务;`pdf_page_jump(documentId, refId, tabId?)` 和历史卡片将验证后的目标交给可见 Reader,Client 再核对版本与 lease,通过既有导航记录同页/跨页多级返回历史。仅页导航仍支持 `page + contentVersion`。读工具不自动跳转,不新增模型 back 工具;真实 Desktop 验收仍待完成。
|
|
262
|
+
|
|
263
|
+
页图、OCR 与 `pdf_evidence_read` 的来源元数据只保存受保护附件句柄或有界派生文字,不复制 PNG。解释模式的 `pdf.region`、`pdf.selection` 及发送前 `prepare-reference` 继续重验源文件、附件和摘要,直送图片仅使用 Host 字节;image-understanding-host 动态取得 reader-host 的证据服务,加载顺序不影响调用。普通模型页图仍不合成未保存草稿覆盖层;图像工具卡与答案正文内联引用未实现。`pdf_status` 的布尔能力代表观察时可用性,详细 `supported/available/reason` 由文档只读/冲突状态、表单/目录信息、可见 Reader 及所选引擎实际探测合成,取消和文档变化不能被吞成成功结果。模型适配器保留 schema、权限、限额及 metadata,领域用例不依赖工具协议。覆盖保存仍由 `pdf_save` 单独请求批准。逐项契约见 [AI_READER_TOOLS.md](./AI_READER_TOOLS.md)。 页图生产与图像处理分离:`pdf_page_render` 只生成图像和句柄,`ocr_recognize`/`image_understand` 消费可信句柄;区域 OCR 只保留 UI 入口,共用服务而不注册区域模型工具。
|
|
230
264
|
|
|
231
|
-
|
|
265
|
+
`pdf.selection` 只用 Host 重提取的原生逐字位置验证选段;单字符与多字符文字项遵循同一坐标契约。缺少逐字数据的文字项只有在原生边界可可靠排除于全部选区之外时才可跳过,选区内坐标不足、引文不匹配、文字不连续及源文件/草稿版本变化仍拒绝。OCR 恢复层不冒充原生逐字证据,解释模式保留已输入问题并提示改用区域框选;Client 按错误码提供本地化提示。
|
|
232
266
|
|
|
233
267
|
## Client 拆分方式
|
|
234
268
|
|
|
@@ -236,7 +270,19 @@ tests/ # 按模块和边界组织测试
|
|
|
236
270
|
|
|
237
271
|
先保持现有窗口化页面、tile 绘制与文字层算法整体可运行,再移动到 `viewer/rendering`;其时序/取消与高亮定位有回归风险,不应与目录迁移同时重写。通用按钮、分段操作和浮层进入 `viewer/components/primitives`(若有其他 Client 消费者再外移);颜色、圆角、透明度、间距先收束为少量主题 token,各 feature CSS 只写自己的布局与状态。继续使用 `dsh-pdf-` CSS 命名空间,不覆盖 DSH 全局样式。
|
|
238
272
|
|
|
239
|
-
Reader
|
|
273
|
+
Reader 左侧工具栏与右侧折叠栏均为 56px 一体细轨,各自只有一层 `.dsh-pdf-rail-shell` 玻璃外壳,导航、交互、文档或栏目分组使用透明容器及细分隔线。左右两栏始终占据实际布局宽度,PDF 的 scrollport 排除这两列;右侧展开默认总宽 296px,保留约 230px 内容卡片。按钮宽度保持 36px,返回与历史组合控件使用 24px 主操作和 16px 箭头。
|
|
274
|
+
|
|
275
|
+
中间的工具/栏目列表独立滚动,外壳不随可用高度压扁。解释模式的区域选择复用同一个 `ExplanationAreaAction`,放入滚动工具列表;计数、错误入口、发送和退出固定在列表下方。阅读器高度达到 640px 时,文档操作直接显示;较短时收进同一组命令生成的菜单,480px 以下撤销/重做也进入菜单。低于 306px 的极短阅读区允许完整外壳滚动,避免挤掉操作。缩放、回退历史、文件操作与任务菜单通过 `ReaderPopover` 挂到当前 Reader 内部,统一右向定位、边界约束及 Escape/焦点恢复;触发器被响应式布局隐藏或滚出细轨时关闭浮层。
|
|
276
|
+
|
|
277
|
+
选区、批注、便签和解释问题框以真正的 PDF scrollport 为定位边界;批注框至少需要 104px 阅读区宽度(80px 卡片和两侧各 12px 留白),不足时隐藏但不清空编辑状态。跳转、页码探测和 fit 缩放共用 16px 顶部阅读留白。
|
|
278
|
+
|
|
279
|
+
页码导航只在右侧控制栏底部挂载一次,展开和折叠时保持可见、可编辑和同一 DOM 身份;内容卡片不再重复挂载分页输入框。开合动画只作用于内容卡片和栏目,不影响页码舱的焦点、输入或位置。
|
|
280
|
+
|
|
281
|
+
Reader 玻璃材质由 `client/glass.css` 统一组合主题底色、透明度、固定像素高度的高光、模糊与内缘,以及两侧共用的外壳圆角、控件圆角和间距。每条细轨/侧栏内容卡片各保留一层材质底板,分组和按钮不叠加模糊。两栏共用悬停、焦点与按下反馈及 `ToolbarTooltip`,避免后置透明背景重置覆盖悬停样式。高对比度、减少透明度及不支持 backdrop-filter 时的实色降级也在这里统一处理。
|
|
282
|
+
|
|
283
|
+
原生表单的未提交输入由每个 Reader 的 `ReaderFormCommits` 协调,以会话、文档、内容版本和权限为生命周期身份,不随普通 revision 重建。控件失焦与保存、撤销、跳转等命令共用提交队列;全局命令等待字段写入成功后读取最新工作副本,失败保留输入并阻止后续命令,资源切换后丢弃迟到操作。文字选区操作仍单独保留浏览器 Range,不对工具栏统一阻止失焦。
|
|
284
|
+
|
|
285
|
+
阅读侧栏的六个页面统一使用 `ScrollablePanel`:组件拥有固定标题、标题操作区、实际滚动视口和滚动条,各功能只提供标题、操作及内容。虚拟列表通过 ref 连接同一个滚动视口,保留各自的窗口计算和导航;不另建外层滚动容器。标题与正文通过正常的 flex 布局分配高度,不使用透明 sticky 标题或按功能写死滚动区的顶部偏移。
|
|
240
286
|
|
|
241
287
|
拆分 controller 前先固定状态寿命与取消规则:
|
|
242
288
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Engine extensions
|
|
2
2
|
|
|
3
|
-
OCR, image understanding
|
|
3
|
+
Image processing has two typed Host services: OCR (image → recognized text, with optional geometry) and image understanding (image + prompt → generated text). Translation and embedding remain separate services. None is a Reader component. Contributors inject the service they extend and own the registration disposer through `ctx.effect()`. They do not need a Client entry or changes to PDF settings. Plugin code is trusted executable code, not a sandbox boundary. Embedding is experimental and disabled by default.
|
|
4
4
|
|
|
5
5
|
## Independent plugin package
|
|
6
6
|
|
|
7
|
+
A buildable example lives in [`examples/http-engines`](../examples/http-engines). Its actual backends are exercised by [`tests/examples/engine-extensions.test.ts`](../tests/examples/engine-extensions.test.ts), including HTTP mapping, cancellation, configuration and live catalog/unload behavior. This checkout changes the image SDK contract; use a package build containing `understand()` rather than an older release.
|
|
8
|
+
|
|
7
9
|
Write an extension as its own package, for example `@example/dsh-pdf-engines`.
|
|
8
10
|
The `@dylanl1/dsh-plugin-pdf/...` imports below refer to the PDF bundle's public
|
|
9
11
|
SDK. These are subpaths of the main npm package, not a separate SDK package or
|
|
@@ -49,11 +51,16 @@ the PDF bundle's plugins. `./backend.js` is your extension's own implementation.
|
|
|
49
51
|
| Service | Import contracts from the PDF SDK | Execution contract |
|
|
50
52
|
| --- | --- | --- |
|
|
51
53
|
| `ocr` | `@dylanl1/dsh-plugin-pdf/ocr-host` | `recognize(request, configuration)` returns validated OCR text and optional image-pixel geometry |
|
|
52
|
-
| `imageUnderstanding` | `@dylanl1/dsh-plugin-pdf/image-host` | `
|
|
53
|
-
| `pdfTranslation` | `@dylanl1/dsh-plugin-pdf/translation` | `translate(request, configuration)` returns a translation
|
|
54
|
+
| `imageUnderstanding` | `@dylanl1/dsh-plugin-pdf/image-host` | `understand(request, configuration)` returns generated text and optional model/limitations |
|
|
55
|
+
| `pdfTranslation` | `@dylanl1/dsh-plugin-pdf/translation` | `translate(request, configuration)` returns a translation, optional lexical details and optional source interpretation |
|
|
54
56
|
| `pdfEmbedding` | `@dylanl1/dsh-plugin-pdf/embedding` | `describe(configuration)` identifies the encoding; `embed(request, configuration)` returns one finite nonzero vector per input |
|
|
55
57
|
|
|
56
|
-
Common descriptors, bindings and configuration types are exported from
|
|
58
|
+
Common descriptors, bindings and configuration types are exported from
|
|
59
|
+
`@dylanl1/dsh-plugin-pdf/engines`. Use stable namespaced IDs such as
|
|
60
|
+
`example.http-ocr`. Each backend has a required `descriptor` with `id`, `name`,
|
|
61
|
+
`version`, `execution` and optional `configurationFields`. The typed service
|
|
62
|
+
normalizes its capability (`ocr-text` or `image-description`); an image backend
|
|
63
|
+
has no additional `id`, `kind`, OCR dependency or interaction/indexing mode.
|
|
57
64
|
|
|
58
65
|
```ts
|
|
59
66
|
import type { Context } from '@deepseek-ai/cordis'
|
|
@@ -69,25 +76,151 @@ export function apply(ctx: Context) {
|
|
|
69
76
|
|
|
70
77
|
## Configuration and selection
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
Settings save automatically. Engine selections and connection settings are
|
|
80
|
+
global. Each PDF stores only its two content switches under
|
|
81
|
+
**Search → Current PDF index**; selecting **Use current defaults** copies their
|
|
82
|
+
new-document defaults. Changes save without starting model work: the user
|
|
83
|
+
starts **Update index** to build a replacement while the current index remains
|
|
84
|
+
usable if its engine is still available. Cancellation or failure retains the
|
|
85
|
+
active plan. Selecting `none` for the global embedding engine stops semantic
|
|
86
|
+
indexing immediately without deleting cached content or document preferences.
|
|
87
|
+
|
|
88
|
+
PDF settings select `ocrEngine`, `imageEngine`, `translationEngine` and
|
|
89
|
+
`embeddingEngine`. `engineConfigurations` retains each engine's configuration
|
|
90
|
+
under its kind and ID (`ocr`, `image`, `translation`, `embedding`). Switching
|
|
91
|
+
engines does not delete other engines' saved configuration. Other Host consumers
|
|
92
|
+
may pass an explicit `binding` without changing the Reader's choice.
|
|
93
|
+
|
|
94
|
+
**Text recognition indexing** (`indexScanPages`, default `true`) uses the selected
|
|
95
|
+
OCR engine for missing or damaged native text. **Visual description indexing**
|
|
96
|
+
(`indexImageDescriptions`, default `false`) uses the selected image-understanding
|
|
97
|
+
engine with the indexing prompt and embeds its generated text, not raw pixels.
|
|
98
|
+
Both switches may be on. Index plans resolve their bindings from the global OCR
|
|
99
|
+
and image-understanding selections; there is no document-specific engine override
|
|
100
|
+
or extra visual-description service
|
|
101
|
+
or registry. Neither switch enables the default-disabled embedding engine.
|
|
102
|
+
Descriptions add model calls, latency and possible fees.
|
|
103
|
+
|
|
104
|
+
The two switches are document preferences. The optional profile patch values
|
|
105
|
+
`indexScanPages` and `indexImageDescriptions` seed new documents; they are not
|
|
106
|
+
duplicate controls in Settings. Changing global engines marks existing index
|
|
107
|
+
plans as pending until an explicit update succeeds, retaining usable old indexes.
|
|
73
108
|
|
|
74
109
|
```yaml
|
|
75
|
-
ocrEngine: example.
|
|
76
|
-
imageEngine:
|
|
77
|
-
|
|
110
|
+
ocrEngine: example.http-ocr
|
|
111
|
+
imageEngine: example.http-vision
|
|
112
|
+
indexScanPages: true
|
|
113
|
+
indexImageDescriptions: true
|
|
78
114
|
engineConfigurations:
|
|
79
115
|
ocr:
|
|
80
|
-
example.
|
|
81
|
-
|
|
116
|
+
example.http-ocr:
|
|
117
|
+
endpoint: http://127.0.0.1:9000/ocr
|
|
118
|
+
image:
|
|
119
|
+
example.http-vision:
|
|
120
|
+
endpoint: http://127.0.0.1:9000/vision
|
|
82
121
|
```
|
|
83
122
|
|
|
84
|
-
|
|
123
|
+
Configuration fields can be `string`, `number`, `boolean` or `secret-reference`,
|
|
124
|
+
with a key, label and optional `required`. Settings render contributor fields; the
|
|
125
|
+
built-in DSH model route uses the selector below. The Host validates declared
|
|
126
|
+
keys/types before execution. A secret reference is an opaque name, never
|
|
127
|
+
an API key. Contributors own Host-side credential creation and resolution.
|
|
128
|
+
|
|
129
|
+
The built-in OCR choices are the current-session model (`session-llm`) and local
|
|
130
|
+
Tesseract (`local-tesseract`). Image understanding has one built-in DSH adapter
|
|
131
|
+
(`dsh-vision`). In PDF settings, **Follow current session** is the default.
|
|
132
|
+
Choose **Specify model** to load DSH's provider-grouped visual-model list; select
|
|
133
|
+
a model and, when advertised, its reasoning effort. Provider/model IDs are not
|
|
134
|
+
manual input fields. This choice configures PDF image processing without changing
|
|
135
|
+
the conversation's model. Returning to Follow clears the model and effort overrides.
|
|
136
|
+
|
|
137
|
+
The saved backend configuration still uses `provider`, `model` and optional
|
|
138
|
+
`reasoningEffort`; an absent route follows the conversation. Existing explicit
|
|
139
|
+
routes remain visible, including an unavailable saved choice after a catalog
|
|
140
|
+
change or load failure. The Host rechecks the exact model's image-input support
|
|
141
|
+
before execution. The adapter always performs inference and returns text; it is
|
|
142
|
+
neither an OCR fallback nor an image-attachment transport.
|
|
143
|
+
|
|
144
|
+
Explanation remains a conversation feature: a visual conversation receives the
|
|
145
|
+
verified image directly, without an extra vision request. A text-only conversation
|
|
146
|
+
can receive text produced by the configured image-understanding engine. An
|
|
147
|
+
unavailable engine reports a reason instead of silently invoking OCR or another
|
|
148
|
+
provider. Explicit OCR calls remain available independently. The final explanation
|
|
149
|
+
always comes from the conversation model.
|
|
150
|
+
|
|
151
|
+
Legacy session-model image selections migrate to `dsh-vision`. Legacy auxiliary
|
|
152
|
+
selections migrate only when an explicit Provider/Model route can be preserved;
|
|
153
|
+
a route formerly held only in an old Host patch remains unavailable until the
|
|
154
|
+
user explicitly selects a new engine. Removed OCR image-bridge selections also
|
|
155
|
+
remain unavailable rather than silently switching to a remote visual model.
|
|
156
|
+
These placeholders do not register or execute the removed backends.
|
|
157
|
+
|
|
158
|
+
## Execution contracts
|
|
159
|
+
|
|
160
|
+
OCR implements `recognize(request, configuration)`. Its input is an encoded image,
|
|
161
|
+
image dimensions, languages, session identity and cancellation signal. Return a
|
|
162
|
+
validated `OcrOutput`: text, optional block/line/word geometry (input-image pixels),
|
|
163
|
+
confidence when available, completion/coverage and warnings. Empty successful
|
|
164
|
+
transcription is valid; do not invent word boxes or confidence scores.
|
|
165
|
+
|
|
166
|
+
Image understanding implements this one operation:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import type { ImageUnderstandingBackend } from '@dylanl1/dsh-plugin-pdf/image-host'
|
|
170
|
+
|
|
171
|
+
const backend: ImageUnderstandingBackend = {
|
|
172
|
+
descriptor: {
|
|
173
|
+
id: 'example.local-vision', name: 'Local vision', version: '1', execution: 'local',
|
|
174
|
+
},
|
|
175
|
+
async availability() { return { available: true } },
|
|
176
|
+
async understand({ image, prompt, signal }) {
|
|
177
|
+
return { text: await describeImage(image, prompt, signal) }
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`understand` receives `{sessionId, image, prompt, signal, binding?}`; `image` contains
|
|
183
|
+
PNG bytes, media type, width and height. It returns `{text, model?, limitations?}`.
|
|
184
|
+
The service returns `{text, source: {backendId, backendVersion, model?}, limitations?}`
|
|
185
|
+
to consumers. Questions, visual index descriptions and other image tasks differ
|
|
186
|
+
in their prompts, not in their backend interfaces. Use the example's actual
|
|
187
|
+
implementation in place of the illustrative `describeImage` function above.
|
|
188
|
+
|
|
189
|
+
## OCR and image content identity
|
|
190
|
+
|
|
191
|
+
Semantic indexing stores a validated `PageContent` snapshot with separate
|
|
192
|
+
native-text, OCR-text and image-description representations. Each retains its
|
|
193
|
+
producer identity, exact output-text digest, optional geometry and clean
|
|
194
|
+
rendered-input digest. OCR positions are converted from input-image pixels to
|
|
195
|
+
PDF coordinates when supplied. Native UTF-16 offsets are never replaced by OCR
|
|
196
|
+
or description offsets. The initial visual block covers the whole page; individual
|
|
197
|
+
figure/table/formula detection is not implemented. Derived hits have approximate
|
|
198
|
+
page/region references and cannot certify native text annotations.
|
|
85
199
|
|
|
86
|
-
|
|
200
|
+
If optional OCR or description fails after usable text is available, that text
|
|
201
|
+
stays indexed but coverage remains `incomplete` and includes `failedPages`. No
|
|
202
|
+
continuation can retry a completed failed enrichment; explicitly rebuild the
|
|
203
|
+
document. Failed content snapshots are not persisted. A successful empty OCR
|
|
204
|
+
result means no text was recognized, not a failed page.
|
|
205
|
+
|
|
206
|
+
Both backend contracts may implement optional asynchronous
|
|
207
|
+
`cacheIdentity(sessionId, signal, configuration)`. Return a stable nonempty string
|
|
208
|
+
only when it identifies the actual model, weights/revision, routing and all other
|
|
209
|
+
output-affecting behavior across restarts. The Host combines it with descriptor
|
|
210
|
+
and canonical configuration. An engine ID or endpoint alone does not attest a
|
|
211
|
+
mutable model. Without an identity (or with `{identity, reusable:false}`), derived
|
|
212
|
+
caches stay isolated by session and registration lifetime. Stable identities
|
|
213
|
+
permit reuse without bypassing source, rendered-input, configuration, availability
|
|
214
|
+
or authorization checks. Registration generation fences late output independently.
|
|
215
|
+
Forward `AbortSignal` and avoid unbounded identity probes.
|
|
87
216
|
|
|
88
217
|
## Embedding provider contract
|
|
89
218
|
|
|
90
219
|
The Reader registers `none` and `local-qwen3-embedding` through `EmbeddingRegistry`.
|
|
220
|
+
The latter displays as **Embedding service**; its stable engine ID remains
|
|
221
|
+
`local-qwen3-embedding` for existing configurations. Its neutral
|
|
222
|
+
`execution: 'service'` descriptor covers both local and remote endpoints; no
|
|
223
|
+
local/remote engine switch is needed. Qwen3 is its default configuration example.
|
|
91
224
|
The default selection is `none`; it starts no model and performs no download.
|
|
92
225
|
A chat/vision model is not automatically an embedding provider. Third-party
|
|
93
226
|
engines use the same registry and settings catalog:
|
|
@@ -106,14 +239,20 @@ export function apply(ctx: Context) {
|
|
|
106
239
|
|
|
107
240
|
An engine supplies `descriptor`, synchronous side-effect-free `describe(config)`,
|
|
108
241
|
optional `availability(config, signal)`, and `embed(request, config)`. The request
|
|
109
|
-
contains `texts`, `purpose: 'query' | 'document'`, `signal` and `timeoutMs`.
|
|
110
|
-
vectors in input order
|
|
111
|
-
|
|
242
|
+
contains `texts`, `purpose: 'query' | 'document'`, `signal` and `timeoutMs`.
|
|
243
|
+
`EmbeddingEngineOutput` accepts either vectors in input order or
|
|
244
|
+
`{ vectors, model: EmbeddingModel }`. The latter lets a provider report the exact
|
|
245
|
+
model identity verified for this operation; the registry requires it to match the
|
|
246
|
+
provider's current description. Traditional vector-array providers remain valid,
|
|
247
|
+
but an identity change during their request is rejected. The registry checks
|
|
248
|
+
count, dimensions, finite numbers and nonzero norm, then L2-normalizes vectors.
|
|
249
|
+
Reject oversized inputs instead of silently
|
|
112
250
|
truncating. Forward cancellation to network/process work and release any owned
|
|
113
251
|
runtime through the contributor's Fiber lifecycle.
|
|
114
252
|
|
|
115
253
|
`EmbeddingModel` reports `engineId/engineVersion`, `model/modelRevision`,
|
|
116
254
|
`dimensions`, `maxBatchSize`, `maxInputCharacters`, `encoding` and `fingerprint`.
|
|
255
|
+
These are provider-owned internal encoding identities, not required user settings.
|
|
117
256
|
The character limit counts UTF-16 units; the provider must additionally enforce
|
|
118
257
|
its real tokenizer limit. The fingerprint must change with weights, tokenizer,
|
|
119
258
|
query/document instructions or other encoding-affecting configuration. It keys
|
|
@@ -121,20 +260,151 @@ rebuildable vectors, not document permissions. Registry `generation(binding)` is
|
|
|
121
260
|
separate: it fences a registration lifetime so unloading/replacing an engine
|
|
122
261
|
invalidates in-flight work even when the encoding fingerprint is unchanged.
|
|
123
262
|
|
|
263
|
+
Providers may also return `documentFingerprint`, an optional nonempty string of
|
|
264
|
+
at most 512 characters identifying only document-vector encoding. It must change
|
|
265
|
+
with document weights, tokenizer, dimensions or document instructions; query-only
|
|
266
|
+
instructions and transport batch sizes can leave it unchanged. Consumers namespace
|
|
267
|
+
it by engine ID. Without it, retrieval conservatively keeps the full binding in
|
|
268
|
+
the vector-cache key. The complete fingerprint, configuration and registration
|
|
269
|
+
generation still fence requests and queries, even when document vectors are reused.
|
|
270
|
+
|
|
124
271
|
Consumers call `registry.embed(request, binding)` and receive `{ vectors, model }`;
|
|
125
272
|
`list/subscribe` drive the dynamic settings catalog. Selection is captured per
|
|
126
273
|
request. Removing a provider aborts its active requests; retrieval also rejects
|
|
127
274
|
late results from the old generation. Settings retain a missing selection while the catalog removes the unloaded provider.
|
|
128
|
-
|
|
129
|
-
|
|
275
|
+
The `pdf_semantic_search` tool requires a configured, available embedding engine
|
|
276
|
+
and reports unavailable engines explicitly. It does not expose the internal
|
|
277
|
+
retriever's automatic lexical fallback; literal matching uses `pdf_keyword_search`.
|
|
278
|
+
|
|
279
|
+
When a document's active policy enables an available embedding engine, opening
|
|
280
|
+
its shared working copy from either the Reader or `pdf_open` schedules background
|
|
281
|
+
preparation. Providers
|
|
282
|
+
receive the same `purpose: 'document'` requests used by search; no new provider
|
|
283
|
+
method, synthetic query or query vector is involved. Before embedding, the shared
|
|
284
|
+
page-content pipeline can invoke selected-engine OCR for scanned/damaged pages
|
|
285
|
+
and optional selected-engine whole-page descriptions using the settings above. Preparation
|
|
286
|
+
shares indexes and in-flight build steps with foreground searches, including
|
|
287
|
+
other authorized Sessions in the same Host instance with matching source bytes,
|
|
288
|
+
page range and encoding configuration. Shared identity includes the source
|
|
289
|
+
digest, page count, extraction/preprocessing identities, retrieval
|
|
290
|
+
versions, scope, embedding fingerprint and registration generation; it excludes
|
|
291
|
+
Session-local document IDs, paths and `contentVersion`. Permissions, drafts,
|
|
292
|
+
queries, continuation handles, results and evidence references remain scoped to
|
|
293
|
+
each Session. Every access is authorized independently: a shared index is not an
|
|
294
|
+
access grant. Preparation does not delay opening, but an early search can still
|
|
295
|
+
need the remaining indexing work.
|
|
296
|
+
Remote services can receive page images and extracted/derived document text when
|
|
297
|
+
preparation starts, before any search query. Missing stable OCR/image identities
|
|
298
|
+
restrict sharing to the same Session and registration lifetime.
|
|
299
|
+
|
|
300
|
+
The Host runs at most one background document task and tracks up to 64 open
|
|
301
|
+
documents; excess documents retain on-demand search. Only actual UI/model
|
|
302
|
+
ownership counts as open, not an indexing lease. Closing the last owner in one
|
|
303
|
+
Session cancels that Session's preparation; it does not stop a build still used
|
|
304
|
+
by another authorized subscriber. Shared build work aborts when no subscribers
|
|
305
|
+
remain. Disabling indexing or applying an updated plan invalidates obsolete work;
|
|
306
|
+
editing defaults or pending choices does not switch the active plan. Provider
|
|
307
|
+
unload aborts affected requests without choosing a fallback engine.
|
|
308
|
+
Source changes can queue a fresh preparation; unchanged annotation edits do not.
|
|
309
|
+
Ready indexes remain subject to the shared LRU/memory budget, not permanent disk
|
|
310
|
+
storage. Failures do not retry indefinitely in the background.
|
|
311
|
+
|
|
312
|
+
The Reader's **Update index** starts, resumes or retries the current open PDF and
|
|
313
|
+
applies pending choices when embedding is enabled. It reuses caches; only an
|
|
314
|
+
unchanged, ready plan is a no-op. **Recompute entire index** forces fresh page
|
|
315
|
+
processing and document embeddings for the selected document without clearing
|
|
316
|
+
other documents' caches. **Clear all index caches** in PDF settings is the separate
|
|
317
|
+
profile-wide reset. Manual cancellation stays stopped for the same active
|
|
318
|
+
source/plan during that open lifecycle. A manual start, source or applied-plan
|
|
319
|
+
change, or close/reopen can schedule preparation again.
|
|
320
|
+
|
|
321
|
+
The existing task entry reports filenames, extracted/total pages and
|
|
322
|
+
embedded/total chunks for all open PDFs in the current resource Session, with
|
|
323
|
+
cancel/retry and persistent green **Index ready** records until Host removes the
|
|
324
|
+
closed PDF's task. Other short-lived save notices keep their existing timers. The
|
|
325
|
+
authenticated `pdf.indexing` RPC provides `status`, `start` and `cancel`, filters ownership by
|
|
326
|
+
Session and uses revision-based long polling for updates. It is a UI control
|
|
327
|
+
surface, not a model tool or an additional embedding-provider interface.
|
|
328
|
+
|
|
329
|
+
The included **Embedding service** adapter accepts a loopback HTTP endpoint or
|
|
330
|
+
an HTTPS endpoint ending in `/embeddings`. It sends OpenAI-compatible embedding
|
|
331
|
+
requests and checks availability through the sibling `/models` endpoint. Standard
|
|
332
|
+
model lists and indexed float-vector responses are sufficient; custom embedding
|
|
333
|
+
metadata and `model_revision` are optional. When supplied, metadata is checked for
|
|
334
|
+
the selected model, dimensions, limits and encoding, and must not report
|
|
335
|
+
truncation. The adapter learns `/models`' `embedding.revision` internally and
|
|
336
|
+
includes it in the fingerprint, so a reported version change separates the old
|
|
337
|
+
index automatically. The `/models` and embedding-response revisions must also
|
|
338
|
+
agree within an operation. Users do not configure a revision. This protocol
|
|
339
|
+
adapter does not cover every provider API.
|
|
340
|
+
|
|
341
|
+
Configure `model`, `dimensions` and input/batch limits for the selected service.
|
|
342
|
+
The adapter derives its cache identity internally from the endpoint, model,
|
|
343
|
+
dimensions, encoding configuration and any learned service revision. The removed `modelRevision` setting is
|
|
344
|
+
ignored when reading old configurations. The Qwen3 launcher in
|
|
345
|
+
[the experiment](RETRIEVAL_EXPERIMENT.md) remains the default
|
|
346
|
+
configuration example. `queryInstruction` defaults to an empty string for every
|
|
347
|
+
model. Configure it explicitly when the model requires a retrieval instruction;
|
|
348
|
+
clearing the field disables the instruction.
|
|
349
|
+
|
|
350
|
+
**Clear all index caches** in PDF settings invalidates this profile's in-memory
|
|
351
|
+
indexes, active searches and continuation state, and clears its persisted retrieval cache.
|
|
352
|
+
It first suspends and drains background preparation, then resumes it for still-open
|
|
353
|
+
documents after a successful reset when their active policy enables embedding.
|
|
354
|
+
Other documents rebuild on their next open or search. Pending choices still
|
|
355
|
+
require **Update index**; clearing caches does not apply them. It does not modify
|
|
356
|
+
PDF files, drafts, annotations or saved document policies. Use it when a service
|
|
357
|
+
replaces the weights or tokenizer behind an unchanged model name without reporting a new revision;
|
|
358
|
+
the standard protocol cannot detect such a replacement reliably. Rebuilding also prevents cancelled, older work from
|
|
359
|
+
repopulating the cleared cache.
|
|
360
|
+
|
|
361
|
+
Optional `apiKeyRef` is a DSH credential reference, resolved in Host through
|
|
362
|
+
`credentials.resolve()` and sent as Bearer authentication. Store the API key in
|
|
363
|
+
DSH credentials, never in `engineConfigurations` or the Client. Services without
|
|
364
|
+
authentication omit the reference. Extensions requiring other protocols or
|
|
365
|
+
authentication still register their own engine and own their credential
|
|
366
|
+
integration.
|
|
367
|
+
|
|
368
|
+
## Translation source recovery
|
|
369
|
+
|
|
370
|
+
The built-in model chooses whether a readable selection is a word, phrase or
|
|
371
|
+
sentence; word count no longer forces dictionary mode. Ordinary sentence
|
|
372
|
+
fragments may be translated directly. The model may correct extraction errors
|
|
373
|
+
or complete clipped text using the bounded adjacent context. Inferred completions
|
|
374
|
+
must remain distinguishable from the original selection; an engine must not
|
|
375
|
+
silently invent missing names, quantities or facts.
|
|
376
|
+
|
|
377
|
+
Translation extensions can optionally return this metadata alongside the existing
|
|
378
|
+
`text`, `status`, `warnings` and lexical `detail`:
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
interpretation: {
|
|
382
|
+
text: 'calculate',
|
|
383
|
+
kind: 'correction', // 'correction' | 'completion'
|
|
384
|
+
certainty: 'confident', // 'confident' | 'tentative'
|
|
385
|
+
explanation: 'Corrected a character recognized incorrectly.',
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`interpretation.text` is the source used for the returned translation or lexical
|
|
390
|
+
entry; `request.text` remains the exact selection. Omit `interpretation` when no
|
|
391
|
+
source change was needed. The plugin does not compare this text with the selection
|
|
392
|
+
or make a second judgment about its meaning. The text may contain at most 8192
|
|
393
|
+
UTF-16 units; the explanation is limited to 500. Both count
|
|
394
|
+
toward the configured output budget, and interpreted text also respects the input
|
|
395
|
+
budget. Only complete outputs may carry recovery metadata. Unknown fields and
|
|
396
|
+
invalid enum values are rejected at registry and authenticated wire boundaries.
|
|
397
|
+
Engines that omit the metadata retain their existing output contract.
|
|
130
398
|
|
|
131
|
-
The
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
399
|
+
The Reader shows the original selection separately from the recovered source,
|
|
400
|
+
labels tentative results as a model inference, and uses the recovered text for
|
|
401
|
+
the lexical heading. `confident` is the engine's assessment, not a verified
|
|
402
|
+
probability. Recovery never edits the PDF, changes a selection or appends to
|
|
403
|
+
conversation history. There is no `recognized` gate: the built-in model returns
|
|
404
|
+
`kind` and its translation or lexical explanation directly. If meaning remains
|
|
405
|
+
uncertain, it explains that uncertainty in its displayed response. The plugin
|
|
406
|
+
validates response structure, limits and request lifetime, not translatability.
|
|
407
|
+
Single words and phrases may both carry model-provided pronunciation.
|
|
138
408
|
|
|
139
409
|
## Directory and lifecycle
|
|
140
410
|
|
|
@@ -145,4 +415,4 @@ registered by default; contributors implementing one must declare
|
|
|
145
415
|
- A missing selection stays selected and displays as unavailable. No silent local/remote fallback occurs. Disconnects retain the last directory until reconnection; they are not interpreted as provider removal.
|
|
146
416
|
- Per-request configuration is captured before asynchronous work. OCR/image consumers can use independent bindings; Host translations use request-scoped instances rather than mutating one global selection.
|
|
147
417
|
|
|
148
|
-
Automatic
|
|
418
|
+
Automatic repair of the Reader's selectable character layer deliberately remains a separate, fixed local path requiring positioned words. Semantic indexing's OCR option independently uses the selected OCR engine for missing/damaged native text; it does not replace that Reader layer or rewrite the PDF. Engine extensibility does not change source authorization, evidence validation, draft ownership or save approval.
|