Metadata-Version: 2.5
Name: zleap_parser
Version: 0.1.4
Summary: zleap 文档解析 SDK：多格式文件/URL 解析为 markdown，经 zleap-sag 提取、octx 打包为 *.octx 归档
Author: zleap
License-Expression: MIT
License-File: LICENSE
License-File: LICENSES/Apache-2.0.txt
Keywords: document,markdown,octx,parser,sag
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Text Processing :: Markup
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: beautifulsoup4>=4.15.0
Requires-Dist: camoufox<0.6,>=0.5.4
Requires-Dist: octx>=0.1.6
Requires-Dist: playwright>=1.40
Requires-Dist: pyarrow>=15
Requires-Dist: pycryptodome>=3.19
Requires-Dist: pydantic<3,>=2.13.4
Requires-Dist: rfc8785>=0.1.4
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: uuid6>=2025.0.1
Requires-Dist: zleap-sag>=0.7.1
Provides-Extra: bigdb
Requires-Dist: clickhouse-connect>=0.8; extra == 'bigdb'
Requires-Dist: psycopg2-binary>=2.9; extra == 'bigdb'
Requires-Dist: pymysql>=1.1; extra == 'bigdb'
Provides-Extra: bigdb-clickhouse
Requires-Dist: clickhouse-connect>=0.8; extra == 'bigdb-clickhouse'
Provides-Extra: bigdb-mysql
Requires-Dist: pymysql>=1.1; extra == 'bigdb-mysql'
Provides-Extra: bigdb-postgres
Requires-Dist: psycopg2-binary>=2.9; extra == 'bigdb-postgres'
Provides-Extra: db
Requires-Dist: oracledb>=2.0; extra == 'db'
Requires-Dist: psycopg2-binary>=2.9; extra == 'db'
Requires-Dist: pymssql>=2.2; extra == 'db'
Requires-Dist: pymysql>=1.1; extra == 'db'
Provides-Extra: db-all
Requires-Dist: clickhouse-connect>=0.8; extra == 'db-all'
Requires-Dist: oracledb>=2.0; extra == 'db-all'
Requires-Dist: psycopg2-binary>=2.9; extra == 'db-all'
Requires-Dist: pymssql>=2.2; extra == 'db-all'
Requires-Dist: pymysql>=1.1; extra == 'db-all'
Provides-Extra: db-mssql
Requires-Dist: pymssql>=2.2; extra == 'db-mssql'
Provides-Extra: db-mysql
Requires-Dist: pymysql>=1.1; extra == 'db-mysql'
Provides-Extra: db-oracle
Requires-Dist: oracledb>=2.0; extra == 'db-oracle'
Provides-Extra: db-postgres
Requires-Dist: psycopg2-binary>=2.9; extra == 'db-postgres'
Provides-Extra: dev
Requires-Dist: build>=1.5.0; extra == 'dev'
Requires-Dist: eventlet>=0.37; extra == 'dev'
Requires-Dist: gevent>=24.11; extra == 'dev'
Requires-Dist: mypy>=2.3.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.15.1; extra == 'dev'
Requires-Dist: pytest>=9.1.1; extra == 'dev'
Requires-Dist: ruff>=0.16.2; extra == 'dev'
Requires-Dist: twine>=7.0.0; extra == 'dev'
Requires-Dist: uvloop>=0.21; (sys_platform != 'win32') and extra == 'dev'
Provides-Extra: docx
Requires-Dist: docling>=2.73.1; extra == 'docx'
Requires-Dist: markitdown[docx]>=0.1.7; extra == 'docx'
Provides-Extra: media
Requires-Dist: docling-slim[format-video]<3,>=2.119.0; extra == 'media'
Requires-Dist: docling[asr]<3,>=2.119.0; extra == 'media'
Requires-Dist: setuptools<81,>=80; extra == 'media'
Provides-Extra: pdf
Requires-Dist: docling>=2.73.1; extra == 'pdf'
Requires-Dist: markitdown[pdf]>=0.1.7; extra == 'pdf'
Requires-Dist: pypdfium2>=5.12.1; extra == 'pdf'
Requires-Dist: rapidocr>=3.9.2; extra == 'pdf'
Provides-Extra: ppt
Requires-Dist: docling>=2.73.1; extra == 'ppt'
Requires-Dist: markitdown[pptx]>=0.1.7; extra == 'ppt'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: xlsx
Requires-Dist: docling>=2.73.1; extra == 'xlsx'
Requires-Dist: markitdown[xlsx]>=0.1.7; extra == 'xlsx'
Requires-Dist: matplotlib>=3.11.1; extra == 'xlsx'
Requires-Dist: openpyxl>=3.1.5; extra == 'xlsx'
Description-Content-Type: text/markdown

