> ## Documentation Index
> Fetch the complete documentation index at: https://docs.somark.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用指南

> SoMark CLI & SDK 使用指南。

CLI 适合手边立刻处理文件；SDK 适合把解析能力放进你的程序。两条路通向同一个目标。想先看定位差异，可先回到 [SoMark CLI & SDK 概览](/cli-sdk/index)。

## 1. 安装

SoMark 有 Python 和 JavaScript 两个实现。你用哪门语言，就装哪个包。两个包都同时提供 SDK 和 `somark` CLI 命令。建议先确认本机版本：Python 需要 3.10+，Node.js 需要 18+。

```bash theme={null}
# Python：安装 SDK 和 CLI
pip install somark

# JavaScript：安装 SDK 和 CLI
npm install somark-js
```

<Warning>
  如果 `somark` 命令找不到，通常不是包没装好，而是命令目录没有进 `PATH`。Python 用户可以先试 `python -m somark.cli.main --help`；Node 用户可以先试 `npx somark-js --help`。能跑起来，再考虑把全局命令路径整理好。
</Warning>

<Note>
  如果同时安装了 Python 和 JS 两个版本，CLI中采用的是先安装的那个，另一个版本在安装的时候碰到 CLI 已经安装的情况会跳过安装。想要看当前是什么语言的版本，可以运行 `somark --help`，在最后会有 `[PY]` 或 `[JS]` 的标识。
</Note>

## 2. 认证与配置

远程解析和用量查询需要 API Key。本地 PDF 处理和 SoMarkDown 预览不需要。你可以用 CLI 保存配置，也可以在 SDK 初始化时直接传参。

<Tip>
  如果项目里习惯用 `.env`，可以把 `SOMARK_API_KEY=sk-your-api-key` 放进去，再用
  `python-dotenv`、`dotenv` 或你自己的启动脚本加载。SoMark
  读取的是环境变量本身，不会替你决定怎么加载 `.env`。另外，别把 `.env` 提交到
  Git。
</Tip>

<CodeGroup>
  ```bash CLI theme={null}
  # 交互式登录，会把 API Key 写入本机配置
  somark login

  # 也可以直接设置、读取和查看配置
  somark config set api_key sk-your-api-key
  somark config get api_key
  somark config list

  # 临时使用时，环境变量更轻便
  export SOMARK_API_KEY=sk-your-api-key
  ```

  ```python Python theme={null}
  from somark import SoMark

  # 显式传参最清楚，也最适合服务端程序
  client = SoMark(
      api_key="sk-your-api-key",
      base_url="https://somark.tech/api/v1",
      timeout=60,
      max_retries=2,
  )
  ```

  ```typescript JavaScript theme={null}
  import { SoMark } from 'somark-js'

  // JavaScript 使用 camelCase 参数名
  const client = new SoMark({
    apiKey: 'sk-your-api-key',
    baseUrl: 'https://somark.tech/api/v1',
    timeout: 60,
    maxRetries: 2,
  })
  ```
</CodeGroup>

<Tabs>
  <Tab title="CLI">
    入口：`somark login`、`somark config ...`，或在命令参数和环境变量里传配置。

    **命令参数 / 环境变量**

    | 能力       | 参数 / 字段                        | 说明                                                          |
    | -------- | ------------------------------ | ----------------------------------------------------------- |
    | API Key  | `--api-key`、`SOMARK_API_KEY`   | 用于远程解析和用量查询。                                                |
    | Base URL | `--base-url`、`SOMARK_BASE_URL` | 默认 `https://somark.tech/api/v1`。                            |
    | 超时       | `--timeout`、`SOMARK_TIMEOUT`   | 单位是秒。                                                       |
    | 重试次数     | `SOMARK_MAX_RETRIES`           | 默认 `2`。                                                     |
    | 解析并发     | `SOMARK_PARSE_MAX_CONCURRENCY` | 批量解析文件时的最大并发数，默认必须为 `1`。                                    |
    | 关闭提醒     | `--no-warnings`                | 关闭 SoMark warning 输出；不影响命令结果、退出码和 SDK 返回对象里的 `warnings` 字段。 |

    <Warning>
      `SOMARK_PARSE_MAX_CONCURRENCY` 默认必须保持为 `1`。官方默认给所有用户的解析并发额度也是
      `1`；只有已经明确获批更高并发的用户，才应该把它设置为 `2` 或更高。CLI 在检测到
      `2` 或更高时会通过 warning 通道提示。这是本地 warning，不来自 API。
    </Warning>

    **配置文件字段**

    | 能力       | 参数 / 字段       | 说明                           |
    | -------- | ------------- | ---------------------------- |
    | API Key  | `api_key`     | 保存在 `~/.somark/config.toml`。 |
    | Base URL | `base_url`    | 保存在 `~/.somark/config.toml`。 |
    | 超时       | `timeout`     | 保存在 `~/.somark/config.toml`。 |
    | 重试次数     | `max_retries` | 保存在 `~/.somark/config.toml`。 |

    优先级：命令参数 > 环境变量 > 配置文件 > 默认值。
  </Tab>

  <Tab title="Python">
    入口：`SoMark(...)` 初始化客户端。

    | 能力       | 参数 / 字段                 | 说明                               |
    | -------- | ----------------------- | -------------------------------- |
    | API Key  | `api_key`               | 用于远程解析和用量查询。                     |
    | Base URL | `base_url`              | 默认 `https://somark.tech/api/v1`。 |
    | 超时       | `timeout`               | 单位是秒。                            |
    | 重试次数     | `max_retries`           | 默认 `2`。                          |
    | 配置文件     | `~/.somark/config.toml` | 未显式传参时自动读取。                      |
  </Tab>

  <Tab title="JavaScript">
    入口：`new SoMark(...)` 初始化客户端。

    | 能力       | 参数 / 字段                 | 说明                               |
    | -------- | ----------------------- | -------------------------------- |
    | API Key  | `apiKey`                | 用于远程解析和用量查询。                     |
    | Base URL | `baseUrl`               | 默认 `https://somark.tech/api/v1`。 |
    | 超时       | `timeout`               | 单位是秒。                            |
    | 重试次数     | `maxRetries`            | 默认 `2`。                          |
    | 配置文件     | `~/.somark/config.toml` | 未显式传参时自动读取。                      |
  </Tab>
