sdpy-kit 0.4.4__tar.gz → 0.5.0__tar.gz

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.
Files changed (51) hide show
  1. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/PKG-INFO +58 -18
  2. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/README.md +52 -15
  3. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/__init__.py +1 -1
  4. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/config/kit_config.py +1 -1
  5. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/docs/__init__.py +2 -1
  6. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/docs/anydoc_kit.py +30 -22
  7. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/docs/docx_kit.py +22 -23
  8. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/docs/marker_kit.py +74 -51
  9. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/docs/md2doc_kit.py +19 -14
  10. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/docs/mineru_kit.py +21 -29
  11. sdpy_kit-0.5.0/kit/docs/rc_docparser_kit.py +546 -0
  12. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/pyproject.toml +7 -3
  13. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/.gitignore +0 -0
  14. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/__init__.py +0 -0
  15. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/base_service.py +0 -0
  16. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/chat_service.py +0 -0
  17. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/embedding_service.py +0 -0
  18. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/exceptions.py +0 -0
  19. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/failover.py +0 -0
  20. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/image_service.py +0 -0
  21. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/model_builder.py +0 -0
  22. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/model_client.py +0 -0
  23. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/pool.py +0 -0
  24. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/profiles.py +0 -0
  25. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/rerank_service.py +0 -0
  26. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/runner.py +0 -0
  27. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/ai/types.py +0 -0
  28. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/common_kit.py +0 -0
  29. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/config/__init__.py +0 -0
  30. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/config/log_config.py +0 -0
  31. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/crawl/__init__.py +0 -0
  32. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/crawl/article_models.py +0 -0
  33. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/crawl/web_article_kit.py +0 -0
  34. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/crawl/wx_article_kit.py +0 -0
  35. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/__init__.py +0 -0
  36. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/base_db_kit.py +0 -0
  37. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/crud.py +0 -0
  38. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/engine.py +0 -0
  39. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/exceptions.py +0 -0
  40. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/models/__init__.py +0 -0
  41. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/models/base.py +0 -0
  42. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/mysql_kit.py +0 -0
  43. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/pg_kit.py +0 -0
  44. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/query.py +0 -0
  45. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/session.py +0 -0
  46. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/sqlite_kit.py +0 -0
  47. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/db/transaction.py +0 -0
  48. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/file_kit.py +0 -0
  49. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/laya_kit.py +0 -0
  50. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/license_kit.py +0 -0
  51. {sdpy_kit-0.4.4 → sdpy_kit-0.5.0}/kit/redis_kit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sdpy-kit
3
- Version: 0.4.4
3
+ Version: 0.5.0
4
4
  Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
5
5
  Author-email: Clark <changhongyuan@126.com>
6
6
  License-Expression: MIT
@@ -32,13 +32,14 @@ Requires-Dist: httpx>=0.28.1; extra == 'all'
32
32
  Requires-Dist: huggingface-hub<2,>=1.5; extra == 'all'
33
33
  Requires-Dist: laya>=0.4.6; extra == 'all'
34
34
  Requires-Dist: markdown-it-py>=4.2.0; extra == 'all'
35
- Requires-Dist: marker-pdf[full]>=2.0.0; extra == 'all'
35
+ Requires-Dist: marker-pdf>=2.0.0; extra == 'all'
36
36
  Requires-Dist: numpy>=2.5.4; extra == 'all'
37
37
  Requires-Dist: pikepdf>=10.17.0; extra == 'all'
38
38
  Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'all'
39
39
  Requires-Dist: psycopg2-binary>=2.9.13; extra == 'all'
40
40
  Requires-Dist: pymysql>=1.2.3; extra == 'all'
41
41
  Requires-Dist: python-dotenv>=1.2.4; extra == 'all'
42
+ Requires-Dist: rc-docparser>=0.2.2; extra == 'all'
42
43
  Requires-Dist: redis>=8.1.0; extra == 'all'
43
44
  Requires-Dist: requests>=2.34.2; extra == 'all'
44
45
  Requires-Dist: sqliteai-vector>=1.1.2; extra == 'all'
@@ -48,11 +49,13 @@ Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'anydoc'
48
49
  Requires-Dist: pikepdf>=10.17.0; extra == 'anydoc'
49
50
  Provides-Extra: docx
50
51
  Requires-Dist: docxtpl>=0.20.2; extra == 'docx'
52
+ Provides-Extra: docx-parse
53
+ Requires-Dist: rc-docparser>=0.2.2; extra == 'docx-parse'
51
54
  Provides-Extra: laya
52
55
  Requires-Dist: huggingface-hub<2,>=1.5; extra == 'laya'
53
56
  Requires-Dist: laya>=0.4.6; extra == 'laya'
54
57
  Provides-Extra: marker
55
- Requires-Dist: marker-pdf[full]>=2.0.0; extra == 'marker'
58
+ Requires-Dist: marker-pdf>=2.0.0; extra == 'marker'
56
59
  Provides-Extra: md2doc