# zleap_parser

`zleap_parser` 是 zleap 公司开源的**文档解析 SDK**：把本地文件或远程 HTML URL 解析为 markdown，经 `sag` 提取结构化产物后由 `octx` 打包为可传播的 `.octx` Package，供下游项目以 pip 方式集成使用。

## 阅读顺序

1. **计划总览**（[`plan/PLAN.md`](./plan/PLAN.md)）：项目定位与规划
2. **SDK 导入手册**（[`docs/sdk/install.md`](./docs/sdk/install.md)）：环境要求、安装、快速上手
3. **配置手册**（[`docs/sdk/configuration.md`](./docs/sdk/configuration.md)）：模块级一次配置，`Parser`/`Connector` 复用
4. **文档解析 API 手册**（[`docs/sdk/parse_api.md`](./docs/sdk/parse_api.md)）：`Parser` 用法与异常表
5. **连接器 API 手册**（[`docs/sdk/connector_api.md`](./docs/sdk/connector_api.md)）：数据源同步
6. **公共服务手册**（[`docs/service/`](./docs/service/)）：部署与 HTTP API

## 快速上手

需要 Python 3.11 或更高版本：

```bash
# 从 PyPI 安装
python -m pip install zleap_parser

# 音频/视频转写另需 ASR extra 与系统 ffmpeg
python -m pip install "zleap_parser[media]"

# 数据库连接器（DbAdapter / BigDbAdapter）按协议组安装驱动
python -m pip install "zleap_parser[db]"       # db_connector：MySQL/PostgreSQL/Oracle/SQL Server
python -m pip install "zleap_parser[bigdb]"    # bigdb_connector：StarRocks/ClickHouse/Doris 等
python -m pip install "zleap_parser[db-all]"   # db + bigdb 全部常用驱动（不含厂商私有驱动）

# RTF 解析需系统安装 LibreOffice，并启用 pdf extra
python -m pip install "zleap_parser[pdf]"
# 如 soffice/libreoffice 不在 PATH，可设置 ZLEAP_LIBREOFFICE_BIN

# 源码开发安装（本地仓库）
pip install -r requirements.txt   # 上游依赖（octx / zleap-sag / uuid6 / pycryptodome + 开发工具链）
pip install -e .                  # 可编辑安装，便于开发调试

# 使用 uv 运行音视频解析时启用 media extra
uv run --extra media python src/main.py doc/1.m4a

# URL 采集首次运行前安装并检查 Camoufox 浏览器二进制
python -m camoufox fetch
python -m camoufox version
```

模块级配置一次（`llm` 与 `embedding` 必填），`Parser` 与 `Connector` 自动继承复用：

```python
import zleap_parser
from zleap_parser import Parser, SearchMode, LLMConfig, EmbeddingConfig

zleap_parser.config(
    llm=LLMConfig(base_url="https://api.xxx.com/v1", api_key="sk-...", model="qwen3.6-flash"),
    embedding=EmbeddingConfig(model="Qwen/Qwen3-Embedding-0.6B", dimensions=1024),
)

parser = Parser()

# 本地文件走 magic bytes、格式适配器和文件内容缓存链路
result = parser.parse(file="/path/to/document.pdf")

# URL 只支持 HTML，走同步 Camoufox 正文采集链路
url_result = parser.parse(url="https://example.com/article")

# 搜索只返回标题、摘要和 URL，不采集结果页正文
search_results = parser.search(
    keyword="zleap_parser",
    limit=10,
    exclude_urls=[],
    mode=SearchMode.NORMAL,
)

# 两种入口都返回 {result: *.octx 归档文件路径, usage: LLM token 用量}
print(result["result"], result["usage"])     # usage: prompt_tokens / completion_tokens / total_tokens
print(url_result["result"], url_result["usage"])
print(search_results)
```