</Tabs>

## 3. Warnings

SoMark 的 warning 分两类：API warning 和本地 warning。API warning 来自响应顶层 `warnings: List[str]` 字段，和 `code`、`message` 同级。本地 warning 来自 SDK 或 CLI 自己的运行判断，例如批量解析并发设置超过默认额度。

CLI 默认会显示 SoMark warning。需要安静输出时，用全局参数 `--no-warnings`：

```bash CLI theme={null}
somark --no-warnings parse ./document.pdf
```

`--no-warnings` 只关闭 warning 的显示，不改变命令结果、退出码，也不会删除 SDK 返回对象里的 `warnings` 字段。

SDK 默认通过语言原生 warning 通道显示 warning，同时把 warning 字符串保留在返回对象的 `warnings` 字段中。空数组表示没有 warning。

<CodeGroup>
  ```python Python theme={null}
  import warnings
  from somark import SoMark, SoMarkWarning

  # 如果你不希望 Python 程序显示 SoMark warning，可以用 warnings 库过滤
  warnings.filterwarnings("ignore", category=SoMarkWarning)

  client = SoMark(api_key="sk-your-api-key")
  response = client.parser.parse(file="./document.pdf")

  print(response.warnings)  # list[str]
  ```

  ```typescript JavaScript theme={null}
  import { SoMark } from 'somark-js'

  // Node.js 会通过 process warning 通道收到 SoMarkWarning
  process.on('warning', (warning) => {
    if (warning.name === 'SoMarkWarning') {
      console.warn(warning.message)
    }
  })

  const client = new SoMark({ apiKey: 'sk-your-api-key' })
  const response = await client.parser.parse({ file: './document.pdf' })

  console.log(response.warnings) // string[]
  ```
</CodeGroup>

## 4. 解析

解析是 SoMark SDK + CLI 的主线任务。你把文件交给 SoMark，它把结果还给你：Markdown、JSON，或者 ZIP 下载地址。默认输出格式是 `md`，也就是 API 里的 `markdown`。

这里有两种用法：同步解析和异步解析。

同步解析适合“我现在就要结果”的场景。CLI 或 SDK 会把文件发到 `/parse/sync`，然后等服务端把结果返回。代码少，心智负担低，适合单个文件、脚本、调试和中小文档。缺点也很直白：文件越大，你等得越久。

异步解析适合大文件、批处理和后台任务。你先把文件发到 `/parse/async`，马上拿到一个 `task_id`；之后用 `task_id` 去 `/parse/async_check` 查进度。CLI 的 `--wait` 和 SDK 的 `task.wait()` 本质上就是帮你定时轮询。它更适合放进队列、定时任务或服务端流程里。

**同步解析流程**

一次请求直接拿结果。适合脚本、调试和中小文件。

```mermaid actions={false} theme={null}
flowchart LR
  classDef client fill:#ECFEFF,stroke:#0891B2,color:#164E63,stroke-width:1.5px
  classDef api fill:#F0FDFA,stroke:#0F766E,color:#134E4A,stroke-width:1.5px
  classDef result fill:#FFF7ED,stroke:#EA580C,color:#7C2D12,stroke-width:1.5px

  subgraph C["Client"]
    A["file + formats"]:::client
    B["wait inline"]:::client
  end

  subgraph S["SoMark API"]
    P["POST /parse/sync"]:::api
    R["ParseResponse"]:::api
  end

  O["save md / json\nor print zip url"]:::result

  A --> P --> B --> R --> O
```

***

**异步解析流程**

先提交任务，再用 `task_id` 查状态。适合大文件、批处理和后台队列。

```mermaid actions={false} theme={null}
flowchart LR
  classDef client fill:#EEF2FF,stroke:#4F46E5,color:#312E81,stroke-width:1.5px
  classDef api fill:#F0FDFA,stroke:#0F766E,color:#134E4A,stroke-width:1.5px
  classDef decision fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:1.5px
  classDef result fill:#FFF7ED,stroke:#EA580C,color:#7C2D12,stroke-width:1.5px

  subgraph C["Client"]
    A["submit file"]:::client
    T["task_id"]:::client
    W["poll / wait"]:::client
  end

  subgraph S["SoMark API"]
    P["POST /parse/async"]:::api
    Q["POST /parse/async_check"]:::api
  end

  D{"done?"}:::decision
  O["save result"]:::result
  E["error / timeout"]:::result

  A --> P --> T --> W --> Q --> D
  D -- "not yet" --> W
  D -- "success" --> O
  D -- "failed" --> E
```

### 4.1 同步解析

同步解析最适合先把流程跑通：给一个文件，等结果，保存到本地。你可以先从 `md` 开始，确认内容质量，再按需加 `json`、`zip` 或页面特征配置。

