Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions docs/monorepo-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
# light-ocr Monorepo 设计

状态:草案(2026-07-21)。
受众:维护者、贡献者。本文定义 monorepo 的目录结构、包依赖关系、迁移路径和约束,不替代各包自身的实现设计。

## 1. 动机

Roadmap §3.1 规划了 7+ 个 npm 包(runtime、light-ocr、tiny、medium、document、layout、model-*),加上 server-side OCR 服务后达到 8+ 个。当前 `bindings/node/` 单包结构无法支撑跨包改动(如 runtime 提取后 tiny/medium/document 都需要同步更新)。Monorepo 用 npm workspaces 统一管理这些包,保持一次 install、一次 lint、一次 test 的开发体验。

## 2. 目录结构

```
light-ocr/
├── package.json # root workspace config(private, workspaces)
├── packages/
│ ├── runtime/ # @arcships/light-ocr-runtime
│ │ ├── package.json
│ │ ├── src/
│ │ └── test/
│ ├── light-ocr/ # @arcships/light-ocr(small 默认,从 bindings/node 迁移)
│ │ ├── package.json # 含 "bin": { "light-ocr": "./src/cli.cjs" }
│ │ ├── src/
│ │ └── test/
│ ├── light-ocr-server/ # @arcships/light-ocr-server(REST API + Docker)
│ │ ├── package.json
│ │ ├── Dockerfile
│ │ ├── .dockerignore
│ │ ├── src/
│ │ └── test/
│ ├── light-ocr-tiny/ # @arcships/light-ocr-tiny(N2)
│ ├── light-ocr-medium/ # @arcships/light-ocr-medium(N2)
│ ├── light-ocr-document/ # @arcships/light-ocr-document(N3)
│ ├── light-ocr-layout/ # @arcships/light-ocr-layout(N4)
│ └── model-*/ # @arcships/light-ocr-model-*(纯数据包)
├── native/ # native addon 源码与 CMake(从 bindings/node 拆出 JS 后的残留)
│ ├── CMakeLists.txt
│ └── src/
│ └── addon.cpp
├── src/ # C++ Core(不变)
├── docs/
├── tests/ # 跨包集成测试与 corpus
├── tools/
└── contracts/ # 公共契约(cli-design.md §2.5 的 contracts/)
├── cli/
│ ├── flags.json5
│ ├── envelope.schema.json
│ ├── exit-codes.json
│ └── fixtures/
└── errors/
└── ocr-error-codes.json
```

### 2.1 关键设计决策

- **`native/` vs `packages/native/`:** native addon 不是独立的 npm 用户入口包,其预编译产物通过 platform-specific optional dependency 被 `runtime` 引用。`native/` 放在根目录,与 `src/`(C++ Core)平级,保持 CMake 构建路径简洁。
- **`contracts/`:** 多个包共享的 flag 定义、schema、exit code 表、golden fixtures。单一来源,由各包的构建/测试脚本读取。
- **`packages/light-ocr/` 的 bin:** roadmap §3.1 规定 `light-ocr` bin 只属于默认 small 包。tiny/medium 若有 CLI,bin 命名为 `light-ocr-tiny` / `light-ocr-medium`。
- **`packages/light-ocr-server/`:** 唯一携带 Dockerfile 的包。它是一个 npm 包(可发布到 registry),也是一个可独立构建的 Docker 镜像。

## 3. 包依赖关系

```text
@arcships/light-ocr-runtime
├── native addon(platform-specific optionalDependencies)
├── express? 否 — runtime 不含 HTTP 层
└── 不默认携带模型

@arcships/light-ocr
├── exact: @arcships/light-ocr-runtime
├── exact: @arcships/light-ocr-model-ppocrv6-small
├── bin: light-ocr
└── 唯一代表"开箱即用"的默认入口

@arcships/light-ocr-server
├── exact: @arcships/light-ocr(默认 small 模型)
├── express, multer
├── Dockerfile(基于 node:22-trixie-slim)
└── 部署制品:Docker 镜像

@arcships/light-ocr-tiny / -medium
├── exact: @arcships/light-ocr-runtime
├── exact: 对应模型包
└── 与 light-ocr 共享相同 JS API + 类型

@arcships/light-ocr-document(N3)
├── exact: @arcships/light-ocr-runtime(或接受注入的 engine factory)
├── PDF renderer(S3 接受分支)
└── 不强制依赖特定杯型

@arcships/light-ocr-layout(N4)
├── exact: @arcships/light-ocr-runtime
├── exact: Layout 模型包
└── 不拥有 CLI bin
```