57
60
  Requires-Dist: markdown-it-py>=4.2.0; extra == 'md2doc'
58
61
  Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'md2doc'
@@ -77,7 +80,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
77
80
 
78
81
  - **统一数据库访问层**:PostgreSQL / MySQL / SQLite,三个 Kit API 完全一致,切换数据库只改一行 import;
79
82
  - **AI 多模型服务池**:chat / embedding / rerank / image 四类能力,多厂商候选链 + 故障自动转移;
80
- - **工具集**:Redis / 文件 / 雪花 ID / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(md + images) / Laya 类型化决策 / 微信公众号文章抓取存储 / 通用网页文章抓取存储。
83
+ - **工具集**:Redis / 文件 / 雪花 ID / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(PDF + 图片 → md + images/) / 离线 docx 解析(md + images/) / Laya 类型化决策 / 微信公众号文章抓取存储 / 通用网页文章抓取存储。
81
84
 
82
85
  > 要求 Python 3.13+
83
86
 
@@ -93,9 +96,10 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
93
96
  | `kit.common_kit` | `CommonKit` | 雪花 ID、时间戳 ID、时间格式化 | 无 |
94
97
  | `kit.docs.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
95
98
  | `kit.docs.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
99
+ | `kit.docs.rc_docparser_kit` | `RcDocParserKit` | 离线 docx → Markdown + Images(无模型) | `docx-parse` |
96
100
  | `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
97
101
  | `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
98
- | `kit.docs.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
102
+ | `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images) | `marker` |
99
103
  | `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
100
104
  | `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(两 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
101
105
  | `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
@@ -121,8 +125,9 @@ uv add sdpy-kit
121
125
  | `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2) | `pip install "sdpy-kit[md2doc]"` |
122
126
  | `anydoc` | AnyDocKit 文档转 Markdown(firecrawl-anydoc + pikepdf) | `pip install "sdpy-kit[anydoc]"` |
123
127
  | `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
128
+ | `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
124
129
  | `mineru` | MinerUKit 解析 API(精准解析 + Agent 轻量解析) | `pip install "sdpy-kit[mineru]"` |
125
- | `marker` | MarkerKit 本地文档解析(marker-pdf[full] → Markdown + Images,依赖体积大) | `pip install "sdpy-kit[marker]"` |
130
+ | `marker` | MarkerKit 本地解析(marker-pdf → Markdown + Images;PDF + 图片,依赖体积大) | `pip install "sdpy-kit[marker]"` |
126
131
  | `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / score / noul) | `pip install "sdpy-kit[laya]"` |
127
132
  | `web-article` | WebArticleKit 通用网页文章抓取转存(trafilatura,纯 HTTP 无浏览器) | `pip install "sdpy-kit[web-article]"` |
128
133
  | `all` | 全部能力 | `pip install "sdpy-kit[all]"` |
@@ -871,10 +876,10 @@ md = AnyDocKit.convert_to_md("scan.pdf", ocr="hosted") # 需 FIRECRAWL_API_KEY
871
876
  | `detect_format(file_path) -> str \| None` | 由扩展名推断规范格式名 |
872
877
  | `convert_to_md(file_path, *, ocr="reject", api_key=None, api_url=None) -> str` | 文件 → Markdown(非标 PDF 自动修复重试) |
873
878
  | `convert_bytes_to_md(data, fmt=None, *, ocr="reject", api_key=None, api_url=None) -> str` | 字节内容 → Markdown |
874
- | `convert(file_path, *, ocr="reject", api_key=None, api_url=None) -> ConvertResult` | 安全转换 |
879
+ | `convert(file_path, *, ocr="reject", api_key=None, api_url=None) -> AnyDocResult` | 安全转换 |
875
880
  | `convert_to_file(src_path, dest_path=None, *, ocr="reject", ...) -> str` | 转换并写入 `.md`,返回目标路径 |
876
881
 
877
- `ConvertResult(success, markdown, file_path, source_format, error)`
882
+ `AnyDocResult(success, markdown, file_path, source_format, error)`
878
883
 
879
884
  ---
880
885
 
@@ -971,15 +976,18 @@ if r.state == "done":
971
976
 
972
977
  ---
973
978
 
974
- ## kit.docs.marker_kit —— 本地文档解析(Markdown + Images)
979
+ ## kit.docs.marker_kit —— 本地解析:PDF + 图片 → Markdown + Images
975
980
 
976
- 安装:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf[full]`,含 torch 等 80+ 包,体积较大)
981
+ 安装:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf`,含 torch 等 80+ 包,体积较大;不含 `[full]`)
982
+
983
+ 支持面:`.pdf`、`.png`、`.jpg`、`.jpeg`、`.webp`、`.gif`、`.bmp`。docx/pptx/xlsx/html/epub 不在支持面
984
+ (marker 侧实现依赖 weasyprint/GTK),传入会抛 `ValueError`。
977
985
 
978
986
  ```python