<CodeGroup>
  ```bash CLI theme={null}
  # 单格式不传 --out：结果写到 stdout，适合管道和脚本
  somark parse ./document.pdf --formats md

  # 安静模式：关闭 SoMark warning 输出
  somark --no-warnings parse ./document.pdf --formats md

  # 单格式写入指定文件；父目录必须已经存在
  somark parse ./document.pdf --formats md --out ./document.md

  # 多格式必须写入已存在目录；会生成 parsed/document.md 和 parsed/document.json
  mkdir -p ./parsed
  somark parse ./document.pdf --formats md,json --out ./parsed/

  # 多个文件用空格分隔；批量输出目录也必须提前创建
  mkdir -p ./parsed
  somark parse ./a.pdf ./b.pdf --out ./parsed/

  # 也可以从清单读取，每行一个文件路径
  mkdir -p ./parsed
  somark parse --file-list ./files.txt --out ./parsed/

  # 输出 Markdown，并打开标题层级识别和 HTML 表格
  somark parse ./paper.pdf --formats md --title-levels --table-fmt html
  ```

  ```python Python theme={null}
  from pathlib import Path
  from somark import SoMark, ExtractConfig, ElementFormats

  client = SoMark(api_key="sk-your-api-key")

  # formats 可以写 md/json/zip；SDK 会映射到 API 字段
  response = client.parser.parse(
      file="./document.pdf",
      formats=["md", "json"],
      element_formats=ElementFormats(
          image="url",
          formula="latex",
          table="html",
          cs="image",
      ),
      extract_config=ExtractConfig(
          enable_title_level_recognition=True,
          enable_table_cross_page=True,
      ),
  )

  # save 可写入文件，或写入已存在目录
  response.save("./document.md")
  Path("./parsed").mkdir(exist_ok=True)
  response.save("./parsed", format=["md", "json"])

  # 不保存时，直接读取响应字段
  print(response.md)
  print(response.json_output)
  print(response.zip_url)

  # 多文件传列表，返回值也是列表，顺序和输入一致
  responses = client.parser.parse(
      file=["./a.pdf", "./b.pdf"],
      formats=["md"],
  )
  for source, item in zip(["./a.pdf", "./b.pdf"], responses):
      item.save(source.replace(".pdf", ".md"))

  # 读取清单文件：每行一个文件路径，相对路径按当前运行目录解析
  responses = client.parser.parse(
      file="./files.txt",
      file_list=True,
      formats=["md"],
  )
  ```

  ```typescript JavaScript theme={null}
  import * as fs from 'fs'
  import { SoMark } from 'somark-js'

  const client = new SoMark({ apiKey: 'sk-your-api-key' })

  // formats 支持 md/json/zip；JS 参数使用 camelCase
  const response = await client.parser.parse({
    file: './document.pdf',
    formats: ['md', 'json'],
    elementFormats: {
      image: 'url',
      formula: 'latex',
      table: 'html',
      cs: 'image',
    },
    extractConfig: {
      enableTitleLevelRecognition: true,
      enableTableCrossPage: true,
    },
  })

  // save 可写入文件，或写入已存在目录
  response.save('./document.md')
  fs.mkdirSync('./parsed', { recursive: true })
  response.save('./parsed', ['md', 'json'])

  // 不保存时，直接读取响应字段
  console.log(response.md)
  console.log(response.jsonOutput)
  console.log(response.zipUrl)

  // 多文件传数组，返回值也是数组，顺序和输入一致
  const responses = await client.parser.parse({
    file: ['./a.pdf', './b.pdf'],
    formats: ['md'],
  })

  responses.forEach((item, index) => {
    item.save(index === 0 ? './a.md' : './b.md')
  })

  // 读取清单文件：每行一个文件路径，相对路径按当前运行目录解析
  const listResponses = await client.parser.parse({
    file: './files.txt',
    fileList: true,
    formats: ['md'],
  })
  ```
</CodeGroup>

