--- title: HTTP API 参考 --- # HTTP API 参考 Koharu 在本地暴露 HTTP API: ```text http://127.0.0.1:/api/v1 ``` 桌面 UI 与 headless Web UI 使用的就是这套 API。 ## 运行时模型 当前实现中的重要行为: - API 与 GUI 或 headless 运行时由同一个进程提供 - 服务器默认绑定到 `127.0.0.1`;使用 `--host` 可绑定到其他地址 - API 与 MCP 服务器共享同一个已加载的项目、模型和管线状态 - 没有提供 `--port` 时,Koharu 会选择一个随机本地端口 - 在应用完成启动之前,除 `/api/v1/downloads`、`/api/v1/operations` 与 `/api/v1/events` 之外的所有路由都会返回 `503 Service Unavailable` ## 资源模型 API 是以项目为中心的。同一时间只会打开一个项目,它包含: - 一组 `Pages`,按 `PageId` 索引 - 每个页面下的 `Nodes`(图像图层、掩码、文本块),通过 `NodeId` 引用 - 一个内容寻址的 `Blob` 存储,按 Blake3 哈希保存原始图像字节 - 由这些组件构建出的 `Scene` 快照,并通过 `epoch` 计数器递进 - 一段 `Op` 修改历史,可以撤销与重做 所有修改都必须经过历史层(`POST /history/apply`),以保证场景、自动保存和事件订阅者都保持同步。 ## 常见响应类型 高频返回结构包括: - `MetaInfo`:应用版本与 ML 设备 - `EngineCatalog`:每个管线阶段可安装的引擎 id - `ProjectSummary`:id、名称、路径、页数、最近一次打开时间 - `SceneSnapshot`:`{ epoch, scene }` - `LlmState`:当前 LLM 加载状态(status、target、error) - `LlmCatalog`:按系列分组的本地 + 提供商模型 - `JobSummary`:`{ id, kind, status, error }` - `DownloadProgress`:包 id、字节数、状态 ## 端点 ### 元信息 | 方法 | 路径 | 用途 | | ----- | ---------- | ------------------------------- | | `GET` | `/meta` | 获取应用版本与当前 ML 后端 | | `GET` | `/engines` | 列出每个管线阶段已注册的引擎 | ### 字体 | 方法 | 路径 | 用途 | | ------ | --------------------------------- | --------------------------------------------- | | `GET` | `/fonts` | 系统字体 + Google Fonts 的合并目录,用于渲染 | | `GET` | `/google-fonts` | 单独列出 Google Fonts 目录 | | `POST` | `/google-fonts/{family}/fetch` | 下载并缓存某个 Google Fonts 字体族 | | `GET` | `/google-fonts/{family}/{file}` | 提供已缓存的 TTF/WOFF 文件 | ### 项目 每个项目都位于受管理的 `{data.path}/projects/` 目录下;客户端永远不需要传入文件系统路径。 | 方法 | 路径 | 用途 | | -------- | ----------------------------- | ------------------------------------------------- | | `GET` | `/projects` | 列出受管理的项目 | | `POST` | `/projects` | 创建新项目(请求体 `{ name }`) | | `POST` | `/projects/import` | 把一个 `.khr` 归档解压到新目录并打开 | | `PUT` | `/projects/current` | 通过 `id` 打开一个受管理项目 | | `DELETE` | `/projects/current` | 关闭当前会话 | | `POST` | `/projects/current/export` | 导出当前项目;返回二进制字节 | `POST /projects/current/export` 接受 `{ format, pages? }`,其中 `format` 是 `khr`、`psd`、`rendered`、`inpainted` 之一。当某种格式会产生多个文件时,响应类型是 `application/zip`。 ### 页面 | 方法 | 路径 | 用途 | | ------ | --------------------------------------- | --------------------------------------------------- | | `POST` | `/pages` | 通过 multipart 上传 N 张图片创建页面 | | `POST` | `/pages/from-paths` | Tauri 专用的快速通道,按绝对路径导入 | | `POST` | `/pages/{id}/image-layers` | 从上传文件添加一个 Custom 图像节点 | | `PUT` | `/pages/{id}/masks/{role}` | 用原始 PNG 字节 upsert 一个掩码节点 | | `GET` | `/pages/{id}/thumbnail` | 获取页面缩略图(以 WebP 缓存) | `role` 是 `segment` 或 `brushInpaint`。`POST /pages` 接受可选字段 `replace=true`;导入时会按文件名自然顺序排序。 ### Scene 与 blobs | 方法 | 路径 | 用途 | | ----- | ------------------ | ----------------------------------------------------------------- | | `GET` | `/scene.json` | 完整的场景快照,供 web/UI 客户端使用 | | `GET` | `/scene.bin` | postcard 编码的 `Snapshot { epoch, scene }`,供 Tauri 客户端使用 | | `GET` | `/blobs/{hash}` | 按 Blake3 哈希返回 blob 的原始字节 | `/scene.bin` 会在 `x-koharu-epoch` 响应头里附带当前 epoch。 ### 历史(修改) 所有场景修改都从这里走。每个响应都返回 `{ epoch }`。 | 方法 | 路径 | 用途 | | ------ | ------------------- | ------------------------------------- | | `POST` | `/history/apply` | 应用一个 `Op`(包括 `Op::Batch`) | | `POST` | `/history/undo` | 撤销上一次应用的 op | | `POST` | `/history/redo` | 重新应用上一次撤销的 op | `Op` 是一个带判别字段的联合类型,覆盖添加/删除/更新节点、添加/删除页面、批量操作以及其他场景过渡。请求体是带 JSON tag 的变体。 ### 管线 | 方法 | 路径 | 用途 | | ------ | ------------- | --------------------------------- | | `POST` | `/pipelines` | 以 operation 形式启动一次管线运行 | 请求体字段: - `steps`:按顺序执行的引擎 id(会与注册表校验) - `pages`:可选的 `PageId` 子集;省略时处理整个项目 - `region`:可选的 inpainter 边界框(修复笔刷流程使用) - `targetLanguage`、`systemPrompt`、`defaultFont`:每次运行可覆盖的可选参数 响应携带 `operationId`。进度与完成事件会通过 `/events` 以 `JobStarted`、`JobProgress`、`JobWarning`、`JobFinished` 推送。 ### Operations `/operations` 是正在运行和最近完成的任务(管线 + 下载)的统一登记表。 | 方法 | 路径 | 用途 | | -------- | --------------------- | ---------------------------------------------------- | | `GET` | `/operations` | 所有正在运行或最近的 operation 的快照 | | `DELETE` | `/operations/{id}` | 取消一次管线运行;对下载尽力而为地清理 | ### Downloads | 方法 | 路径 | 用途 | | ------ | ------------- | ----------------------------------------------- | | `GET` | `/downloads` | 所有正在进行或最近的下载快照 | | `POST` | `/downloads` | 启动一次模型包下载(`{ modelId }`) | `modelId` 是通过 `declare_hf_model_package!` 声明的包 id(例如 `"model:comic-text-detector:yolo-v5"`)。响应是 `{ operationId }`,其值复用包 id。 ### LLM 控制 已加载模型是位于 `/llm/current` 的单例资源。 | 方法 | 路径 | 用途 | | -------- | ---------------- | --------------------------------------------- | | `GET` | `/llm/current` | 当前状态(status、target、error) | | `PUT` | `/llm/current` | 加载指定的 target(local 或 provider) | | `DELETE` | `/llm/current` | 卸载 / 释放当前模型 | | `GET` | `/llm/catalog` | 列出可用的本地模型 + 提供商支持的模型 | `PUT /llm/current` 接受 `LlmLoadRequest`: - provider target:`{ kind: "provider", providerId, modelId }` - local target:`{ kind: "local", modelId }` - 可选的 `options { temperature, maxTokens, customSystemPrompt }` `PUT /llm/current` 在加载任务入队后立即返回 `204`。实际就绪状态会作为 `LlmLoaded` 事件发布到 `/events`。 ### 配置 | 方法 | 路径 | 用途 | | -------- | --------------------------------- | ----------------------------------------------- | | `GET` | `/config` | 读取当前 `AppConfig` | | `PATCH` | `/config` | 应用 `ConfigPatch`;持久化并广播变更 | | `PUT` | `/config/providers/{id}/secret` | 保存(或覆盖)某个 provider 的 API key | | `DELETE` | `/config/providers/{id}/secret` | 清除某个 provider 已保存的 API key | `AppConfig` 暴露顶层的 `data`、`http`、`pipeline` 与 `providers`: - `data.path`:本地数据目录,用于运行时、模型缓存与项目 - `http { connectTimeout, readTimeout, maxRetries }`:下载与 provider 请求共用的 HTTP 客户端 - `pipeline { detector, fontDetector, segmenter, bubbleSegmenter, ocr, translator, inpainter, renderer }`:每个阶段选用的引擎 id - `providers[] { id, baseUrl?, apiKey? }`:保存的 API key 在往返中始终以遮蔽占位符 `"[REDACTED]"` 出现,绝不返回原始密钥 内置 provider id: - `openai` - `gemini` - `claude` - `deepseek` - `deepl` - `google-translate` - `caiyun` - `openai-compatible` API key 保存在平台凭据存储中,而不是 `config.toml` 里。PATCH `apiKey: ""` 会清除已保存的 key;PATCH `"[REDACTED]"` 表示保持原值不变。专门的 `/config/providers/{id}/secret` 路由是显式管理某个 provider 密钥的非 PATCH 方式。 ## 事件流 Koharu 通过以下地址暴露 Server-Sent Events: ```text GET /events ``` 行为: - 全新连接(不带 `Last-Event-ID` 请求头)会先收到一个 `Snapshot` 事件,包含当前的任务和下载登记表 - 重连时,服务器会按顺序回放缓冲区中 `seq > Last-Event-ID` 的事件;如果请求的 id 已经从环形缓冲区中滚出,则会重新发送一个 `Snapshot` - 每个实时事件都会用其 `seq` 作为 SSE `id:` 字段 - 维持 15 秒的 keep-alive 当前事件变体包括: - `Snapshot`:用于全新客户端与延迟恢复客户端的完整状态种子 - `JobStarted`、`JobProgress`、`JobWarning`、`JobFinished`:管线任务生命周期 - `DownloadProgress`:包下载进度 - `ConfigChanged`:通过 `PATCH /config` 或 secret 路由应用了配置变更 - `LlmLoaded`、`LlmUnloaded`:LLM 生命周期切换 - `SceneAdvanced`:场景修改使 epoch 推进时触发 ## 典型工作流 新建一个项目时常见的 API 调用顺序: 1. `POST /projects`:创建项目 2. `POST /pages`(或 Tauri 下的 `/pages/from-paths`):导入图片 3. `PUT /llm/current`:加载翻译模型(本地或 provider) 4. `POST /pipelines`:启动 `detect → ocr → translate → inpaint → render` 5. 监听 `GET /events` 直到 `JobFinished` 6. `POST /projects/current/export`,`format = "rendered"` 或 `"psd"` 如果你需要更精细的控制,可以直接 `POST /history/apply` 携带显式 `Op` 负载,而不是运行整条管线。 如果你更想用面向 Agent 的接口,而不是手动编排 HTTP 端点,请参见 [MCP 工具参考](mcp-tools.md)。