979
987
  from kit.docs.marker_kit import MarkerKit
980
988
 
981
- # 落盘 md + images(推荐):源文件同目录下建同名子目录
982
- md_path = MarkerKit.convert_to_dir("report.pdf") # -> report/report.md + 图片
989
+ # 落盘 md + images/(推荐):源文件同目录下建同名子目录
990
+ md_path = MarkerKit.convert_to_dir("report.pdf") # -> report/report.md + report/images/
983
991
  md_path = MarkerKit.convert_to_dir("report.pdf", "out/doc") # 指定产物目录
984
992
 
985
993
  # 只取 Markdown 文本(图片不落盘)
@@ -990,24 +998,56 @@ result = MarkerKit.convert("report.pdf", "out/doc")
990
998
  if result.success:
991
999
  print(result.md_path, len(result.image_paths), result.page_count)
992
1000
 
993
- # 跳过 VLM(无需 llama.cpp)
994
- md = MarkerKit.convert_to_md("scan.pdf", disable_ocr=True)
1001
+ # 文本层 PDF 离线抽取(跳过 VLM/OCR,无需 llama-server)
1002
+ md = MarkerKit.convert_to_md("report.pdf", disable_ocr=True)
1003
+
1004
+ # 图片输入(无文本层,必须 OCR:仅 disable_ocr=True 时无文字内容)
1005
+ md = MarkerKit.convert_to_md("figure.png", disable_ocr=True)
995
1006
 
996
1007
  MarkerKit.shutdown() # 释放模型与推理服务(幂等)
997
1008
  ```
998
1009
 
999
1010
  | 方法 | 说明 |
1000
1011
  |------|------|
1001
- | `is_supported(file_path) -> bool` | 扩展名白名单(pdf / 图片 / docx / pptx / xlsx / html / epub) |
1012
+ | `is_supported(file_path) -> bool` | 白名单:`.pdf` + `.png/.jpg/.jpeg/.webp/.gif/.bmp` |
1002
1013
  | `convert_to_md(file_path, *, mode=None, force_ocr=None, disable_ocr=None, page_range=None) -> str` | 解析并返回 Markdown 文本(图片不落盘) |
1003
- | `convert_to_dir(src_path, output_dir=None, *, mode=None, force_ocr=None, disable_ocr=None, page_range=None, extract_images=True) -> str` | 解析并落盘 md + images,返回 md 绝对路径 |
1014
+ | `convert_to_dir(src_path, output_dir=None, *, mode=None, force_ocr=None, disable_ocr=None, page_range=None, extract_images=True) -> str` | 解析并落盘 md + `images/`,返回 md 绝对路径 |
1004
1015
  | `convert(src_path, output_dir=None, *, ...) -> MarkerResult` | 安全版:失败封装为 `MarkerResult`,不抛异常 |
1005
1016
  | `shutdown() -> None` | 释放模型与共享推理服务(幂等) |
1006
1017
 
1007
- > 产物:`<output_dir>/<源文件名>.md` + 图片文件(md 内相对路径引用),**不生成** `_meta.json`。
1018
+ > 产物:`<output_dir>/<源文件名>.md` + `<output_dir>/images/`(md 内以 `images/<文件名>` 相对引用;无图片不建空目录),**不生成** `_meta.json`。
1008
1019
  > 结果类型:`MarkerResult`(`success` / `markdown` / `file_path` / `output_dir` / `md_path` / `image_paths` / `page_count` / `error`);异常:`MarkerError`。
1009
1020
  > `mode` 缺省时不写入配置,交由 marker 按设备自动选择;首次使用会下载模型权重(默认走 `MARKER_HF_ENDPOINT` 镜像)。
1010
- > 非 NVIDIA 平台完整 VLM 能力(公式 / 扫描页 OCR)需 llama.cpp 的 `llama-server`,否则用 `disable_ocr=True` 跳过。
1021
+ > **前提条件**:文本层 PDF 传 `disable_ocr=True` 即可完全离线(无需 llama-server);不传时 marker 默认启用 VLM/OCR,
1022
+ > 缺 `llama-server` 抛 `SpawnError`。扫描件 / 图片型 PDF / 图片输入要得到文字,必须有 llama.cpp 的 `llama-server`
1023
+ > (`MARKER_LLAMA_CPP_BINARY`),否则只得到空 md 或一条整图引用。PDF 内嵌图片抽取不需要 `llama-server`。
1024
+
1025
+ ## kit.docs.rc_docparser_kit —— 离线 docx → Markdown + Images
1026
+
1027
+ 安装:`pip install "sdpy-kit[docx-parse]"`(依赖 `rc-docparser`,纯 Python,无模型 / OCR / GTK)
1028
+
1029
+ 支持面:仅 `.docx`。产物与 `MarkerKit` 同一契约:`<out>/<stem>.md` + `<out>/images/`。
1030
+
1031
+ ```python
1032
+ from kit.docs.rc_docparser_kit import RcDocParserKit
1033
+
1034
+ md_path = RcDocParserKit.convert_to_dir("report.docx") # -> report/report.md + report/images/
1035
+ md = RcDocParserKit.convert_to_md("report.docx") # 只取文本(图片不落盘)
1036
+ result = RcDocParserKit.convert("report.docx", "out/doc")
1037
+ if result.success:
1038
+ print(result.md_path, len(result.image_paths))
1039
+ ```
1040
+
1041
+ | 方法 | 说明 |
1042
+ |------|------|
1043
+ | `is_supported(file_path) -> bool` | 是否 `.docx` |
1044
+ | `convert_to_md(file_path) -> str` | 解析并返回 Markdown 文本 |
1045
+ | `convert_to_dir(src_path, output_dir=None, *, extract_images=True) -> str` | 落盘 md + `images/`,返回 md 绝对路径 |
1046
+ | `convert(src_path, output_dir=None, *, ...) -> RcDocParserResult` | 安全版:失败封装为结果对象 |
1047
+
1048
+ > 产物:`<out>/images/img-NNN-<sha1 前 10>.<ext>`(按出现顺序、内容哈希命名);无图不建空目录。
1049
+ > 前提:中文标题依赖 `w:outlineLvl` 或「标题 N」样式(都没有时退化为正文);合并单元格的复杂表格
1050
+ > 会退化为 GFM 表格;页眉页脚/脚注不提取。纯本地,无需模型或 GPU。
1011
1051
 
1012
1052
  ## kit.crawl.wx_article_kit —— 微信公众号文章抓取存储
1013
1053
 
@@ -4,7 +4,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
4
4
 
5
5
  - **统一数据库访问层**:PostgreSQL / MySQL / SQLite,三个 Kit API 完全一致,切换数据库只改一行 import;
6
6
  - **AI 多模型服务池**:chat / embedding / rerank / image 四类能力,多厂商候选链 + 故障自动转移;
7
- - **工具集**:Redis / 文件 / 雪花 ID / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(md + images) / Laya 类型化决策 / 微信公众号文章抓取存储 / 通用网页文章抓取存储。
7
+ - **工具集**:Redis / 文件 / 雪花 ID / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(PDF + 图片 → md + images/) / 离线 docx 解析(md + images/) / Laya 类型化决策 / 微信公众号文章抓取存储 / 通用网页文章抓取存储。
8
8
 
9
9
  > 要求 Python 3.13+
10
10
 
@@ -20,9 +20,10 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
20
20
  | `kit.common_kit` | `CommonKit` | 雪花 ID、时间戳 ID、时间格式化 | 无 |
21
21
  | `kit.docs.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