<Tabs>
  <Tab title="CLI">
    入口：`somark parse [files...]`。

    | 能力    | 参数 / 字段                    | 说明                                   |
    | ----- | -------------------------- | ------------------------------------ |
    | 文件    | `[files...]`               | 一个或多个待解析文件路径。多个文件用空格分隔。              |
    | 文件清单  | `--file-list`              | 读取一个文本文件，每个非空行是一个文件路径；相对路径按当前运行目录解析。 |
    | 输出格式  | `--formats`                | 支持 `md`/`markdown`、`json`、`zip`。     |
    | 输出目标  | `--out`                    | 输出文件路径，或已存在目录。CLI 不会自动创建目录。          |
    | 图片格式  | `--image-fmt`              | `url`、`base64`、`none`。               |
    | 公式格式  | `--formula-fmt`            | `latex`、`mathml`、`ascii`。            |
    | 表格格式  | `--table-fmt`              | `markdown`、`html`、`image`。           |
    | 化学结构式 | `--cs-fmt`                 | 当前支持 `image`。                        |
    | 标题层级  | `--title-levels`           | 识别标题级别。                              |
    | 文字跨页  | `--cross-page-text`        | 合并跨页文字。                              |
    | 表格跨页  | `--cross-page-table`       | 合并跨页表格。                              |
    | 文中图   | `--no-inline-image`        | 关闭文中图。                               |
    | 表中图   | `--no-table-image`         | 关闭表中图。                               |
    | 图片理解  | `--no-image-understanding` | 关闭图片理解。                              |
    | 页眉页脚  | `--keep-header-footer`     | 保留页眉页脚。                              |

    **返回处理**

    `--out` 只表示输出目标，不表示“帮我创建目录”，也不表示“把文件名当模板”。这样脚本里更容易预测结果。

    | 场景               | 行为                                            |
    | ---------------- | --------------------------------------------- |
    | 单文件单格式，无 `--out` | 输出到 stdout。                                   |
    | 多格式，无 `--out`    | 报错；多格式需要 `--out` 指向已存在目录。                     |
    | `--out` 是已存在目录   | 按输入文件 stem 生成 `.md`、`.json`、`.zip`。           |
    | `--out` 是文件路径    | 只允许单格式；父目录必须已经存在。                             |
    | `--out` 是不存在目录   | 报错；请先自己创建目录。                                  |
    | ZIP 输出           | 响应里是下载 URL；保存为 `.zip` 时，会下载这个 URL 指向的内容并写入文件。 |

    **多文件执行**

    | 能力    | 参数 / 字段      | 说明                                          |
    | ----- | ------------ | ------------------------------------------- |
    | 并发限制  | 环境变量配置       | 默认并发为 `1`；配置方式见第 2 节“认证与配置”。                |
    | 并发提醒  | warning 通道   | 并发配置为 `2` 或更高时，会通过本地 warning 提示官方默认额度是 `1`。 |
    | 进度与结果 | CLI 输出       | 显示总文件数、当前进度、每个文件是否存在、状态、用时和输出位置。            |
    | 缺失文件  | `missing` 状态 | CLI 不发送缺失文件，继续处理其他文件；最终退出码为 `1`。            |
  </Tab>

  <Tab title="Python">
    入口：`client.parser.parse(...)`。

    | 能力   | 参数 / 字段           | 说明                                           |
    | ---- | ----------------- | -------------------------------------------- |
    | 文件   | `file`            | 待解析文件路径；多文件时传 `list[str]`。                   |
    | 文件清单 | `file_list`       | 设为 `True` 时，`file` 会被当作清单文件读取；相对路径按当前运行目录解析。 |
    | 输出格式 | `formats`         | 支持 `md`/`markdown`、`json`、`zip`。             |
    | 元素格式 | `element_formats` | 传入 `ElementFormats(...)` 或同结构字典。             |
    | 提取配置 | `extract_config`  | 传入 `ExtractConfig(...)` 或同结构字典。              |

    **元素格式：`ElementFormats(...)`**

    | 能力    | 参数 / 字段                  | 说明                         |
    | ----- | ------------------------ | -------------------------- |
    | 图片格式  | `ElementFormats.image`   | `url`、`base64`、`none`。     |
    | 公式格式  | `ElementFormats.formula` | `latex`、`mathml`、`ascii`。  |
    | 表格格式  | `ElementFormats.table`   | `markdown`、`html`、`image`。 |
    | 化学结构式 | `ElementFormats.cs`      | 当前支持 `image`。              |

    **提取配置：`ExtractConfig(...)`**

    | 能力   | 参数 / 字段                                        | 说明           |
    | ---- | ---------------------------------------------- | ------------ |
    | 标题层级 | `ExtractConfig.enable_title_level_recognition` | 识别标题级别。      |
    | 文字跨页 | `ExtractConfig.enable_text_cross_page`         | 合并跨页文字。      |
    | 表格跨页 | `ExtractConfig.enable_table_cross_page`        | 合并跨页表格。      |
    | 文中图  | `ExtractConfig.enable_inline_image`            | SDK 中用布尔值配置。 |
    | 表中图  | `ExtractConfig.enable_table_image`             | SDK 中用布尔值配置。 |
    | 图片理解 | `ExtractConfig.enable_image_understanding`     | SDK 中用布尔值配置。 |
    | 页眉页脚 | `ExtractConfig.keep_header_footer`             | 保留页眉页脚。      |

    **保存结果：`response.save(path, format)`**

    | 能力   | 参数 / 字段  | 说明              |
    | ---- | -------- | --------------- |
    | 保存路径 | `path`   | 输出文件路径，或已存在目录。  |
    | 保存格式 | `format` | 可选；支持单个格式或多个格式。 |

    `path` 可以是文件路径，也可以是已存在目录。多个格式只能保存到已存在目录。SDK 不自动创建目录。保存 `zip` 时会下载 `response.zip_url` 指向的内容；不保存时，直接读取 `response.md`、`response.json_output`、`response.zip_url`。

    **响应字段：`ParseResponse`**

    | 能力       | 参数 / 字段             | 说明                                             |
    | -------- | ------------------- | ---------------------------------------------- |
    | Warnings | `response.warnings` | API warning 和本地 warning 字符串列表；空数组表示没有 warning。 |

    **多文件返回值**

    | 能力   | 参数 / 字段               | 说明                              |
    | ---- | --------------------- | ------------------------------- |
    | 返回类型 | `list[ParseResponse]` | 只有多文件或 `file_list=True` 时返回列表。  |
    | 返回顺序 | 输入顺序                  | 即使并发完成顺序不同，返回列表也和输入文件顺序一致。      |
    | 最大并发 | 环境变量配置                | 默认并发为 `1`；配置方式见第 2 节“认证与配置”。    |
    | 缺失文件 | `InvalidParamError`   | SDK 会先检查所有文件；只要有缺失文件，就不会发送任何请求。 |
  </Tab>

  <Tab title="JavaScript">
    入口：`client.parser.parse(...)`。

    | 能力   | 参数 / 字段          | 说明                                           |
    | ---- | ---------------- | -------------------------------------------- |
    | 文件   | `file`           | 待解析文件路径；多文件时传 `string[]`。                    |
    | 文件清单 | `fileList`       | 设为 `true` 时，`file` 会被当作清单文件读取；相对路径按当前运行目录解析。 |
    | 输出格式 | `formats`        | 支持 `md`/`markdown`、`json`、`zip`。             |
    | 元素格式 | `elementFormats` | 传入元素格式对象。                                    |
    | 提取配置 | `extractConfig`  | 传入提取配置对象。                                    |

    **元素格式：`elementFormats`**

    | 能力    | 参数 / 字段                  | 说明                         |
    | ----- | ------------------------ | -------------------------- |
    | 图片格式  | `elementFormats.image`   | `url`、`base64`、`none`。     |
    | 公式格式  | `elementFormats.formula` | `latex`、`mathml`、`ascii`。  |
    | 表格格式  | `elementFormats.table`   | `markdown`、`html`、`image`。 |
    | 化学结构式 | `elementFormats.cs`      | 当前支持 `image`。              |

    **提取配置：`extractConfig`**

    | 能力   | 参数 / 字段                                     | 说明           |
    | ---- | ------------------------------------------- | ------------ |
    | 标题层级 | `extractConfig.enableTitleLevelRecognition` | 识别标题级别。      |
    | 文字跨页 | `extractConfig.enableTextCrossPage`         | 合并跨页文字。      |
    | 表格跨页 | `extractConfig.enableTableCrossPage`        | 合并跨页表格。      |
    | 文中图  | `extractConfig.enableInlineImage`           | SDK 中用布尔值配置。 |
    | 表中图  | `extractConfig.enableTableImage`            | SDK 中用布尔值配置。 |
    | 图片理解 | `extractConfig.enableImageUnderstanding`    | SDK 中用布尔值配置。 |
    | 页眉页脚 | `extractConfig.keepHeaderFooter`            | 保留页眉页脚。      |

    **保存结果：`response.save(path, format)`**

    | 能力   | 参数 / 字段  | 说明              |
    | ---- | -------- | --------------- |
    | 保存路径 | `path`   | 输出文件路径，或已存在目录。  |
    | 保存格式 | `format` | 可选；支持单个格式或多个格式。 |

    `path` 可以是文件路径，也可以是已存在目录。多个格式只能保存到已存在目录。SDK 不自动创建目录。保存 `zip` 时会下载 `response.zipUrl` 指向的内容；不保存时，直接读取 `response.md`、`response.jsonOutput`、`response.zipUrl`。

    **响应字段：`ParseResponse`**

    | 能力       | 参数 / 字段             | 说明                                             |
    | -------- | ------------------- | ---------------------------------------------- |
    | Warnings | `response.warnings` | API warning 和本地 warning 字符串数组；空数组表示没有 warning。 |

    **多文件返回值**

    | 能力   | 参数 / 字段                    | 说明                              |
    | ---- | -------------------------- | ------------------------------- |
    | 返回类型 | `Promise<ParseResponse[]>` | 只有多文件或 `fileList: true` 时返回数组。  |
    | 返回顺序 | 输入顺序                       | 即使并发完成顺序不同，返回数组也和输入文件顺序一致。      |
    | 最大并发 | 环境变量配置                     | 默认并发为 `1`；配置方式见第 2 节“认证与配置”。    |
    | 缺失文件 | `InvalidParamError`        | SDK 会先检查所有文件；只要有缺失文件，就不会发送任何请求。 |
  </Tab>