本地冒烟测试（读取根目录 `.env` 初始化配置）：

```bash
cp .env.example .env      # 填入 LLM / Embedding 配置
python src/main.py --list                 # 打印生效配置
python src/main.py tests/fixtures/sample.txt   # 解析本地文件
python src/main.py                        # 默认解析 tests/fixtures/ 下全部样例
```

## 支持的文件格式

本地文件按 **内容优先** 判定（magic bytes / 内容特征，不依赖扩展名），支持：

| 类别 | 格式 | 说明 |
| --- | --- | --- |
| 文本 | `txt` | 保持原文直出 |
| 源码 | `java`、`javascript`(js/cjs/mjs)、`typescript`(ts/tsx)、`python`(py/pyi/pyw，含 shebang)、`c`/`c++`(c/cc/cpp/cxx/h/hh/hpp/hxx)、`c#`、`go`、`rust`、`kotlin`(kt/kts)、`swift`、`dart`、`scala`、`ruby`、`php`、`shell`(sh/bash/zsh)、`sql`、`r`、`lua`、`css`、`json`、`yaml`(yaml/yml)、`toml`、`tft` | 包装为带语言标识的 Markdown 代码块 |
| 文档 | `pdf`、`docx`、`ppt`(按 OOXML 内容识别 PPTX，扩展名 `.ppt` 亦可)、`xlsx` | 用户自定义适配器 → Docling → markitdown 兜底链路 |
| 富文本 | `rtf` | 内容头识别 `{\rtf` → LibreOffice headless 转临时 PDF → 复用 PDF 兜底链路 |
| 网页 | `html` | 本地 HTML 文件 |
| 结构化 | `xml` | 原文导出 |
| 图片 | `png`、`jpg`、`gif`、`bmp`、`webp`、`svg`（`image/*` 通配） | 原始图片文件作为 OCTX 附件 |
| 音视频 | `audio`(m4a/mp3/wav 等)、`video` | FFmpeg 抽音频 → Docling Whisper ASR → 说话人分离 |

URL 输入仅支持 HTML 正文采集（`parse(url=)`），远程 PDF / 图片 / 普通文件不接受。

## 支持的连接器

| 连接器 | name | 数据源 |
| --- | --- | --- |
| `WebSearchAdapter` | `web_search` | Bing / Baidu / Google 搜索结果网页（固定来源域名白名单） |
| `DeepCrawlAdapter` | `deep_crawl` | 从起始 URL 复用单个 Camoufox 会话进行同源 BFS 深度抓取，每个有效页面产出一个 OCTX |
| `RSSAdapter` | `rss` | RSS / Atom 订阅 |
| `WxWorkChatAdapter` | `wxwork_chat` | 企业微信会话内容存档（C SDK 解密） |
| `FeishuBotChatAdapter` | `feishu_bot_chat` | 飞书机器人会话 |
| `SalesmartlyChatAdapter` | `salesmartly_chat` | Salesmartly 客服聊天 |
| `ApiRequestAdapter` | `api_request` | 通用 API 请求 |
| `GithubRepositoryAdapter` | `github_repository` | GitHub 仓库项目分析 |
| `GitlabRepositoryAdapter` | `gitlab_repository` | GitLab 仓库项目分析（支持私有化部署） |
| `GiteeRepositoryAdapter` | `gitee_repository` | Gitee 仓库项目分析 |
| `DbAdapter` | `db_connector` | 数据库查询（SQLite/MySQL/PostgreSQL 等，单次 SELECT → CSV → 临时经 `markdown_to_octx` / `pack_csv_to_octx` → `*.octx`） |
| `BigDbAdapter` | `bigdb_connector` | 大数据数据库查询（StarRocks/ClickHouse/Doris/Hologres 等 10 种，CSV → `*.octx`，支持流式/分批拉取） |