22
22
  | `kit.docs.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
23
+ | `kit.docs.rc_docparser_kit` | `RcDocParserKit` | 离线 docx → Markdown + Images(无模型) | `docx-parse` |
23
24
  | `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
24
25
  | `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
25
- | `kit.docs.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
26
+ | `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images) | `marker` |
26
27
  | `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
27
28
  | `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(两 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
28
29
  | `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
@@ -48,8 +49,9 @@ uv add sdpy-kit
48
49
  | `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2) | `pip install "sdpy-kit[md2doc]"` |
49
50
  | `anydoc` | AnyDocKit 文档转 Markdown(firecrawl-anydoc + pikepdf) | `pip install "sdpy-kit[anydoc]"` |
50
51
  | `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
52
+ | `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
51
53
  | `mineru` | MinerUKit 解析 API(精准解析 + Agent 轻量解析) | `pip install "sdpy-kit[mineru]"` |
52
- | `marker` | MarkerKit 本地文档解析(marker-pdf[full] → Markdown + Images,依赖体积大) | `pip install "sdpy-kit[marker]"` |
54
+ | `marker` | MarkerKit 本地解析(marker-pdf → Markdown + Images;PDF + 图片,依赖体积大) | `pip install "sdpy-kit[marker]"` |
53
55
  | `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / score / noul) | `pip install "sdpy-kit[laya]"` |
54
56
  | `web-article` | WebArticleKit 通用网页文章抓取转存(trafilatura,纯 HTTP 无浏览器) | `pip install "sdpy-kit[web-article]"` |
55
57
  | `all` | 全部能力 | `pip install "sdpy-kit[all]"` |
@@ -798,10 +800,10 @@ md = AnyDocKit.convert_to_md("scan.pdf", ocr="hosted") # 需 FIRECRAWL_API_KEY
798
800
  | `detect_format(file_path) -> str \| None` | 由扩展名推断规范格式名 |
799
801
  | `convert_to_md(file_path, *, ocr="reject", api_key=None, api_url=None) -> str` | 文件 → Markdown(非标 PDF 自动修复重试) |
800
802
  | `convert_bytes_to_md(data, fmt=None, *, ocr="reject", api_key=None, api_url=None) -> str` | 字节内容 → Markdown |