### 3.1 Server 的依赖方向

Server 依赖 `@arcships/light-ocr`(默认 small),而不是 `runtime`。这确保 server 开箱即用:一个 `docker run` 就能跑 OCR,不需要用户另外装模型。

未来可选支持通过环境变量切换模型杯型(例如 `MODEL_PACKAGE=@arcships/light-ocr-medium`),但默认保持 small。

## 4. 工具链

### 4.1 选择:npm workspaces

当前项目使用 npm,零额外工具迁移成本。

```jsonc
// 根 package.json
{
"private": true,
"workspaces": [
"packages/*",
"native"
]
}
```

- `npm install` 在根目录执行,自动为所有 workspace 安装依赖并创建 symlink。
- `npm test --workspaces` 运行所有包的测试。
- `npm publish --workspace packages/light-ocr` 发布单个包。

不需要 rush、lerna、turbo 等额外编排工具,当前 8 个包的规模 npm workspaces 完全够用。如果将来需要统一版本发布、changelog 生成,再评估 changesets。

### 4.2 版本策略

采用**独立版本**(independent versioning),每个包有自己的 `version` 字段:

| 包 | 版本锚点 | 说明 |
| --- | --- | --- |
| `runtime` | 与 Core 版本解耦 | 适配层,变化频率低 |
| `light-ocr` | 跟随项目 semver | 默认入口,用户感知的主版本号 |
| `server` | 独立 semver | 部署制品,有自己的 breaking change 周期 |
| `model-*` | 与模型 bundle ID 对齐 | 数据包,模型变更时发新版 |
| `tiny/medium/document/layout` | 各自独立 | 按各自成熟度独立发版 |

约束:
- `light-ocr` 精确锁定 `runtime` 和 `model-*` 的兼容版本(`"@arcships/light-ocr-runtime": "1.2.3"` 精确 pin,不用 `^`)。
- `server` 精确锁定 `light-ocr` 版本。
- 每次 `light-ocr` 发版时,同步检查 `server` 是否需要更新依赖版本。

## 5. 迁移路径

### 5.1 阶段 0:当前状态(不破坏现有结构)

```
bindings/node/ # @arcships/light-ocr,JS + native addon 混合
├── js/ # JS facade(未来 → packages/runtime/)
├── src/ # native addon(未来 → native/)
├── CMakeLists.txt
└── package.json
```

### 5.2 阶段 1:建立 monorepo 骨架(N2 启动时)

- 创建 `packages/runtime/`,从 `bindings/node/js/` 迁移 JS facade 代码。
- 创建 `packages/light-ocr/`,依赖 `runtime` + 模型包,包含 CLI bin。
- `native/` 保持 addon 源码 + CMake,平台预编译包独立发布。
- `bindings/node/` 标记为 deprecated,保留到确认迁移稳定后删除。
- 此阶段 CI 同时运行旧路径和新 workspace。

### 5.3 阶段 2:加入 server(N2 完成或 N3 前后)

- `packages/light-ocr-server/` 加入 workspace。
- Dockerfile 使用两阶段构建或从 npm registry 安装依赖。
- 本地开发时 `npm install` 自动 symlink workspace 内的 `light-ocr`,无需先发布。

### 5.4 阶段 3:加入 tiny、medium(N2 GA)

- 两个新包加入 workspace。
- 验证三杯型共享同一 API、类型、测试套件。

### 5.5 阶段 4:document、layout(N3、N4)

- 按各自节点加入。

## 6. Server Docker 构建

Server 是特殊的包:它既作为 npm 包发布,也作为 Docker 镜像分发。

### 6.1 Dockerfile 策略

```dockerfile
# packages/light-ocr-server/Dockerfile
FROM node:22-trixie-slim

WORKDIR /app

# 从 npm registry 安装(生产模式不需要 workspace)
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

COPY src/ ./src/

RUN groupadd -r ocr && useradd -r -g ocr ocr
USER ocr

EXPOSE 3000
ENV EXECUTION_MODE=cpu
ENV QUEUE_CAPACITY=4

CMD ["node", "src/server.js"]
```