深度抓取通过连接器返回 OCTX 数组；`depth=0/1` 只抓入口页，`depth=2` 包含入口页和下一层链接：

```python
from zleap_parser.connector import Connector, DeepCrawlAdapter

result = Connector().fetch(
    adapter=DeepCrawlAdapter(
        url="https://example.com/",
        depth=2,
        max_pages=20,
    )
)
octx_paths = result["results"]  # [".../*.octx", ".../*.octx", ...]
```

数据库驱动按协议组**懒加载**：`DbAdapter` / `BigDbAdapter` 的 MySQL 组依赖 `pymysql`、PostgreSQL 组依赖 `psycopg2`、Oracle 组依赖 `oracledb`、SQL Server 组依赖 `pymssql`、ClickHouse 依赖 `clickhouse-connect`；虚谷 / GBase 8a / 达梦 / 崖山使用厂商官方驱动（可能需私有源）。SQLite 开箱即用（仅测试/冒烟）。完整清单与安装命令见 [`docs/sdk/install.md`](./docs/sdk/install.md) §2.1。

自定义数据源经 entry-points 分组 `zleap_parser.connector.sources` 接入，详见 [`connector_api.md`](./docs/sdk/connector_api.md)。

## 要点

- 产出物统一为 `*.octx` 归档文件：可读 markdown + 稳定身份、版本、完整性信息及可选的 chunks、events、entities、vectors。
- 文件类型以 **内容优先** 判定：PDF、图片、OOXML、HTML、XML 等二进制/结构化格式优先按 magic bytes 或内容特征识别；普通文本在确认没有 NUL/control bytes 后，源码类文件再按受控扩展名或 Python shebang 细分。
- **配置**：模块级 `zleap_parser.config(...)` 一次配置，`Parser`/`Connector` 自动继承；实例级 `*.config()` 与全局合并，便于高度自定义。搜索代理可通过 `http_proxy=` 或环境变量 `HTTP_PROXY` 配置，非空时优先使用代理。
- **网页搜索**：`parser.search(...)` 并发聚合 Bing、Baidu、Google，按 URL 去重、应用 `exclude_urls` 并根据标题/摘要关键词命中排序；只返回 `title`、`summary`、`url`，不采集搜索结果正文，也不依赖 LLM/Embedding。
- **缓存**：本地文件解析使用 Redis 或本地二级缓存；直接调用 `Parser.parse(url=...)` 仍每次重新采集；
  Connector 的 URL 文档使用 SQLite WAL + 磁盘 OCTX 产物缓存，同 URL 并发构建通过租约合并，缓存命中直接返回已有归档。
- **URL 采集**：只校验 HTTP(S) URL 格式，不执行 DNS/IP 类型判定；每次调用独立创建并关闭 Browser、Context、Page，不使用进程级信号量，多个线程可并发调用。正文提取、正文图片筛选和 Markdown 转换沿用参考 webcrawler 的规则语义，正文质量不足严格抛 `ConversionError`。
- **URL 附件**：成功渲染的 `page.html` 与成功下载的正文图片随 `markdown_to_octx(source_files=[...])` 以同一 `document_id` 写入 OCTX；远程 PDF/图片/普通文件不接受。
- **pdf 兜底链路**：用户自定义适配器 → Docling → markitdown（其他兜底可追加）。
- **rtf 链路**：内容头识别 `{\rtf` → LibreOffice headless 转为临时 PDF，复用 PDF 兜底链路；依赖系统命令 `soffice` / `libreoffice`（可用 `ZLEAP_LIBREOFFICE_BIN` 指定路径），原始 RTF、生成 PDF 与 PDF 提取附件一并写入 OCTX。
- **docx 兜底链路**：用户自定义适配器 → Docling → markitdown；DOCX 内嵌图片随原文件写入 OCTX 附件。
- **ppt 兜底链路**：用户自定义适配器 → Docling → markitdown；按 OOXML 内容识别 PPTX，即使扩展名为 `.ppt` 也可解析。
- **xlsx 兜底链路**：用户自定义适配器 → Docling → markitdown；按 OOXML 内容识别 XLSX，保留工作表表格，并提取内嵌位图、渲染常见柱状图/折线图/饼图作为 OCTX 附件。
- **db_connector 产物消费**：`csv_to_octx(csv_content)` / `xlsx_to_octx(xlsx_path)` 复用 `markdown_to_octx` 管线，把 CSV/XLSX 经 zleap-sag 提取打包为 `*.octx`（zleap_parser 只负责落盘与打包，解析/事项提取语义由 zleap-sag 决定；csv/xlsx 事项提取通道当前为 `# TODO`，待 zleap-sag 更新后实现）；`source_files` 与 `markdown_to_octx` 一致打包进归档，xlsx 输入文件自动作为附件保留；`pack_csv_to_octx` / `pack_xlsx_to_octx` 为免 sag 的知识-only 打包。
- **音视频 ASR 链路**：音频/视频先由 FFmpeg 统一抽取为 16 kHz、16-bit、单声道 WAV 并做 -23 LUFS 响度归一化，再由 Docling Whisper 转写并执行说话人分离；输出复用聊天数据源的会话 Markdown 结构，包含相对时间段与 `speaker_1`、`speaker_2` 等说话人标签。
- **源码文本链路**：txt 保持原文直出；java / javascript / python / tft 包装为带语言标识的 Markdown 代码块，避免源码被当作 Markdown 语法解析。