</Tabs>

### 4.2 异步解析

异步解析适合大文件和批处理。先提交任务，拿到 `task_id`；稍后轮询，直到成功或失败。推荐轮询间隔 3 到 5 秒，别太频繁，服务器也需要安静工作。

<CodeGroup>
  ```bash CLI theme={null}
  # 1. 提交任务，只拿 task_id，不等待结果
  somark parse ./large.pdf --async --formats md,json

  # 一次提交多个文件，每个文件都会得到独立的 task_id
  somark parse ./a.pdf ./b.pdf --async --formats md

  # 也可以从清单读取
  somark parse --file-list ./files.txt --async --formats md

  # 2. 等待已有任务完成，并把结果保存到本地
  somark parse --task-id task_xxx --wait --out ./large.md

  # 3. 只查一次状态，不阻塞等待
  somark parse --task-id task_xxx
  ```

  ```python Python theme={null}
  from somark import SoMark

  client = SoMark(api_key="sk-your-api-key")

  # create 只提交任务，适合放进队列或后台流程
  task = client.parser.create(file="./large.pdf", formats=["md"])
  print(task.id)

  # 多文件提交会返回 list[Task]
  tasks = client.parser.create(file=["./a.pdf", "./b.pdf"], formats=["md"])
  for task in tasks:
      print(task.id)

  # 清单文件同样支持
  tasks = client.parser.create(file="./files.txt", file_list=True, formats=["md"])

  # wait 会定时轮询 async_check，直到成功、失败或超时
  result = task.wait(poll_interval=3.0, timeout=120.0)
  result.save("./large.md")
  ```

  ```typescript JavaScript theme={null}
  import { SoMark } from 'somark-js'

  const client = new SoMark({ apiKey: 'sk-your-api-key' })

  // create 只提交任务，马上返回 task id
  const task = await client.parser.create({
    file: './large.pdf',
    formats: ['md'],
  })

  console.log(task.id)

  // 多文件提交会返回 Task[]
  const tasks = await client.parser.create({
    file: ['./a.pdf', './b.pdf'],
    formats: ['md'],
  })

  tasks.forEach((item) => console.log(item.id))

  // 清单文件同样支持
  const listTasks = await client.parser.create({
    file: './files.txt',
    fileList: true,
    formats: ['md'],
  })

  // wait 会定时轮询 async_check，直到成功、失败或超时
  const result = await task.wait({ pollInterval: 3, timeout: 120 })
  result.save('./large.md')
  ```
</CodeGroup>