### 6.2 本地开发 vs 生产构建

- **本地开发**:`npm install`(workspace 解析为 `packages/light-ocr/` 的 symlink),直接 `node src/server.js`,改动即时生效。
- **生产 Docker 构建**:`npm install` 走 npm registry,拉取已发布的 `@arcships/light-ocr`。镜像构建独立于 monorepo。
- **CI 集成测试**:先 `npm install`(workspace),再 `npm test --workspace packages/light-ocr-server`,验证 server + engine 的端到端行为。

### 6.3 与 docker-compose 的关系

根目录 `docker-compose.yml` 引用 `packages/light-ocr-server/Dockerfile`:

```yaml
services:
light-ocr-api:
build:
context: .
dockerfile: packages/light-ocr-server/Dockerfile
ports:
- "3000:3000"
environment:
- EXECUTION_MODE=cpu
```

## 7. 约束

- **根 `package.json` 不包含业务依赖。** 只声明 `workspaces` 和顶层 scripts(lint、test、build 编排)。
- **每个包独立可发布。** `npm publish --workspace <name>` 不依赖 workspace symlink。
- **跨包依赖使用精确版本。** `"@arcships/light-ocr": "0.4.0"`,不用 `^` 或 `~`。
- **`contracts/` 是跨包共享的单一来源。** 各包 CI 从 `contracts/` 生成 flag parser 或验证 golden fixtures,不自行复制 schema。
- **`native/` 不在 npm workspace 中发布为用户包。** 它只作为 `runtime` 的 platform-specific optionalDependency 的构建源。
- **Server 的 Docker 镜像版本与 npm 包版本保持一致。** 每次 `packages/light-ocr-server/package.json` 的 version bump 对应一个 Docker image tag。

## 8. 不做

- 不引入 rush/lerna/turbo/nx 等额外编排工具,除非 npm workspaces 被证明不够用。
- 不把所有包强制统一版本号(lockstep versioning)。server 和 light-ocr 的 breaking change 周期不同。
- 不修改 C++ Core 的源码目录结构。
- 不在 monorepo 迁移完成前向用户承诺 server package 的 API 稳定性。
21 changes: 19 additions & 2 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
![light-ocr Roadmap 像素风宣传图](assets/light-ocr-roadmap.png)

- 状态:Draft
- 更新时间:2026-07-16
- 更新时间:2026-07-21
- 适用范围:`0.2.x` 之后至 `1.0` 的产品与工程演进

## 1. 目标与定位
Expand Down Expand Up @@ -159,6 +159,7 @@ Roadmap 结束时应形成以下入口,而不是一个不断膨胀的单包 AP
| `@arcships/light-ocr-layout`(逻辑角色) | 显式选择 Layout 的用户 | Layout analyzer、公共 label 映射和 capability resolver;依赖 runtime 与一个精确 Layout model,不拥有 CLI bin |
| `@arcships/light-ocr-model-<layout-id>`(逻辑角色) | 由 Layout capability 间接安装 | 纯数据 Layout bundle、manifest、license 和模型 identity;不包含编排代码 |
| Agent Skill / Plugin | Codex 与其他可调用本地命令的 Agent | 选择正确命令、约束输出、处理错误,不实现 OCR |
| `@arcships/light-ocr-server` | 后端服务、CI/CD pipeline、非 JS 调用方 | HTTP REST API 包装 OCR engine;Docker 镜像分发;不替代 CLI 作为本地入口 |

具体包名可在 N2/N3 的设计决策中调整,但以下约束必须保持:

Expand All @@ -176,6 +177,8 @@ Roadmap 结束时应形成以下入口,而不是一个不断膨胀的单包 AP
- Document Node API 接受调用方注入兼容的 engine factory,因此不强制 small;`light-ocr-document` CLI Preview 可以精确依赖 small 作为开箱即用默认值;
- Layout model 不成为 Document 包的默认依赖。`@arcships/light-ocr-layout` 精确锁定兼容 runtime 和 Layout model;Document 只接受注入的 versioned Layout analyzer interface,避免反向依赖和循环依赖;
- 上述 Layout 包名是待 D109 接受的逻辑角色,不在 Roadmap 中提前冻结最终 registry 名称。
- `@arcships/light-ocr-server` 依赖 `@arcships/light-ocr`(默认 small),精确锁定兼容版本;server 的 HTTP API 与 CLI 共享相同的 OcrError 语义和 exit code 映射,但不要求 CLI 先达到 stable——server 可以独立 preview 发布。
- server 的开发和分发依赖 monorepo 结构(见 [monorepo 设计](monorepo-design.md));在 monorepo 迁移完成前,server 作为独立仓库存在,不在主仓库 `server/` 目录下开发。

### 3.2 结果模型演进

Expand Down Expand Up @@ -225,6 +228,17 @@ N1 冻结以下术语,后续 PDF 和 Layout 只能扩展,不能重新解释
- JSONL page record 带 document identity、page index 和 `status`。中途取消或失败时,已完成记录保持有效,stderr 给出终态,进程返回非零 exit code;
- `structure: "ocr-order"` 表示只有 OCR 几何顺序;`structure: "layout"` 仅在 Layout compose 成功时出现,不表示结果达到人工真值。

### 3.4 Monorepo 与工程结构

Roadmap §3.1 规划的产品入口对应 8+ 个 npm 包。跨包改动(runtime 提取、模型切换、共享契约更新)在单包结构下不可维护。monorepo 是这些包的工程基础,不是产品功能。

设计决策:

- 工具链选择 npm workspaces,零额外迁移成本。
- 目录结构、包依赖关系、版本策略和迁移路径见 [monorepo 设计文档](monorepo-design.md)。
- monorepo 迁移在 N2 启动时执行(runtime 提取是迁移的第一步),不与 N1 CLI 竞争资源。
- `packages/light-ocr-server/` 在 monorepo 就绪后加入;在此之前,server 作为独立仓库(`light-ocr-server`)存在,引用已发布的 `@arcships/light-ocr`。

## 4. N0 — 发布与需求基础

### 4.1 目标
Expand Down Expand Up @@ -272,6 +286,8 @@ N1 冻结以下术语,后续 PDF 和 Layout 只能扩展,不能重新解释

## 5. N1 — CLI、结果契约与 ROI

> 落地前细化设计见 [cli-design.md](cli-design.md)(2026-07-20 草案,含分发形态对比、分层 help、stdout/stderr 严格分离、退出码表、批量 JSONL 分页语义、agent 友好性 checklist 与 8 项 D-N1 待决策)。SKILL.md 草稿在 [`.agents/skills/local-ocr/SKILL.md`](../.agents/skills/local-ocr/SKILL.md)。

### 5.1 目标

让普通用户和 Agent 无需编写 Node.js 集成代码,即可从本地图片获得稳定文本、置信度和坐标;同时建立 PDF、Layout 和多模型都能复用的版本化结果契约。
Expand Down Expand Up @@ -989,7 +1005,7 @@ Roadmap 允许调整,但调整必须说明证据和受影响节点。
- 默认安装全部模型或运行时自动下载模型;
- 让 tiny/small/medium 形成不同 API;
- 将 PDF renderer、Layout、表格和公式全部并入 C++ OCR Core;
- 在 CLI 稳定前优先建设 MCP server;
- 在 CLI 稳定前优先建设 MCP server;REST API server 作为可选部署制品在 monorepo 就绪后提供,不阻塞 CLI 的主路径交付。
- 没有独立验证就承诺字符级坐标、任意模型兼容或 GPU 全平台;
- 将 GPU 作为默认必需依赖,或把 provider 不可用静默伪装成 GPU 成功;
- 为内部实现名称保留长期兼容 shim;
Expand All @@ -1015,6 +1031,7 @@ Roadmap 允许调整,但调整必须说明证据和受影响节点。
- [Apple Device 加速技术方案](apple-device-acceleration.md)
- [Windows Device 加速技术方案](windows-device-acceleration.md)
- [Linux Device 加速技术方案](linux-device-acceleration.md)
- [Monorepo 设计](monorepo-design.md)
- [Core requirements](requirements.md)
- [Architecture](architecture.md)
- [Accepted and deferred decisions](decisions.md)
Expand Down
Loading