801
- | `convert(file_path, *, ocr="reject", api_key=None, api_url=None) -> ConvertResult` | 安全转换 |
803
+ | `convert(file_path, *, ocr="reject", api_key=None, api_url=None) -> AnyDocResult` | 安全转换 |
802
804
  | `convert_to_file(src_path, dest_path=None, *, ocr="reject", ...) -> str` | 转换并写入 `.md`,返回目标路径 |
803
805
 
804
- `ConvertResult(success, markdown, file_path, source_format, error)`
806
+ `AnyDocResult(success, markdown, file_path, source_format, error)`
805
807
 
806
808
  ---
807
809
 
@@ -898,15 +900,18 @@ if r.state == "done":
898
900
 
899
901
  ---
900
902
 
901
- ## kit.docs.marker_kit —— 本地文档解析(Markdown + Images)
903
+ ## kit.docs.marker_kit —— 本地解析:PDF + 图片 → Markdown + Images
902
904
 
903
- 安装:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf[full]`,含 torch 等 80+ 包,体积较大)
905
+ 安装:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf`,含 torch 等 80+ 包,体积较大;不含 `[full]`)
906
+
907
+ 支持面:`.pdf`、`.png`、`.jpg`、`.jpeg`、`.webp`、`.gif`、`.bmp`。docx/pptx/xlsx/html/epub 不在支持面
908
+ (marker 侧实现依赖 weasyprint/GTK),传入会抛 `ValueError`。
904
909
 
905
910
  ```python
906
911
  from kit.docs.marker_kit import MarkerKit
907
912
 
908
- # 落盘 md + images(推荐):源文件同目录下建同名子目录
909
- md_path = MarkerKit.convert_to_dir("report.pdf") # -> report/report.md + 图片
913
+ # 落盘 md + images/(推荐):源文件同目录下建同名子目录
914
+ md_path = MarkerKit.convert_to_dir("report.pdf") # -> report/report.md + report/images/
910
915
  md_path = MarkerKit.convert_to_dir("report.pdf", "out/doc") # 指定产物目录
911
916
 
912
917
  # 只取 Markdown 文本(图片不落盘)
@@ -917,24 +922,56 @@ result = MarkerKit.convert("report.pdf", "out/doc")
917
922
  if result.success:
918
923
  print(result.md_path, len(result.image_paths), result.page_count)
919
924
 
920
- # 跳过 VLM(无需 llama.cpp)
921
- md = MarkerKit.convert_to_md("scan.pdf", disable_ocr=True)
925
+ # 文本层 PDF 离线抽取(跳过 VLM/OCR,无需 llama-server)
926
+ md = MarkerKit.convert_to_md("report.pdf", disable_ocr=True)
927
+
928
+ # 图片输入(无文本层,必须 OCR:仅 disable_ocr=True 时无文字内容)
929
+ md = MarkerKit.convert_to_md("figure.png", disable_ocr=True)
922
930
 
923
931
  MarkerKit.shutdown() # 释放模型与推理服务(幂等)
924
932
  ```
925
933
 
926
934
  | 方法 | 说明 |
927
935
  |------|------|
928
- | `is_supported(file_path) -> bool` | 扩展名白名单(pdf / 图片 / docx / pptx / xlsx / html / epub) |
936
+ | `is_supported(file_path) -> bool` | 白名单:`.pdf` + `.png/.jpg/.jpeg/.webp/.gif/.bmp` |
929
937
  | `convert_to_md(file_path, *, mode=None, force_ocr=None, disable_ocr=None, page_range=None) -> str` | 解析并返回 Markdown 文本(图片不落盘) |
930
- | `convert_to_dir(src_path, output_dir=None, *, mode=None, force_ocr=None, disable_ocr=None, page_range=None, extract_images=True) -> str` | 解析并落盘 md + images,返回 md 绝对路径 |
938
+ | `convert_to_dir(src_path, output_dir=None, *, mode=None, force_ocr=None, disable_ocr=None, page_range=None, extract_images=True) -> str` | 解析并落盘 md + `images/`,返回 md 绝对路径 |
931
939
  | `convert(src_path, output_dir=None, *, ...) -> MarkerResult` | 安全版:失败封装为 `MarkerResult`,不抛异常 |
932
940
  | `shutdown() -> None` | 释放模型与共享推理服务(幂等) |
933
941
 
934
- > 产物:`<output_dir>/<源文件名>.md` + 图片文件(md 内相对路径引用),**不生成** `_meta.json`。
942
+ > 产物:`<output_dir>/<源文件名>.md` + `<output_dir>/images/`(md 内以 `images/<文件名>` 相对引用;无图片不建空目录),**不生成** `_meta.json`。
935
943
  > 结果类型:`MarkerResult`(`success` / `markdown` / `file_path` / `output_dir` / `md_path` / `image_paths` / `page_count` / `error`);异常:`MarkerError`。