<Tabs>
  <Tab title="CLI">
    入口：`somark parse [files...] --async` 提交任务；`somark parse --task-id task_xxx` 查询任务。

    **提交任务：`somark parse [files...] --async`**

    | 能力   | 参数 / 字段       | 说明                                   |
    | ---- | ------------- | ------------------------------------ |
    | 文件   | `[files...]`  | 提交任务时必填；多个文件用空格分隔。                   |
    | 文件清单 | `--file-list` | 读取一个文本文件，每个非空行是一个文件路径；相对路径按当前运行目录解析。 |
    | 输出格式 | `--formats`   | 与同步解析一致。                             |

    多文件异步提交时，每个文件会得到独立的 `task_id`。CLI 会显示每个文件的存在性、提交状态、用时和任务 ID。

    **查询 / 等待任务：`somark parse --task-id task_xxx`**

    | 能力    | 参数 / 字段     | 说明                   |
    | ----- | ----------- | -------------------- |
    | 任务 ID | `--task-id` | 查询已有任务。              |
    | 等待开关  | `--wait`    | 阻塞等待完成。              |
    | 输出目标  | `--out`     | 等待完成后保存结果；规则与同步解析一致。 |
  </Tab>

  <Tab title="Python">
    入口：`client.parser.create(...)` 提交任务。

    **提交任务：`client.parser.create(...)`**

    | 能力   | 参数 / 字段     | 说明                                           |
    | ---- | ----------- | -------------------------------------------- |
    | 文件   | `file`      | 提交任务时必填；多文件时传 `list[str]`。                   |
    | 文件清单 | `file_list` | 设为 `True` 时，`file` 会被当作清单文件读取；相对路径按当前运行目录解析。 |
    | 输出格式 | `formats`   | 与同步解析一致。                                     |

    多文件提交时返回 `list[Task]`，顺序和输入文件一致。

    **任务对象字段**

    | 能力       | 参数 / 字段         | 说明                                           |
    | -------- | --------------- | -------------------------------------------- |
    | 任务 ID    | `task.id`       | 由提交任务后返回的任务对象提供。                             |
    | Warnings | `task.warnings` | 任务提交或状态查询时收到的 warning 字符串列表；空数组表示没有 warning。 |

    **等待结果：`task.wait(...)`**

    | 能力   | 参数 / 字段         | 说明             |
    | ---- | --------------- | -------------- |
    | 轮询间隔 | `poll_interval` | 单位是秒，默认 `3`。   |
    | 等待超时 | `timeout`       | 单位是秒，默认 `120`。 |

    **查询状态：`task.refresh()`**

    | 能力 | 参数 / 字段 | 说明                |
    | -- | ------- | ----------------- |
    | 参数 | 无       | 更新任务状态、页数、文件名等信息。 |

    **保存结果：`result.save(path, format)`**

    | 能力   | 参数 / 字段  | 说明              |
    | ---- | -------- | --------------- |
    | 保存路径 | `path`   | 输出文件路径，或已存在目录。  |
    | 保存格式 | `format` | 可选；支持单个格式或多个格式。 |
  </Tab>

  <Tab title="JavaScript">
    入口：`client.parser.create(...)` 提交任务。

    **提交任务：`client.parser.create(...)`**

    | 能力   | 参数 / 字段    | 说明                                           |
    | ---- | ---------- | -------------------------------------------- |
    | 文件   | `file`     | 提交任务时必填；多文件时传 `string[]`。                    |
    | 文件清单 | `fileList` | 设为 `true` 时，`file` 会被当作清单文件读取；相对路径按当前运行目录解析。 |
    | 输出格式 | `formats`  | 与同步解析一致。                                     |

    多文件提交时返回 `Task[]`，顺序和输入文件一致。

    **任务对象字段**

    | 能力       | 参数 / 字段         | 说明                                           |
    | -------- | --------------- | -------------------------------------------- |
    | 任务 ID    | `task.id`       | 由提交任务后返回的任务对象提供。                             |
    | Warnings | `task.warnings` | 任务提交或状态查询时收到的 warning 字符串数组；空数组表示没有 warning。 |

    **等待结果：`task.wait(...)`**

    | 能力   | 参数 / 字段        | 说明             |
    | ---- | -------------- | -------------- |
    | 轮询间隔 | `pollInterval` | 单位是秒，默认 `3`。   |
    | 等待超时 | `timeout`      | 单位是秒，默认 `120`。 |

    **查询状态：`task.refresh()`**

    | 能力 | 参数 / 字段 | 说明                |
    | -- | ------- | ----------------- |
    | 参数 | 无       | 更新任务状态、页数、文件名等信息。 |

    **保存结果：`result.save(path, format)`**

    | 能力   | 参数 / 字段  | 说明              |
    | ---- | -------- | --------------- |
    | 保存路径 | `path`   | 输出文件路径，或已存在目录。  |
    | 保存格式 | `format` | 可选；支持单个格式或多个格式。 |
  </Tab>
</Tabs>

## 5. 用量查询

用量查询会返回当前 API Key 的剩余额度和控制台地址。它也可以顺手检查 API Key 是否有效。

<CodeGroup>
  ```bash CLI theme={null}
  # 默认用表格显示，适合人眼查看
  somark usage

  # JSON 更适合脚本读取
  somark usage --format json

  # text 输出更轻，适合日志
  somark usage --format text
  ```

  ```python Python theme={null}
  from somark import SoMark

  client = SoMark(api_key="sk-your-api-key")

  # 返回的是结构化对象，字段名使用 Python snake_case
  usage = client.usage.get()

  print(usage.remaining_paid_pages)
  print(usage.remaining_free_pages_today)
  print(usage.remaining_free_pages_this_month)
  print(usage.dashboard_url)
  ```

  ```typescript JavaScript theme={null}
  import { SoMark } from 'somark-js'

  const client = new SoMark({ apiKey: 'sk-your-api-key' })

  // 返回的是结构化对象，字段名使用 JavaScript camelCase
  const usage = await client.usage.get()

  console.log(usage.remainingPaidPages)
  console.log(usage.remainingFreePagesToday)
  console.log(usage.remainingFreePagesThisMonth)
  console.log(usage.dashboardUrl)
  ```
</CodeGroup>