- **异常**：所有 API 失败均抛 `ZleapParserError` 及子类（`ConfigError` / `AdapterNotFoundError` / `ConversionError` / `DownloadError` / `TimeoutError` / `ExtractError`），不裸奔底层异常；URL 采集编排层保留已有 SDK 异常，并将其他内部异常包装为 `ConversionError`。

## 异步与线程模型兼容性

- **同步 API、零线程模型假设**：全部公开 API（`Parser` / `Connector` 各入口）为同步阻塞调用，不要求下游使用任何特定线程模型；SDK 不安装信号处理器、不修改全局事件循环策略、不在导入期启动线程。
- **asyncio 收敛于 SDK 私有后台线程**：上游 `zleap-sag` 的提取引擎为纯异步 API，SDK 将其收敛到**单一私有后台事件循环线程**（懒创建、守护线程、fork 后自动重建，见 `zleap_parser.utils.run_async`）：
  - 不在调用方线程创建/运行/关闭任何事件循环（不使用 `asyncio.run` / `set_event_loop`），调用方线程已有运行中的事件循环时亦可安全调用；
  - 调用方 `contextvars`（如用量采集 `capture_usage` 绑定）随协程传播，跨线程边界语义与同线程执行一致；
  - 多线程并发调用复用同一后台循环，无循环创建/销毁 churn。
- **gevent / eventlet / uvloop 等环境已验证**：`tests/test_async_compat.py` 以子进程方式在 `gevent.monkey.patch_all()`、`eventlet.monkey_patch()`、uvloop 事件循环策略下验证 import、`run_async`（含 greenlet/线程/已有运行中循环三种调用形态）、用量采集、缓存读写与连接器构造均自包含运行，且不干扰宿主自身调度。
- **已知边界**：URL 采集依赖 Camoufox（内部 Playwright 自带线程/事件循环），该链路不与 SDK 后台循环共享；若宿主 monkey-patch 与 Playwright 冲突，本地文件解析与 `*.octx` 打包链路不受影响。

## 边界

- 搜索 API 只负责搜索结果元数据聚合，不提供召回排序模型、结果正文采集或 Agent 协议。
- 核心解析框架与格式实现解耦：新增格式即新增适配器，不改核心代码。
- 上游参考实现：`sag`（markdown 结构化提取）与 `octx`（`*.octx` 打包）位于本地仓库 `../SAG` 与 `../open-context`。
- URL 正文规则直接复制 MinerU HTML webcrawler 的正文抽取器（本地命名为 `article_extractor.py`）；许可与归属见 [`THIRD_PARTY_NOTICES.md`](./THIRD_PARTY_NOTICES.md) 及 `LICENSES/`。