936
944
  > `mode` 缺省时不写入配置,交由 marker 按设备自动选择;首次使用会下载模型权重(默认走 `MARKER_HF_ENDPOINT` 镜像)。
937
- > 非 NVIDIA 平台完整 VLM 能力(公式 / 扫描页 OCR)需 llama.cpp 的 `llama-server`,否则用 `disable_ocr=True` 跳过。
945
+ > **前提条件**:文本层 PDF 传 `disable_ocr=True` 即可完全离线(无需 llama-server);不传时 marker 默认启用 VLM/OCR,
946
+ > 缺 `llama-server` 抛 `SpawnError`。扫描件 / 图片型 PDF / 图片输入要得到文字,必须有 llama.cpp 的 `llama-server`
947
+ > (`MARKER_LLAMA_CPP_BINARY`),否则只得到空 md 或一条整图引用。PDF 内嵌图片抽取不需要 `llama-server`。
948
+
949
+ ## kit.docs.rc_docparser_kit —— 离线 docx → Markdown + Images
950
+
951
+ 安装:`pip install "sdpy-kit[docx-parse]"`(依赖 `rc-docparser`,纯 Python,无模型 / OCR / GTK)
952
+
953
+ 支持面:仅 `.docx`。产物与 `MarkerKit` 同一契约:`<out>/<stem>.md` + `<out>/images/`。
954
+
955
+ ```python
956
+ from kit.docs.rc_docparser_kit import RcDocParserKit
957
+
958
+ md_path = RcDocParserKit.convert_to_dir("report.docx") # -> report/report.md + report/images/
959
+ md = RcDocParserKit.convert_to_md("report.docx") # 只取文本(图片不落盘)
960
+ result = RcDocParserKit.convert("report.docx", "out/doc")
961
+ if result.success:
962
+ print(result.md_path, len(result.image_paths))
963
+ ```
964
+
965
+ | 方法 | 说明 |
966
+ |------|------|
967
+ | `is_supported(file_path) -> bool` | 是否 `.docx` |
968
+ | `convert_to_md(file_path) -> str` | 解析并返回 Markdown 文本 |
969
+ | `convert_to_dir(src_path, output_dir=None, *, extract_images=True) -> str` | 落盘 md + `images/`,返回 md 绝对路径 |
970
+ | `convert(src_path, output_dir=None, *, ...) -> RcDocParserResult` | 安全版:失败封装为结果对象 |
971
+
972
+ > 产物:`<out>/images/img-NNN-<sha1 前 10>.<ext>`(按出现顺序、内容哈希命名);无图不建空目录。
973
+ > 前提:中文标题依赖 `w:outlineLvl` 或「标题 N」样式(都没有时退化为正文);合并单元格的复杂表格
974
+ > 会退化为 GFM 表格;页眉页脚/脚注不提取。纯本地,无需模型或 GPU。
938
975
 
939
976
  ## kit.crawl.wx_article_kit —— 微信公众号文章抓取存储
940
977
 
@@ -23,7 +23,7 @@ Author: Clark Chang
23
23
  数据库访问通过 kit.db 子包提供(基于 SQLModel)。
24
24
  """
25
25
 
26
- __version__ = "0.4.4"
26
+ __version__ = "0.5.0"
27
27
 
28
28
  from kit.db import (
29
29
  MYSQL,
@@ -89,7 +89,7 @@ class Settings(BaseSettings):
89
89
  default=600, ge=10, description="任务轮询总超时(秒),超时抛 MinerUError"
90
90
  )
91
91
 
92
- # Marker 本地文档解析配置(marker-pdf,PDF→Markdown+Images,模型本地运行)
92
+ # Marker 本地解析配置(marker-pdf,PDF/图片→Markdown+Images,模型本地运行)
93
93
  MARKER_MODE: str | None = Field(
94
94
  default=None,
95
95
  description=(
@@ -25,7 +25,8 @@ Author: Clark Chang
25
25
  - md2doc_kit: Md2DocKit Markdown 转 Word/PDF/HTML(pimd,extra: md2doc)
26
26
  - docx_kit: DocxKit Word 模板动态填充(docxtpl,extra: docx)
27
27
  - mineru_kit: MinerUKit MinerU 文档解析 API(requests,extra: mineru)
28
- - marker_kit: MarkerKit Marker 本地文档解析(marker-pdf,extra: marker)
28
+ - marker_kit: MarkerKit PDF + 图片本地解析(marker-pdf,extra: marker)
29
+ - rc_docparser_kit: RcDocParserKit 离线 docx 解析(rc-docparser,extra: docx-parse)
29
30
 
30
31
  本 __init__ 不导入任何成员:五个模块均携带重型可选依赖,保持
31
32
  `import kit.docs` 在基础安装下零成本可用(对齐 kit.crawl 先例)。
@@ -18,21 +18,27 @@ Author: Clark Chang
18
18
  """
19
19
 
20
20
  # date: 2026-09-23