<Tabs>
  <Tab title="CLI">
    入口：`somark usage`。

    **命令参数**

    | 能力   | 参数 / 字段    | 说明                                           |
    | ---- | ---------- | -------------------------------------------- |
    | 输出格式 | `--format` | CLI 展示格式，默认 `table`。支持`text`、`json`、`table`。 |

    **输出字段**

    | 能力     | 参数 / 字段 | 说明             |
    | ------ | ------- | -------------- |
    | 付费剩余页数 | 输出字段    | 所有未过期付费套餐剩余页数。 |
    | 今日免费剩余 | 输出字段    | 今日免费额度。        |
    | 本月免费剩余 | 输出字段    | 本月免费额度。        |
    | 控制台地址  | 输出字段    | SoMark 控制台链接。  |
  </Tab>

  <Tab title="Python">
    入口：`client.usage.get()`。这个方法不需要额外参数。

    **返回字段**

    | 能力       | 参数 / 字段                           | 说明                                 |
    | -------- | --------------------------------- | ---------------------------------- |
    | 付费剩余页数   | `remaining_paid_pages`            | 所有未过期付费套餐剩余页数。                     |
    | 今日免费剩余   | `remaining_free_pages_today`      | 今日免费额度。                            |
    | 本月免费剩余   | `remaining_free_pages_this_month` | 本月免费额度。                            |
    | 控制台地址    | `dashboard_url`                   | SoMark 控制台链接。                      |
    | Warnings | `usage.warnings`                  | API warning 字符串列表；空数组表示没有 warning。 |
  </Tab>

  <Tab title="JavaScript">
    入口：`client.usage.get()`。这个方法不需要额外参数。

    **返回字段**

    | 能力       | 参数 / 字段                       | 说明                                 |
    | -------- | ----------------------------- | ---------------------------------- |
    | 付费剩余页数   | `remainingPaidPages`          | 所有未过期付费套餐剩余页数。                     |
    | 今日免费剩余   | `remainingFreePagesToday`     | 今日免费额度。                            |
    | 本月免费剩余   | `remainingFreePagesThisMonth` | 本月免费额度。                            |
    | 控制台地址    | `dashboardUrl`                | SoMark 控制台链接。                      |
    | Warnings | `usage.warnings`              | API warning 字符串数组；空数组表示没有 warning。 |
  </Tab>
</Tabs>

## 6. SoMarkDown 服务