21
- """文档解析工具类,基于 anydoc 将各类文档统一转为 Markdown
22
-
23
- 本模块封装 firecrawl-anydoc(pip 安装名 firecrawl-anydoc,import 名 anydoc),
24
- 将 Word、PowerPoint、Excel、CSV、PDF 格式统一转为 GitHub-Flavored Markdown;
25
- Markdown → Word/HTML/PDF 的反向转换见 kit.docs.md2doc_kit(Md2DocKit)。
26
-
27
- 设计要点:
28
- - 全部使用静态方法提供服务,无内部状态。
29
- - 低层 convert_to_md 直接抛出异常(与 FileKit 风格一致,便于精确定位)。
30
- - PDF 兼容性:结构非标但内容完好的 PDF(知网 WebFastLoad 尾部垃圾等)遇
31
- MalformedError 时自动 pikepdf 重写修复并重试一次;扫描件可传 ocr="hosted"
32
- 走云端 OCR(需 FIRECRAWL_API_KEY),默认 reject 行为与 anydoc 一致。
33
- - 高层 convert 返回 ConvertResult 结果对象,封装成功/失败状态,便于批量处理。
34
- - 路径参数统一支持 str 与 pathlib.Path;需访问文件的方法(convert_to_md /
35
- convert_to_file)内部 resolve 为绝对路径,纯扩展名判断方法不做 resolve。
21
+ """AnyDocKit —— 文档转 Markdown 工具类(基于 firecrawl-anydoc)
22
+
23
+ 支持面:`.doc` / `.docx` / `.ppt` / `.pptx` / `.xls` / `.xlsx` / `.csv` / `.pdf`
24
+ (白名单为 anydoc 支持面的常用子集)。
25
+
26
+ 实现内容:
27
+ - 全部静态方法、无内部状态;低层 `convert_to_md` 直接抛异常,高层 `convert`
28
+ 返回 `AnyDocResult` 封装成功/失败,便于批量处理。
29
+ - PDF 结构非标(如知网件 `%%EOF` 后附加元数据)遇 `MalformedError` 时,用
30
+ pikepdf 重写为临时文件后重试一次;扫描页默认 reject,`ocr="hosted"` 走云端 OCR。
31
+ - 路径参数支持 `str` / `Path`;访问文件的方法内部 resolve 为绝对路径,
32
+ 纯扩展名判断方法不做 resolve。
33
+
34
+ 前提条件:
35
+ - 只产出 Markdown 文本,**不产出图片**(需要 `md + images/` 时用 `MarkerKit`
36
+ 或 `RcDocParserKit`)。
37
+ - 云端 OCR 需 `FIRECRAWL_API_KEY`(或显式传 `api_key`)。
38
+
39
+ 依赖:
40
+ - `firecrawl-anydoc`(extra `anydoc`,导入名 `anydoc`)+ `pikepdf`;
41
+ 未安装时导入报错并提示安装命令。
36
42
  """
37
43
 
38
44
  import io
@@ -44,7 +50,9 @@ try:
44
50
  import anydoc
45
51
  import pikepdf
46
52
  except ImportError as e:
47
- raise ImportError("anydoc_kit 需要可选依赖,请安装:pip install sdpy-kit[anydoc]") from e
53
+ raise ImportError(
54
+ "anydoc_kit 需要可选依赖,请安装:pip install sdpy-kit[anydoc]"
55
+ ) from e
48
56
 
49
57
  from kit.config.log_config import get_default_logger
50
58
 
@@ -139,7 +147,7 @@ def _convert_with_repair(
139
147
 
140
148
 
141
149
  @dataclass(frozen=True, slots=True)
142
- class ConvertResult:
150
+ class AnyDocResult:
143
151
  """文档转换结果对象
144
152
 
145
153
  由 AnyDocKit.convert 返回,将成功与失败信息统一封装,调用方无需 try/except。
@@ -374,11 +382,11 @@ class AnyDocKit:
374
382
  ocr: Literal["reject", "hosted"] = "reject",
375
383
  api_key: str | None = None,
376
384
  api_url: str | None = None,
377
- ) -> ConvertResult:
385
+ ) -> AnyDocResult:
378
386
  """安全转换文档为 Markdown,返回包含状态的结果对象
379
387
 
380
388
  与 convert_to_md 不同,本方法不抛出转换异常,而是将成功/失败信息
381
- 封装到 ConvertResult 中,便于批量处理或状态检查。
389
+ 封装到 AnyDocResult 中,便于批量处理或状态检查。
382
390
  ocr/api_key/api_url 语义同 convert_to_md(PDF 自动修复对调用方
383
391
  透明,失败信息封装在 result.error 中)。
384
392
  边界:文件不存在 / 格式不支持 / 转换失败均封装为结果;若 file_path
@@ -391,7 +399,7 @@ class AnyDocKit:
391
399
  api_url: 云端 OCR 服务地址;None 时回落 FIRECRAWL_API_URL
392
400
 
393
401
  Returns:
394
- ConvertResult: 转换结果,包含 success/markdown/file_path/source_format/error
402
+ AnyDocResult: 转换结果,包含 success/markdown/file_path/source_format/error
395
403
 
396
404
  Example:
397
405
  >>> result = AnyDocKit.convert("report.pdf")
@@ -410,7 +418,7 @@ class AnyDocKit:
410
418
  except (ValueError, anydoc.ConvertError, OSError) as e:
411
419
  # OSError 同时覆盖 FileNotFoundError(文件不存在)与文件读取失败
412
420
  logger.warning(f"文档转换失败: {path.name}({type(e).__name__}): {e}")
413
- return ConvertResult(
421
+ return AnyDocResult(
414
422
  success=False,
415
423
  markdown="",
416
424
  file_path=str(path),
@@ -418,7 +426,7 @@ class AnyDocKit:
418
426
  error=f"{type(e).__name__}: {e}",
419
427
  )
420
428
 
421
- return ConvertResult(
429
+ return AnyDocResult(
422
430
  success=True,
423
431
  markdown=markdown,
424
432
  file_path=str(path),
@@ -18,29 +18,28 @@ Author: Clark Chang
18
18
  """
19
19
 
20
20
  # date: 2026-09-14
21
- """docx 模板动态填充工具类,基于 docxtpl 实现章节与内容全动态的模板渲染
22
-
23
- 本模块封装 docxtpl,提供「Word 模板 + 结构化数据 → 成品 docx」的模板填充
24
- 服务:章节数量、层级、标题、正文段落、多级列表、表格全部由运行时数据决定,
25
- 模板只承载版式骨架(封面、页眉页脚、字体、编号绑定)。
26
-
27
- 与 Md2DocKit 的边界:Md2DocKit 负责格式转换(含 Markdown → Word 整篇渲染),本模块
28
- 负责模板填充,二者互不调用。
29
-
30
- 设计要点:
31
- - 混合方案:章节循环由模板中的 ``{%p for %}`` 承载,非文本块(列表/表格)
32
- 以私有使用区字符组成的 marker 段落承载「位置 + 载荷」,渲染后由 python-docx
33
- 后处理原位转换,规避 docxtpl 无法表达动态层级与富内容的限制。
34
- - 章节标题段使用锚点样式(默认 ``DocxSectionTitle``),渲染后按 ``level``
35
- 重设为 ``Heading N``,使模板自身绑定的 Word 多级列表自动编号生效。
36
- - 模板渲染开启 ``autoescape``,正文文本中的 ``&`` / ``<`` / ``>`` 不会破坏 XML。
37
- - 变量值中的控制字符语义沿用 docxtpl 约定:``\\n`` 段内软换行、``\\a`` 新段落。
38
- - 全静态方法、无模块级可变状态(除常量),线程安全。
39
- - 路径参数统一支持 str 与 pathlib.Path。
40
- - 有序列表按「连续组」克隆独立编号定义,避免 Word 把多个列表串成一条编号序列。
41
- - 模板样式统一::meth:`DocxKit.unify_font` 统一字体、:meth:`DocxKit.unify_heading_color`
42
- 统一标题颜色,产物继承模板样式;主标题段由 :meth:`DocxKit.create_template` 的
43
- ``title`` 参数生成(居中、加粗)。
21
+ """DocxKit —— docx 模板动态填充工具类(基于 docxtpl)
22
+
23
+ 支持面:Word 模板(`.docx`)+ 结构化数据 → 成品 docx;章节数量、层级、标题、
24
+ 正文段落、多级列表、表格均由运行时数据决定,模板只承载版式骨架。
25
+
26
+ 实现内容:
27
+ - 章节循环由模板中的 ``{%p for %}`` 承载;列表/表格以私有使用区字符组成的 marker
28
+ 段落承载「位置 + 载荷」,渲染后由 python-docx 原位转换,规避 docxtpl 无法表达
29
+ 动态层级与富内容的限制。
30
+ - 章节标题段使用锚点样式(默认 ``DocxSectionTitle``),渲染后按 ``level`` 重设为
31
+ ``Heading N``,使模板绑定的 Word 多级列表自动编号生效。
32
+ - 有序列表按「连续组」克隆独立编号定义,避免多个列表被 Word 串成一条编号序列。
33
+ - 渲染开启 ``autoescape``;变量值控制字符沿用 docxtpl 约定(``\\n`` 软换行、
34
+ ``\\a`` 新段落)。
35
+ - 全静态方法、无模块级可变状态;路径参数支持 `str` / `Path`。
36
+
37
+ 前提条件:
38
+ - 模板需保留样式与书签骨架;字体/标题颜色统一由 `unify_font` /
39
+ `unify_heading_color` 处理,产物继承模板样式。
40
+
41
+ 依赖:
42
+ - `docxtpl` + `python-docx`(extra `docx`);未安装时导入报错并提示安装命令。
44
43
  """
45
44
 
46
45
  import re