SoMarkDown 服务会在本地启动一个预览服务器，用浏览器打开 `.md` 或 `.smd` 文件。它不需要 API Key，也不消耗额度。底层渲染能力来自 [SoMarkAI/SoMarkDown](https://github.com/SoMarkAI/SoMarkDown)，需要看语法和渲染器细节时可以直接跳过去。

<CodeGroup>
  ```bash CLI theme={null}
  # 预览 SoMarkDown 文件，并自动打开浏览器
  somark preview ./document.smd

  # 指定本地服务监听地址和端口
  somark preview ./document.md --host 127.0.0.1 --port 7878

  # 只启动服务，不自动打开浏览器
  somark preview --no-open
  ```

  ```python Python theme={null}
  from somark import SoMarkDownPreview

  preview = SoMarkDownPreview()

  # 启动本地预览服务；不需要 API Key
  server = preview.start(
      file="./document.smd",
      host="127.0.0.1",
      port=7878,
      open_browser=True,
  )

  print(server.file_url)

  # 脚本结束前主动停止服务
  server.stop()
  ```

  ```javascript JavaScript theme={null}
  import { SoMarkDownPreview } from 'somark-js'

  const preview = new SoMarkDownPreview()

  // 启动本地预览服务；不需要 API Key
  const server = await preview.start({
    file: './document.smd',
    host: '127.0.0.1',
    port: 7878,
    openBrowser: true,
  })

  console.log(server.fileUrl)
  await server.stop()
  ```
</CodeGroup>

<Callout icon="braces" color="#0F766E">
  JavaScript SDK 还额外导出 `SoMarkDown`。它不启动本地 HTTP 服务，直接把
  Markdown / SoMarkDown 字符串渲染成 HTML；适合在 Node
  服务、自定义前端或小工具里接入渲染结果。
</Callout>

```javascript JavaScript theme={null}
import { SoMarkDown } from 'somark-js'

const renderer = new SoMarkDown()
const html = renderer.render('Inline: $e^{i\\pi} + 1 = 0$')

console.log(html)
```

<Tabs>
  <Tab title="CLI">
    入口：`somark preview [file]`。

    | 能力      | 参数 / 字段     | 说明                 |
    | ------- | ----------- | ------------------ |
    | 预览文件    | `[file]`    | 支持 `.md` 和 `.smd`。 |
    | 绑定地址    | `--host`    | 默认 `127.0.0.1`。    |
    | 端口      | `--port`    | 默认 `7878`。         |
    | 自动打开浏览器 | `--no-open` | 关闭自动打开浏览器。         |

    **输出字段**

    | 能力   | 参数 / 字段 | 说明          |
    | ---- | ------- | ----------- |
    | 服务地址 | 输出字段    | 本地服务地址。     |
    | 文件地址 | 输出字段    | 带文件参数的预览地址。 |

    停止服务：在终端按 Ctrl+C。
  </Tab>

  <Tab title="Python">
    入口：`SoMarkDownPreview().start(...)`。

    | 能力      | 参数 / 字段        | 说明                 |
    | ------- | -------------- | ------------------ |
    | 预览文件    | `file`         | 支持 `.md` 和 `.smd`。 |
    | 绑定地址    | `host`         | 默认 `127.0.0.1`。    |
    | 端口      | `port`         | 默认 `7878`。         |
    | 自动打开浏览器 | `open_browser` | SDK 中用布尔值配置。       |

    **返回对象字段**

    | 能力   | 参数 / 字段           | 说明          |
    | ---- | ----------------- | ----------- |
    | 服务地址 | `server.url`      | 本地服务地址。     |
    | 文件地址 | `server.file_url` | 带文件参数的预览地址。 |

    **停止服务：`server.stop()`**

    | 能力 | 参数 / 字段 | 说明        |
    | -- | ------- | --------- |
    | 参数 | 无       | 停止本地预览服务。 |
  </Tab>

  <Tab title="JavaScript">
    入口：`new SoMarkDownPreview().start(...)`。

    | 能力      | 参数 / 字段       | 说明                 |
    | ------- | ------------- | ------------------ |
    | 预览文件    | `file`        | 支持 `.md` 和 `.smd`。 |
    | 绑定地址    | `host`        | 默认 `127.0.0.1`。    |
    | 端口      | `port`        | 默认 `7878`。         |
    | 自动打开浏览器 | `openBrowser` | SDK 中用布尔值配置。       |

    **返回对象字段**

    | 能力   | 参数 / 字段          | 说明          |
    | ---- | ---------------- | ----------- |
    | 服务地址 | `server.url`     | 本地服务地址。     |
    | 文件地址 | `server.fileUrl` | 带文件参数的预览地址。 |

    **停止服务：`server.stop()`**

    | 能力 | 参数 / 字段 | 说明        |
    | -- | ------- | --------- |
    | 参数 | 无       | 停止本地预览服务。 |

    **字符串渲染：`new SoMarkDown().render(markdown)`**

    | 能力          | 参数 / 字段    | 说明                         |
    | ----------- | ---------- | -------------------------- |
    | Markdown 内容 | `markdown` | Markdown / SoMarkDown 字符串。 |
  </Tab>
</Tabs>

## 7. PDF 处理

PDF 处理目前提供本地 PDF 转图片。它适合做预览图、调试页面布局，或者把 PDF 页面交给别的视觉处理流程。Python 侧的本地 PDF 能力基于 [SoMarkAI/SoPDF](https://github.com/SoMarkAI/SoPDF)。

<CodeGroup>
  ```bash CLI theme={null}
  # 把 PDF 每一页转成图片，输出到当前目录
  somark pdf toimg ./document.pdf

  # 指定输出目录和分辨率
  somark pdf toimg ./document.pdf --out ./pages --dpi 200

  # 自定义图片文件名
  somark pdf toimg ./document.pdf --format "{name}-{n}.png"
  ```

  ```python Python theme={null}
  from somark import PDFProcessor

  pdf = PDFProcessor()

  # 本地处理，不消耗解析额度
  result = pdf.to_images(
      file="./document.pdf",
      out="./pages",
      filename_format="{name}.page-{n}.png",
      dpi=200,
  )

  print(result.files)
  ```

  ```typescript JavaScript theme={null}
  import { PDFProcessor } from 'somark-js'

  const pdf = new PDFProcessor()

  // 本地处理，不消耗解析额度
  const result = await pdf.toImages({
    file: './document.pdf',
    out: './pages',
    filenameFormat: '{name}.page-{n}.png',
    dpi: 200,
  })

  console.log(result.files)
  ```
</CodeGroup>

<Tabs>
  <Tab title="CLI">
    入口：`somark pdf toimg <file>`。

    | 能力     | 参数 / 字段    | 说明                        |
    | ------ | ---------- | ------------------------- |
    | PDF 文件 | `<file>`   | 待转换 PDF。                  |
    | 输出目录   | `--out`    | 默认 `./`。                  |
    | 文件名模板  | `--format` | 默认 `{name}.page-{n}.png`。 |
    | 分辨率    | `--dpi`    | 默认 `150`。                 |

    **输出字段**

    | 能力   | 参数 / 字段 | 说明              |
    | ---- | ------- | --------------- |
    | 结果文件 | 输出字段    | 转换成功后的图片路径列表。   |
    | 错误信息 | 输出字段    | 本地依赖缺失或转换失败时返回。 |
  </Tab>

  <Tab title="Python">
    入口：`PDFProcessor().to_images(...)`。

    | 能力     | 参数 / 字段           | 说明                        |
    | ------ | ----------------- | ------------------------- |
    | PDF 文件 | `file`            | 待转换 PDF。                  |
    | 输出目录   | `out`             | 默认 `./`。                  |
    | 文件名模板  | `filename_format` | 默认 `{name}.page-{n}.png`。 |
    | 分辨率    | `dpi`             | 默认 `150`。                 |

    **返回字段**

    | 能力   | 参数 / 字段        | 说明              |
    | ---- | -------------- | --------------- |
    | 结果文件 | `result.files` | 转换成功后的图片路径列表。   |
    | 错误信息 | `result.error` | 本地依赖缺失或转换失败时返回。 |
  </Tab>

  <Tab title="JavaScript">
    入口：`new PDFProcessor().toImages(...)`。

    | 能力     | 参数 / 字段          | 说明                        |
    | ------ | ---------------- | ------------------------- |
    | PDF 文件 | `file`           | 待转换 PDF。                  |
    | 输出目录   | `out`            | 默认 `./`。                  |
    | 文件名模板  | `filenameFormat` | 默认 `{name}.page-{n}.png`。 |
    | 分辨率    | `dpi`            | 默认 `150`。                 |

    **返回字段**

    | 能力   | 参数 / 字段        | 说明              |
    | ---- | -------------- | --------------- |
    | 结果文件 | `result.files` | 转换成功后的图片路径列表。   |
    | 错误信息 | `result.error` | 本地依赖缺失或转换失败时返回。 |
  </Tab>
</Tabs>

## 8. Doctor 自动修复

Doctor 用来检查安装状态、网络、API Key 和本地预览资源。它是命令行里的体检表，不治百病，但能发现不少低级事故。

```bash CLI theme={null}
# 跑一遍基础诊断：配置、网络、预览资源都看一眼
somark doctor

# 尝试修复 SoMarkDown 预览资源
somark doctor fix

# 只检查网络连通性
somark doctor ping
```

Doctor 是 CLI 维护命令，SDK 不提供对应资源。写程序时通常不需要它；遇到命令跑不起来、预览打不开、网络不确定时，再请它出场。

更底层的接口、字段和响应结构，请看 API Reference。它由同一套接口规范生成，少一点手写，多一点可靠。
