sdpy-kit 0.4.2__tar.gz → 0.4.4__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.
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/PKG-INFO +21 -21
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/README.md +20 -20
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/__init__.py +1 -1
- sdpy_kit-0.4.4/kit/docs/__init__.py +32 -0
- {sdpy_kit-0.4.2/kit → sdpy_kit-0.4.4/kit/docs}/anydoc_kit.py +2 -2
- {sdpy_kit-0.4.2/kit → sdpy_kit-0.4.4/kit/docs}/marker_kit.py +3 -3
- {sdpy_kit-0.4.2/kit → sdpy_kit-0.4.4/kit/docs}/md2doc_kit.py +2 -2
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/license_kit.py +154 -147
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/pyproject.toml +5 -5
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/.gitignore +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/base_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/chat_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/embedding_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/failover.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/image_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/model_client.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/pool.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/profiles.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/rerank_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/runner.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/ai/types.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/common_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/config/kit_config.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/crawl/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/crawl/article_models.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/crawl/web_article_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/crawl/wx_article_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/base_db_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/crud.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/engine.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/exceptions.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/mysql_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/pg_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/query.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/session.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/sqlite_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.4.2/kit → sdpy_kit-0.4.4/kit/docs}/docx_kit.py +0 -0
- {sdpy_kit-0.4.2/kit → sdpy_kit-0.4.4/kit/docs}/mineru_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/file_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/kit/laya_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.4}/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.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
|
|
5
5
|
Author-email: Clark <changhongyuan@126.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -91,11 +91,11 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
91
91
|
| `kit.redis_kit` | `RedisKit` | Redis string / hash / list / set / zset | `redis` |
|
|
92
92
|
| `kit.file_kit` | `FileKit` | 文件与目录读写、移动、遍历 | 无 |
|
|
93
93
|
| `kit.common_kit` | `CommonKit` | 雪花 ID、时间戳 ID、时间格式化 | 无 |
|
|
94
|
-
| `kit.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
|
|
95
|
-
| `kit.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
|
|
96
|
-
| `kit.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
|
|
97
|
-
| `kit.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
98
|
-
| `kit.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
|
|
94
|
+
| `kit.docs.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
|
|
95
|
+
| `kit.docs.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
|
|
96
|
+
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
|
|
97
|
+
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
98
|
+
| `kit.docs.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
|
|
99
99
|
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
|
|
100
100
|
| `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(两 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
|
|
101
101
|
| `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
|
|
@@ -180,7 +180,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
180
180
|
|
|
181
181
|
### 环境变量 ↔ 能力对照表
|
|
182
182
|
|
|
183
|
-
> 表中未出现的模块(`kit.crawl.article_models`、`kit.common_kit`、`kit.docx_kit`、`kit.md2doc_kit`、`kit.crawl.wx_article_kit`、`kit.crawl.web_article_kit`)不读取任何环境变量。
|
|
183
|
+
> 表中未出现的模块(`kit.crawl.article_models`、`kit.common_kit`、`kit.docs.docx_kit`、`kit.docs.md2doc_kit`、`kit.crawl.wx_article_kit`、`kit.crawl.web_article_kit`)不读取任何环境变量。
|
|
184
184
|
|
|
185
185
|
**基础与日志**(影响 `kit.config` 及所有模块的日志输出)
|
|
186
186
|
|
|
@@ -231,7 +231,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
231
231
|
| `KIT_AI_CHAIN_TIMEOUT` | 未设置 | 全局整链预算(秒)= 门面同步等待上限。⚠ **不是 `Settings` 字段**,由代码直读 `os.environ`,无类型/范围校验,非法值仅告警后回退 |
|
|
232
232
|
| *(密钥,变量名由档案决定)* | 无 | 名字写在 `ai_models.json` 各档案的 `api_key` 字段(如 `SILICONFLOW_API_KEY`),值放 `.env` 或系统环境变量。未配置的档案会被剔除出候选链;该类型全部未配置则调用失败 |
|
|
233
233
|
|
|
234
|
-
**MinerU** → `kit.mineru_kit`
|
|
234
|
+
**MinerU** → `kit.docs.mineru_kit`
|
|
235
235
|
|
|
236
236
|
| 环境变量 | 默认值 | 作用 |
|
|
237
237
|
|----------|--------|------|
|
|
@@ -242,7 +242,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
242
242
|
| `MINERU_POLL_INTERVAL` | `2.0`(≥0.5) | 任务轮询间隔 |
|
|
243
243
|
| `MINERU_MAX_WAIT` | `600`(≥10) | 轮询总超时,超时抛 `MinerUError` |
|
|
244
244
|
|
|
245
|
-
**Marker** → `kit.marker_kit`
|
|
245
|
+
**Marker** → `kit.docs.marker_kit`
|
|
246
246
|
|
|
247
247
|
| 环境变量 | 默认值 | 作用 |
|
|
248
248
|
|----------|--------|------|
|
|
@@ -265,7 +265,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
265
265
|
> `LAYA_HF_*` 同样由 kit 在**导入 laya 之前**经 `os.environ.setdefault` 应用,不覆盖显式配置;
|
|
266
266
|
> `LAYA_MODEL` / `LAYA_DEVICE` 由 `Settings` 读取(进程内缓存,改后需重启进程)。
|
|
267
267
|
|
|
268
|
-
**云 OCR**(任何 `ocr="hosted"` 的转换)→ `kit.anydoc_kit`
|
|
268
|
+
**云 OCR**(任何 `ocr="hosted"` 的转换)→ `kit.docs.anydoc_kit`
|
|
269
269
|
|
|
270
270
|
| 环境变量 | 默认值 | 作用 |
|
|
271
271
|
|----------|--------|------|
|
|
@@ -295,7 +295,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
295
295
|
| 改动内容 | 生效时机 |
|
|
296
296
|
|----------|----------|
|
|
297
297
|
| `LOG_*`、`DB_*`、`MYSQL_*`、`REDIS_*`、`FILE_STORAGE_ROOT`、`MINERU_*`、`MARKER_MODE` | `get_settings()` 带 `lru_cache`,进程内首次读取后固定 → **重启进程**(或调用 `AI.reload_config()`,它会顺带清空该缓存) |
|
|
298
|
-
| `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
298
|
+
| `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.docs.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
299
299
|
| `LAYA_HF_ENDPOINT` / `LAYA_HF_DISABLE_XET` | 仅在 `import kit.laya_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
300
300
|
| AI 密钥写在 `.env` 里 | `.env` 只在加载档案时注入一次 → 重启,或 `AI.reload_config(reload_file=True)` |
|
|
301
301
|
| AI 密钥由自己写入 `os.environ` | `AI.reload_config()`(默认 `reload_file=False`,只重读 `os.environ`) |
|
|
@@ -849,12 +849,12 @@ CommonKit.format_current_time("yyyy-MM-dd")
|
|
|
849
849
|
|
|
850
850
|
---
|
|
851
851
|
|
|
852
|
-
## kit.anydoc_kit —— 文档转 Markdown
|
|
852
|
+
## kit.docs.anydoc_kit —— 文档转 Markdown
|
|
853
853
|
|
|
854
854
|
安装:`pip install "sdpy-kit[anydoc]"`(支持 `.doc .docx .ppt .pptx .xls .xlsx .csv .pdf`)
|
|
855
855
|
|
|
856
856
|
```python
|
|
857
|
-
from kit.anydoc_kit import AnyDocKit
|
|
857
|
+
from kit.docs.anydoc_kit import AnyDocKit
|
|
858
858
|
|
|
859
859
|
markdown = AnyDocKit.convert_to_md("report.docx") # 直接转换,失败抛异常
|
|
860
860
|
result = AnyDocKit.convert("report.pdf") # 安全转换,异常封装为结果对象
|
|
@@ -878,12 +878,12 @@ md = AnyDocKit.convert_to_md("scan.pdf", ocr="hosted") # 需 FIRECRAWL_API_KEY
|
|
|
878
878
|
|
|
879
879
|
---
|
|
880
880
|
|
|
881
|
-
## kit.docx_kit —— Word 模板动态填充
|
|
881
|
+
## kit.docs.docx_kit —— Word 模板动态填充
|
|
882
882
|
|
|
883
883
|
安装:`pip install "sdpy-kit[docx]"`
|
|
884
884
|
|
|
885
885
|
```python
|
|
886
|
-
from kit.docx_kit import DocxKit, DocxReport, DocxSection, DocxParagraph, DocxTable
|
|
886
|
+
from kit.docs.docx_kit import DocxKit, DocxReport, DocxSection, DocxParagraph, DocxTable
|
|
887
887
|
|
|
888
888
|
DocxKit.create_template("templates/report.docx", title="{{ title }}") # 生成模板骨架
|
|
889
889
|
DocxKit.unify_font("templates/report.docx", font_name="宋体")
|
|
@@ -917,12 +917,12 @@ DocxKit.render("templates/report.docx", "out/report.docx", report)
|
|
|
917
917
|
|
|
918
918
|
---
|
|
919
919
|
|
|
920
|
-
## kit.md2doc_kit —— Markdown 转 Word / PDF / HTML
|
|
920
|
+
## kit.docs.md2doc_kit —— Markdown 转 Word / PDF / HTML
|
|
921
921
|
|
|
922
922
|
安装:`pip install "sdpy-kit[md2doc]"`(PDF 需系统已安装 `mainfont` 指定字体,默认 `SimSun`)
|
|
923
923
|
|
|
924
924
|
```python
|
|
925
|
-
from kit.md2doc_kit import Md2DocKit
|
|
925
|
+
from kit.docs.md2doc_kit import Md2DocKit
|
|
926
926
|
|
|
927
927
|
Md2DocKit.convert_md_to_docx("report.md", "out.docx", generate_toc=True)
|
|
928
928
|
Md2DocKit.convert_md_to_html("report.md", title="项目报告")
|
|
@@ -939,12 +939,12 @@ Md2DocKit.convert_md_to_pdf("report.md", mainfont="KaiTi")
|
|
|
939
939
|
|
|
940
940
|
---
|
|
941
941
|
|
|
942
|
-
## kit.mineru_kit —— MinerU 文档解析
|
|
942
|
+
## kit.docs.mineru_kit —— MinerU 文档解析
|
|
943
943
|
|
|
944
944
|
安装:`pip install "sdpy-kit[mineru]"`(精准解析需 `MINERU_API_KEY`,Agent 轻量解析免登录)
|
|
945
945
|
|
|
946
946
|
```python
|
|
947
|
-
from kit.mineru_kit import MinerUKit
|
|
947
|
+
from kit.docs.mineru_kit import MinerUKit
|
|
948
948
|
|
|
949
949
|
# 精准解析:公网 URL / 本地文件
|
|
950
950
|
result = MinerUKit.parse_url("https://cdn-mineru.openxlab.org.cn/demo/example.pdf", model_version="vlm")
|
|
@@ -971,12 +971,12 @@ if r.state == "done":
|
|
|
971
971
|
|
|
972
972
|
---
|
|
973
973
|
|
|
974
|
-
## kit.marker_kit —— 本地文档解析(Markdown + Images)
|
|
974
|
+
## kit.docs.marker_kit —— 本地文档解析(Markdown + Images)
|
|
975
975
|
|
|
976
976
|
安装:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf[full]`,含 torch 等 80+ 包,体积较大)
|
|
977
977
|
|
|
978
978
|
```python
|
|
979
|
-
from kit.marker_kit import MarkerKit
|
|
979
|
+
from kit.docs.marker_kit import MarkerKit
|
|
980
980
|
|
|
981
981
|
# 落盘 md + images(推荐):源文件同目录下建同名子目录
|
|
982
982
|
md_path = MarkerKit.convert_to_dir("report.pdf") # -> report/report.md + 图片
|
|
@@ -18,11 +18,11 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
18
18
|
| `kit.redis_kit` | `RedisKit` | Redis string / hash / list / set / zset | `redis` |
|
|
19
19
|
| `kit.file_kit` | `FileKit` | 文件与目录读写、移动、遍历 | 无 |
|
|
20
20
|
| `kit.common_kit` | `CommonKit` | 雪花 ID、时间戳 ID、时间格式化 | 无 |
|
|
21
|
-
| `kit.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
|
|
22
|
-
| `kit.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
|
|
23
|
-
| `kit.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
|
|
24
|
-
| `kit.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
25
|
-
| `kit.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
|
|
21
|
+
| `kit.docs.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
|
|
22
|
+
| `kit.docs.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
|
|
23
|
+
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
|
|
24
|
+
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
25
|
+
| `kit.docs.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
|
|
26
26
|
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
|
|
27
27
|
| `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(两 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
|
|
28
28
|
| `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
|
|
@@ -107,7 +107,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
107
107
|
|
|
108
108
|
### 环境变量 ↔ 能力对照表
|
|
109
109
|
|
|
110
|
-
> 表中未出现的模块(`kit.crawl.article_models`、`kit.common_kit`、`kit.docx_kit`、`kit.md2doc_kit`、`kit.crawl.wx_article_kit`、`kit.crawl.web_article_kit`)不读取任何环境变量。
|
|
110
|
+
> 表中未出现的模块(`kit.crawl.article_models`、`kit.common_kit`、`kit.docs.docx_kit`、`kit.docs.md2doc_kit`、`kit.crawl.wx_article_kit`、`kit.crawl.web_article_kit`)不读取任何环境变量。
|
|
111
111
|
|
|
112
112
|
**基础与日志**(影响 `kit.config` 及所有模块的日志输出)
|
|
113
113
|
|
|
@@ -158,7 +158,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
158
158
|
| `KIT_AI_CHAIN_TIMEOUT` | 未设置 | 全局整链预算(秒)= 门面同步等待上限。⚠ **不是 `Settings` 字段**,由代码直读 `os.environ`,无类型/范围校验,非法值仅告警后回退 |
|
|
159
159
|
| *(密钥,变量名由档案决定)* | 无 | 名字写在 `ai_models.json` 各档案的 `api_key` 字段(如 `SILICONFLOW_API_KEY`),值放 `.env` 或系统环境变量。未配置的档案会被剔除出候选链;该类型全部未配置则调用失败 |
|
|
160
160
|
|
|
161
|
-
**MinerU** → `kit.mineru_kit`
|
|
161
|
+
**MinerU** → `kit.docs.mineru_kit`
|
|
162
162
|
|
|
163
163
|
| 环境变量 | 默认值 | 作用 |
|
|
164
164
|
|----------|--------|------|
|
|
@@ -169,7 +169,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
169
169
|
| `MINERU_POLL_INTERVAL` | `2.0`(≥0.5) | 任务轮询间隔 |
|
|
170
170
|
| `MINERU_MAX_WAIT` | `600`(≥10) | 轮询总超时,超时抛 `MinerUError` |
|
|
171
171
|
|
|
172
|
-
**Marker** → `kit.marker_kit`
|
|
172
|
+
**Marker** → `kit.docs.marker_kit`
|
|
173
173
|
|
|
174
174
|
| 环境变量 | 默认值 | 作用 |
|
|
175
175
|
|----------|--------|------|
|
|
@@ -192,7 +192,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
192
192
|
> `LAYA_HF_*` 同样由 kit 在**导入 laya 之前**经 `os.environ.setdefault` 应用,不覆盖显式配置;
|
|
193
193
|
> `LAYA_MODEL` / `LAYA_DEVICE` 由 `Settings` 读取(进程内缓存,改后需重启进程)。
|
|
194
194
|
|
|
195
|
-
**云 OCR**(任何 `ocr="hosted"` 的转换)→ `kit.anydoc_kit`
|
|
195
|
+
**云 OCR**(任何 `ocr="hosted"` 的转换)→ `kit.docs.anydoc_kit`
|
|
196
196
|
|
|
197
197
|
| 环境变量 | 默认值 | 作用 |
|
|
198
198
|
|----------|--------|------|
|
|
@@ -222,7 +222,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
222
222
|
| 改动内容 | 生效时机 |
|
|
223
223
|
|----------|----------|
|
|
224
224
|
| `LOG_*`、`DB_*`、`MYSQL_*`、`REDIS_*`、`FILE_STORAGE_ROOT`、`MINERU_*`、`MARKER_MODE` | `get_settings()` 带 `lru_cache`,进程内首次读取后固定 → **重启进程**(或调用 `AI.reload_config()`,它会顺带清空该缓存) |
|
|
225
|
-
| `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
225
|
+
| `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.docs.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
226
226
|
| `LAYA_HF_ENDPOINT` / `LAYA_HF_DISABLE_XET` | 仅在 `import kit.laya_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
227
227
|
| AI 密钥写在 `.env` 里 | `.env` 只在加载档案时注入一次 → 重启,或 `AI.reload_config(reload_file=True)` |
|
|
228
228
|
| AI 密钥由自己写入 `os.environ` | `AI.reload_config()`(默认 `reload_file=False`,只重读 `os.environ`) |
|
|
@@ -776,12 +776,12 @@ CommonKit.format_current_time("yyyy-MM-dd")
|
|
|
776
776
|
|
|
777
777
|
---
|
|
778
778
|
|
|
779
|
-
## kit.anydoc_kit —— 文档转 Markdown
|
|
779
|
+
## kit.docs.anydoc_kit —— 文档转 Markdown
|
|
780
780
|
|
|
781
781
|
安装:`pip install "sdpy-kit[anydoc]"`(支持 `.doc .docx .ppt .pptx .xls .xlsx .csv .pdf`)
|
|
782
782
|
|
|
783
783
|
```python
|
|
784
|
-
from kit.anydoc_kit import AnyDocKit
|
|
784
|
+
from kit.docs.anydoc_kit import AnyDocKit
|
|
785
785
|
|
|
786
786
|
markdown = AnyDocKit.convert_to_md("report.docx") # 直接转换,失败抛异常
|
|
787
787
|
result = AnyDocKit.convert("report.pdf") # 安全转换,异常封装为结果对象
|
|
@@ -805,12 +805,12 @@ md = AnyDocKit.convert_to_md("scan.pdf", ocr="hosted") # 需 FIRECRAWL_API_KEY
|
|
|
805
805
|
|
|
806
806
|
---
|
|
807
807
|
|
|
808
|
-
## kit.docx_kit —— Word 模板动态填充
|
|
808
|
+
## kit.docs.docx_kit —— Word 模板动态填充
|
|
809
809
|
|
|
810
810
|
安装:`pip install "sdpy-kit[docx]"`
|
|
811
811
|
|
|
812
812
|
```python
|
|
813
|
-
from kit.docx_kit import DocxKit, DocxReport, DocxSection, DocxParagraph, DocxTable
|
|
813
|
+
from kit.docs.docx_kit import DocxKit, DocxReport, DocxSection, DocxParagraph, DocxTable
|
|
814
814
|
|
|
815
815
|
DocxKit.create_template("templates/report.docx", title="{{ title }}") # 生成模板骨架
|
|
816
816
|
DocxKit.unify_font("templates/report.docx", font_name="宋体")
|
|
@@ -844,12 +844,12 @@ DocxKit.render("templates/report.docx", "out/report.docx", report)
|
|
|
844
844
|
|
|
845
845
|
---
|
|
846
846
|
|
|
847
|
-
## kit.md2doc_kit —— Markdown 转 Word / PDF / HTML
|
|
847
|
+
## kit.docs.md2doc_kit —— Markdown 转 Word / PDF / HTML
|
|
848
848
|
|
|
849
849
|
安装:`pip install "sdpy-kit[md2doc]"`(PDF 需系统已安装 `mainfont` 指定字体,默认 `SimSun`)
|
|
850
850
|
|
|
851
851
|
```python
|
|
852
|
-
from kit.md2doc_kit import Md2DocKit
|
|
852
|
+
from kit.docs.md2doc_kit import Md2DocKit
|
|
853
853
|
|
|
854
854
|
Md2DocKit.convert_md_to_docx("report.md", "out.docx", generate_toc=True)
|
|
855
855
|
Md2DocKit.convert_md_to_html("report.md", title="项目报告")
|
|
@@ -866,12 +866,12 @@ Md2DocKit.convert_md_to_pdf("report.md", mainfont="KaiTi")
|
|
|
866
866
|
|
|
867
867
|
---
|
|
868
868
|
|
|
869
|
-
## kit.mineru_kit —— MinerU 文档解析
|
|
869
|
+
## kit.docs.mineru_kit —— MinerU 文档解析
|
|
870
870
|
|
|
871
871
|
安装:`pip install "sdpy-kit[mineru]"`(精准解析需 `MINERU_API_KEY`,Agent 轻量解析免登录)
|
|
872
872
|
|
|
873
873
|
```python
|
|
874
|
-
from kit.mineru_kit import MinerUKit
|
|
874
|
+
from kit.docs.mineru_kit import MinerUKit
|
|
875
875
|
|
|
876
876
|
# 精准解析:公网 URL / 本地文件
|
|
877
877
|
result = MinerUKit.parse_url("https://cdn-mineru.openxlab.org.cn/demo/example.pdf", model_version="vlm")
|
|
@@ -898,12 +898,12 @@ if r.state == "done":
|
|
|
898
898
|
|
|
899
899
|
---
|
|
900
900
|
|
|
901
|
-
## kit.marker_kit —— 本地文档解析(Markdown + Images)
|
|
901
|
+
## kit.docs.marker_kit —— 本地文档解析(Markdown + Images)
|
|
902
902
|
|
|
903
903
|
安装:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf[full]`,含 torch 等 80+ 包,体积较大)
|
|
904
904
|
|
|
905
905
|
```python
|
|
906
|
-
from kit.marker_kit import MarkerKit
|
|
906
|
+
from kit.docs.marker_kit import MarkerKit
|
|
907
907
|
|
|
908
908
|
# 落盘 md + images(推荐):源文件同目录下建同名子目录
|
|
909
909
|
md_path = MarkerKit.convert_to_dir("report.pdf") # -> report/report.md + 图片
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
Project: sdpy
|
|
17
|
+
Author: Clark Chang
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# date: 2026-10-16
|
|
21
|
+
"""文档格式转换子包——文档解析 / 渲染 / 模板填充工具集
|
|
22
|
+
|
|
23
|
+
成员(均按需 import,各自绑定独立 extras,缺依赖时模块顶层报错并提示安装命令):
|
|
24
|
+
- anydoc_kit: AnyDocKit 文档转 Markdown(firecrawl-anydoc,extra: anydoc)
|
|
25
|
+
- md2doc_kit: Md2DocKit Markdown 转 Word/PDF/HTML(pimd,extra: md2doc)
|
|
26
|
+
- docx_kit: DocxKit Word 模板动态填充(docxtpl,extra: docx)
|
|
27
|
+
- mineru_kit: MinerUKit MinerU 文档解析 API(requests,extra: mineru)
|
|
28
|
+
- marker_kit: MarkerKit Marker 本地文档解析(marker-pdf,extra: marker)
|
|
29
|
+
|
|
30
|
+
本 __init__ 不导入任何成员:五个模块均携带重型可选依赖,保持
|
|
31
|
+
`import kit.docs` 在基础安装下零成本可用(对齐 kit.crawl 先例)。
|
|
32
|
+
"""
|
|
@@ -22,7 +22,7 @@ Author: Clark Chang
|
|
|
22
22
|
|
|
23
23
|
本模块封装 firecrawl-anydoc(pip 安装名 firecrawl-anydoc,import 名 anydoc),
|
|
24
24
|
将 Word、PowerPoint、Excel、CSV、PDF 格式统一转为 GitHub-Flavored Markdown;
|
|
25
|
-
Markdown → Word/HTML/PDF 的反向转换见 kit.md2doc_kit(Md2DocKit)。
|
|
25
|
+
Markdown → Word/HTML/PDF 的反向转换见 kit.docs.md2doc_kit(Md2DocKit)。
|
|
26
26
|
|
|
27
27
|
设计要点:
|
|
28
28
|
- 全部使用静态方法提供服务,无内部状态。
|
|
@@ -183,7 +183,7 @@ class AnyDocKit:
|
|
|
183
183
|
# 5. 字节内容转换(无需落盘)
|
|
184
184
|
md = AnyDocKit.convert_bytes_to_md(file_bytes, "csv")
|
|
185
185
|
|
|
186
|
-
Markdown → Word/PDF/HTML 的反向转换见 kit.md2doc_kit(Md2DocKit)。
|
|
186
|
+
Markdown → Word/PDF/HTML 的反向转换见 kit.docs.md2doc_kit(Md2DocKit)。
|
|
187
187
|
|
|
188
188
|
可与 FileKit 组合使用以访问 FILE_STORAGE_ROOT 下的相对路径文件:
|
|
189
189
|
full_path = FileKit.get_full_path("docs/report.docx")
|
|
@@ -22,9 +22,9 @@ Author: Clark Chang
|
|
|
22
22
|
|
|
23
23
|
本模块基于 marker-pdf(Marker v2.x,https://github.com/datalab-to/marker),
|
|
24
24
|
在本地解析 PDF / 图片 / Office / HTML / EPUB,输出 GitHub-Flavored Markdown
|
|
25
|
-
并提取图片,产物形态对齐 kit.mineru_kit 的「md + images」。
|
|
25
|
+
并提取图片,产物形态对齐 kit.docs.mineru_kit 的「md + images」。
|
|
26
26
|
|
|
27
|
-
与 kit.anydoc_kit 的分工:
|
|
27
|
+
与 kit.docs.anydoc_kit 的分工:
|
|
28
28
|
- AnyDocKit:轻量文本转换(firecrawl-anydoc),只产出 Markdown 文本;
|
|
29
29
|
- MarkerKit:本地模型解析,产出 md + 图片,质量更高但依赖重(torch 等)。
|
|
30
30
|
|
|
@@ -144,7 +144,7 @@ class MarkerKit:
|
|
|
144
144
|
"""Marker 本地文档解析工具类,将文档转为 Markdown + Images
|
|
145
145
|
|
|
146
146
|
支持 PDF、图片、Office(docx/pptx/xlsx)、HTML、EPUB,产物形态对齐
|
|
147
|
-
kit.mineru_kit(md + 图片落盘于同一目录,md 以相对路径引用图片)。
|
|
147
|
+
kit.docs.mineru_kit(md + 图片落盘于同一目录,md 以相对路径引用图片)。
|
|
148
148
|
|
|
149
149
|
使用前安装可选依赖::
|
|
150
150
|
|
|
@@ -22,7 +22,7 @@ Author: Clark Chang
|
|
|
22
22
|
|
|
23
23
|
本模块基于 PiMD(pimd >= 2.2.5)将 Markdown 转换为 Word(.docx)与独立
|
|
24
24
|
HTML;PDF 由本模块内嵌的 fpdf2 渲染器直接生成,保证中文与表格完整。
|
|
25
|
-
各类文档(Office/PDF)→ Markdown 的正向解析见 kit.anydoc_kit(AnyDocKit)。
|
|
25
|
+
各类文档(Office/PDF)→ Markdown 的正向解析见 kit.docs.anydoc_kit(AnyDocKit)。
|
|
26
26
|
|
|
27
27
|
设计要点:
|
|
28
28
|
- 全部使用静态方法提供服务,无内部状态;PiMD 转换引擎为模块级单例,按进程
|
|
@@ -1056,7 +1056,7 @@ class Md2DocKit:
|
|
|
1056
1056
|
convert_md_to_pdf 方法说明);需要 PDF 时优先用 convert_md_to_html
|
|
1057
1057
|
后在浏览器中打印/另存 PDF,或 convert_md_to_docx 后在 Word 中另存 PDF。
|
|
1058
1058
|
|
|
1059
|
-
各类文档(Office/PDF)→ Markdown 的正向解析见 kit.anydoc_kit(AnyDocKit)。
|
|
1059
|
+
各类文档(Office/PDF)→ Markdown 的正向解析见 kit.docs.anydoc_kit(AnyDocKit)。
|
|
1060
1060
|
可与 FileKit 组合使用以访问 FILE_STORAGE_ROOT 下的相对路径文件:
|
|
1061
1061
|
full_path = FileKit.get_full_path("docs/report.md")
|
|
1062
1062
|
Md2DocKit.convert_md_to_html(full_path)
|
|
@@ -23,9 +23,9 @@ Author: Clark Chang
|
|
|
23
23
|
面向单机分发场景的轻量授权体系(防君子定位):应用以打包 exe 形式离线分发,
|
|
24
24
|
无远端授权服务。工作流:
|
|
25
25
|
|
|
26
|
-
1. 客户机 `export_fingerprint_file()` 导出 `.fp` 指纹文件;
|
|
27
|
-
2. 签发方 `parse_fingerprint_file()` 解析,填入 `LicensePayload`
|
|
28
|
-
`sign()` 生成 `.lic` 授权文件回签;
|
|
26
|
+
1. 客户机 `LicenseKit.export_fingerprint_file()` 导出 `.fp` 指纹文件;
|
|
27
|
+
2. 签发方 `LicenseKit.parse_fingerprint_file()` 解析,填入 `LicensePayload`
|
|
28
|
+
后 `LicenseKit.sign()` 生成 `.lic` 授权文件回签;
|
|
29
29
|
3. 客户机 `LicenseVerifier.verify()` 验签 + 绑定校验(product / 机器码 / 有效期)。
|
|
30
30
|
|
|
31
31
|
安全边界(务必知悉):
|
|
@@ -41,19 +41,20 @@ Author: Clark Chang
|
|
|
41
41
|
规范化 JSON 序列化后签名,杜绝字段顺序 / 空白差异导致验签漂移。
|
|
42
42
|
|
|
43
43
|
用法:
|
|
44
|
-
from kit.license_kit import
|
|
45
|
-
LicensePayload, LicenseVerifier, export_fingerprint_file,
|
|
46
|
-
generate_keypair, parse_fingerprint_file, sign,
|
|
47
|
-
)
|
|
44
|
+
from kit.license_kit import LicenseKit, LicensePayload, LicenseVerifier
|
|
48
45
|
|
|
49
|
-
private_pem, public_pem = generate_keypair()
|
|
46
|
+
private_pem, public_pem = LicenseKit.generate_keypair()
|
|
50
47
|
payload = LicensePayload(product="myapp", machine_code="0123456789abcdef",
|
|
51
48
|
issued_at=datetime.now(timezone.utc),
|
|
52
49
|
expires_at=None)
|
|
53
|
-
lic_text = sign(payload, private_pem)
|
|
50
|
+
lic_text = LicenseKit.sign(payload, private_pem)
|
|
54
51
|
|
|
55
52
|
verifier = LicenseVerifier(public_pem, Path("license.lic"))
|
|
56
53
|
info = verifier.verify(product="myapp", machine_code="0123456789abcdef")
|
|
54
|
+
|
|
55
|
+
形态说明:0.4.2 为模块级函数,0.4.3 起收进静态工具类 `LicenseKit`
|
|
56
|
+
(与 FileKit / LayaKit 等其它 kit 形态一致);`LicenseVerifier` 因持有
|
|
57
|
+
公钥缓存与验签上下文实例状态,保持独立类。
|
|
57
58
|
"""
|
|
58
59
|
|
|
59
60
|
import base64
|
|
@@ -125,7 +126,7 @@ class LicenseExpiredError(LicenseError):
|
|
|
125
126
|
|
|
126
127
|
|
|
127
128
|
# ---------------------------------------------------------------------------
|
|
128
|
-
#
|
|
129
|
+
# 机器指纹私有实现(Windows 专属;winreg/ctypes 延迟导入保证模块任意平台可 import)
|
|
129
130
|
# ---------------------------------------------------------------------------
|
|
130
131
|
|
|
131
132
|
|
|
@@ -175,118 +176,14 @@ def _collect_components() -> tuple[str, str]:
|
|
|
175
176
|
return _read_machine_guid(), _read_volume_serial()
|
|
176
177
|
|
|
177
178
|
|
|
178
|
-
def
|
|
179
|
-
"""
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
str: 16 位十六进制机器码
|
|
183
|
-
|
|
184
|
-
Raises:
|
|
185
|
-
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
186
|
-
LicenseFormatError: 指纹组件读取失败
|
|
187
|
-
"""
|
|
188
|
-
machine_guid, volume_serial = _collect_components()
|
|
179
|
+
def _machine_code_of(machine_guid: str, volume_serial: str) -> str:
|
|
180
|
+
"""由原始组件计算机器码短码(签发与导出共用,避免两处公式漂移)"""
|
|
189
181
|
digest = hashlib.sha256(f"{machine_guid}|{volume_serial}".encode("utf-8"))
|
|
190
182
|
return digest.hexdigest()[:MACHINE_CODE_LENGTH]
|
|
191
183
|
|
|
192
184
|
|
|
193
|
-
def export_fingerprint_file(path: Path) -> Path:
|
|
194
|
-
"""导出指纹文件(.fp):机器码 + 原始组件 + 导出时间
|
|
195
|
-
|
|
196
|
-
原始组件仅用于报障诊断(作者侧可看出哪个组件不匹配),验签只比短码。
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
path: 指纹文件输出路径
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
Path: 实际写入的文件路径
|
|
203
|
-
|
|
204
|
-
Raises:
|
|
205
|
-
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
206
|
-
LicenseFormatError: 指纹组件读取失败
|
|
207
|
-
"""
|
|
208
|
-
machine_guid, volume_serial = _collect_components()
|
|
209
|
-
machine_code = hashlib.sha256(
|
|
210
|
-
f"{machine_guid}|{volume_serial}".encode("utf-8")
|
|
211
|
-
).hexdigest()[:MACHINE_CODE_LENGTH]
|
|
212
|
-
document = {
|
|
213
|
-
"kind": _FINGERPRINT_KIND,
|
|
214
|
-
"format_version": FINGERPRINT_FORMAT_VERSION,
|
|
215
|
-
"machine_code": machine_code,
|
|
216
|
-
"components": {
|
|
217
|
-
"machine_guid": machine_guid,
|
|
218
|
-
"volume_serial": volume_serial,
|
|
219
|
-
},
|
|
220
|
-
"exported_at": datetime.now(timezone.utc).isoformat(),
|
|
221
|
-
}
|
|
222
|
-
target = Path(path)
|
|
223
|
-
target.parent.mkdir(parents=True, exist_ok=True)
|
|
224
|
-
target.write_text(
|
|
225
|
-
json.dumps(document, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
226
|
-
)
|
|
227
|
-
logger.info("指纹文件已导出:%s(机器码 %s)", target, machine_code)
|
|
228
|
-
return target
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def parse_fingerprint_file(path: Path) -> str:
|
|
232
|
-
"""解析指纹文件,返回机器码(签发侧使用;跨机器无法重算原文,只校验结构)
|
|
233
|
-
|
|
234
|
-
Args:
|
|
235
|
-
path: 指纹文件路径
|
|
236
|
-
|
|
237
|
-
Returns:
|
|
238
|
-
str: 16 位十六进制机器码
|
|
239
|
-
|
|
240
|
-
Raises:
|
|
241
|
-
LicenseFormatError: 文件缺失 / JSON 损坏 / 标识或版本不符 / 机器码格式非法
|
|
242
|
-
"""
|
|
243
|
-
source = Path(path)
|
|
244
|
-
if not source.is_file():
|
|
245
|
-
raise LicenseFormatError(f"指纹文件不存在:{source}")
|
|
246
|
-
try:
|
|
247
|
-
document = json.loads(source.read_text(encoding="utf-8"))
|
|
248
|
-
except (OSError, json.JSONDecodeError) as exc:
|
|
249
|
-
raise LicenseFormatError(f"指纹文件读取或解析失败:{exc}") from exc
|
|
250
|
-
if not isinstance(document, dict):
|
|
251
|
-
raise LicenseFormatError("指纹文件结构不合法(顶层应为 JSON 对象)")
|
|
252
|
-
if document.get("kind") != _FINGERPRINT_KIND:
|
|
253
|
-
raise LicenseFormatError("指纹文件标识不符(kind)")
|
|
254
|
-
version = document.get("format_version")
|
|
255
|
-
if not isinstance(version, int) or version > FINGERPRINT_FORMAT_VERSION:
|
|
256
|
-
raise LicenseFormatError(f"指纹文件格式版本不识别:{version!r}")
|
|
257
|
-
machine_code = document.get("machine_code")
|
|
258
|
-
if not isinstance(machine_code, str) or not _HEX16_RE.fullmatch(machine_code):
|
|
259
|
-
raise LicenseFormatError("指纹文件缺少合法机器码(16 hex)")
|
|
260
|
-
return machine_code
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
# ---------------------------------------------------------------------------
|
|
264
|
-
# 密钥
|
|
265
|
-
# ---------------------------------------------------------------------------
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def generate_keypair() -> tuple[str, str]:
|
|
269
|
-
"""生成 Ed25519 密钥对(一次性初始化用)
|
|
270
|
-
|
|
271
|
-
Returns:
|
|
272
|
-
tuple[str, str]: (private_pem, public_pem),均为 PEM 文本;
|
|
273
|
-
私钥务必本机妥善保管(丢失 = 已发授权全部作废)
|
|
274
|
-
"""
|
|
275
|
-
private_key = Ed25519PrivateKey.generate()
|
|
276
|
-
private_pem = private_key.private_bytes(
|
|
277
|
-
encoding=serialization.Encoding.PEM,
|
|
278
|
-
format=serialization.PrivateFormat.PKCS8,
|
|
279
|
-
encryption_algorithm=serialization.NoEncryption(),
|
|
280
|
-
).decode("utf-8")
|
|
281
|
-
public_pem = private_key.public_key().public_bytes(
|
|
282
|
-
encoding=serialization.Encoding.PEM,
|
|
283
|
-
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
284
|
-
).decode("utf-8")
|
|
285
|
-
return private_pem, public_pem
|
|
286
|
-
|
|
287
|
-
|
|
288
185
|
# ---------------------------------------------------------------------------
|
|
289
|
-
#
|
|
186
|
+
# 载荷(签发与验签共用)
|
|
290
187
|
# ---------------------------------------------------------------------------
|
|
291
188
|
|
|
292
189
|
|
|
@@ -340,39 +237,149 @@ def _canonical_bytes(payload: dict) -> bytes:
|
|
|
340
237
|
).encode("utf-8")
|
|
341
238
|
|
|
342
239
|
|
|
343
|
-
|
|
344
|
-
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
# 静态工具类(与其它 kit 形态一致;格式唯一事实源——签发与验签共用本模块实现)
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
345
243
|
|
|
346
|
-
Args:
|
|
347
|
-
payload: 授权载荷
|
|
348
|
-
private_key_pem: Ed25519 私钥 PEM(PKCS8)
|
|
349
244
|
|
|
350
|
-
|
|
351
|
-
|
|
245
|
+
class LicenseKit:
|
|
246
|
+
"""license 授权静态工具类:指纹、密钥、签发"""
|
|
352
247
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
248
|
+
@staticmethod
|
|
249
|
+
def get_machine_code() -> str:
|
|
250
|
+
"""计算本机机器码:组合指纹 SHA256 短码(16 hex 小写)
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
str: 16 位十六进制机器码
|
|
254
|
+
|
|
255
|
+
Raises:
|
|
256
|
+
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
257
|
+
LicenseFormatError: 指纹组件读取失败
|
|
258
|
+
"""
|
|
259
|
+
machine_guid, volume_serial = _collect_components()
|
|
260
|
+
return _machine_code_of(machine_guid, volume_serial)
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def export_fingerprint_file(path: Path) -> Path:
|
|
264
|
+
"""导出指纹文件(.fp):机器码 + 原始组件 + 导出时间
|
|
265
|
+
|
|
266
|
+
原始组件仅用于报障诊断(作者侧可看出哪个组件不匹配),验签只比短码。
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
path: 指纹文件输出路径
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Path: 实际写入的文件路径
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
276
|
+
LicenseFormatError: 指纹组件读取失败
|
|
277
|
+
"""
|
|
278
|
+
machine_guid, volume_serial = _collect_components()
|
|
279
|
+
document = {
|
|
280
|
+
"kind": _FINGERPRINT_KIND,
|
|
281
|
+
"format_version": FINGERPRINT_FORMAT_VERSION,
|
|
282
|
+
"machine_code": _machine_code_of(machine_guid, volume_serial),
|
|
283
|
+
"components": {
|
|
284
|
+
"machine_guid": machine_guid,
|
|
285
|
+
"volume_serial": volume_serial,
|
|
286
|
+
},
|
|
287
|
+
"exported_at": datetime.now(timezone.utc).isoformat(),
|
|
288
|
+
}
|
|
289
|
+
target = Path(path)
|
|
290
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
291
|
+
target.write_text(
|
|
292
|
+
json.dumps(document, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
359
293
|
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
294
|
+
logger.info("指纹文件已导出:%s(机器码 %s)", target, document["machine_code"])
|
|
295
|
+
return target
|
|
296
|
+
|
|
297
|
+
@staticmethod
|
|
298
|
+
def parse_fingerprint_file(path: Path) -> str:
|
|
299
|
+
"""解析指纹文件,返回机器码(签发侧使用;跨机器无法重算原文,只校验结构)
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
path: 指纹文件路径
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
str: 16 位十六进制机器码
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
LicenseFormatError: 文件缺失 / JSON 损坏 / 标识或版本不符 / 机器码格式非法
|
|
309
|
+
"""
|
|
310
|
+
source = Path(path)
|
|
311
|
+
if not source.is_file():
|
|
312
|
+
raise LicenseFormatError(f"指纹文件不存在:{source}")
|
|
313
|
+
try:
|
|
314
|
+
document = json.loads(source.read_text(encoding="utf-8"))
|
|
315
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
316
|
+
raise LicenseFormatError(f"指纹文件读取或解析失败:{exc}") from exc
|
|
317
|
+
if not isinstance(document, dict):
|
|
318
|
+
raise LicenseFormatError("指纹文件结构不合法(顶层应为 JSON 对象)")
|
|
319
|
+
if document.get("kind") != _FINGERPRINT_KIND:
|
|
320
|
+
raise LicenseFormatError("指纹文件标识不符(kind)")
|
|
321
|
+
version = document.get("format_version")
|
|
322
|
+
if not isinstance(version, int) or version > FINGERPRINT_FORMAT_VERSION:
|
|
323
|
+
raise LicenseFormatError(f"指纹文件格式版本不识别:{version!r}")
|
|
324
|
+
machine_code = document.get("machine_code")
|
|
325
|
+
if not isinstance(machine_code, str) or not _HEX16_RE.fullmatch(machine_code):
|
|
326
|
+
raise LicenseFormatError("指纹文件缺少合法机器码(16 hex)")
|
|
327
|
+
return machine_code
|
|
328
|
+
|
|
329
|
+
@staticmethod
|
|
330
|
+
def generate_keypair() -> tuple[str, str]:
|
|
331
|
+
"""生成 Ed25519 密钥对(一次性初始化用)
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
tuple[str, str]: (private_pem, public_pem),均为 PEM 文本;
|
|
335
|
+
私钥务必本机妥善保管(丢失 = 已发授权全部作废)
|
|
336
|
+
"""
|
|
337
|
+
private_key = Ed25519PrivateKey.generate()
|
|
338
|
+
private_pem = private_key.private_bytes(
|
|
339
|
+
encoding=serialization.Encoding.PEM,
|
|
340
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
341
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
342
|
+
).decode("utf-8")
|
|
343
|
+
public_pem = private_key.public_key().public_bytes(
|
|
344
|
+
encoding=serialization.Encoding.PEM,
|
|
345
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
346
|
+
).decode("utf-8")
|
|
347
|
+
return private_pem, public_pem
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def sign(payload: LicensePayload, private_key_pem: str) -> str:
|
|
351
|
+
"""对载荷签名,返回 license 文件内容(JSON 文本,即 .lic)
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
payload: 授权载荷
|
|
355
|
+
private_key_pem: Ed25519 私钥 PEM(PKCS8)
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
str: license 文件内容 `{"payload": {...}, "signature": "<b64>"}`
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
LicenseFormatError: 私钥解析失败或类型不是 Ed25519
|
|
362
|
+
"""
|
|
363
|
+
try:
|
|
364
|
+
private_key = serialization.load_pem_private_key(
|
|
365
|
+
private_key_pem.encode("utf-8"), password=None
|
|
366
|
+
)
|
|
367
|
+
except (ValueError, TypeError) as exc:
|
|
368
|
+
raise LicenseFormatError(f"私钥 PEM 解析失败:{exc}") from exc
|
|
369
|
+
if not isinstance(private_key, Ed25519PrivateKey):
|
|
370
|
+
raise LicenseFormatError("私钥类型不是 Ed25519")
|
|
371
|
+
payload_dict = payload.model_dump(mode="json")
|
|
372
|
+
signature = private_key.sign(_canonical_bytes(payload_dict))
|
|
373
|
+
document = {
|
|
374
|
+
"payload": payload_dict,
|
|
375
|
+
"signature": base64.b64encode(signature).decode("ascii"),
|
|
376
|
+
}
|
|
377
|
+
logger.info(
|
|
378
|
+
"license 已签发:product=%s machine_code=%s",
|
|
379
|
+
payload.product,
|
|
380
|
+
payload.machine_code,
|
|
381
|
+
)
|
|
382
|
+
return json.dumps(document, ensure_ascii=False, indent=2)
|
|
376
383
|
|
|
377
384
|
|
|
378
385
|
# ---------------------------------------------------------------------------
|
|
@@ -445,7 +452,7 @@ class LicenseVerifier:
|
|
|
445
452
|
|
|
446
453
|
Args:
|
|
447
454
|
product: 期望的应用标识
|
|
448
|
-
machine_code: 期望的机器码(本机 `get_machine_code()` 结果)
|
|
455
|
+
machine_code: 期望的机器码(本机 `LicenseKit.get_machine_code()` 结果)
|
|
449
456
|
now: 判定"当前时间";None 取 aware UTC 当前时刻;naive 按 UTC 处理
|
|
450
457
|
|
|
451
458
|
Returns:
|
|
@@ -47,11 +47,11 @@ dynamic = ["version"]
|
|
|
47
47
|
# sqliteai → sqlite_kit 向量方法(numpy/sqliteai-vector,向量扩展)
|
|
48
48
|
# redis → redis_kit
|
|
49
49
|
# ai → kit/ai 服务池(httpx;dotenv 由 kit.ai 直接 import,故显式声明)
|
|
50
|
-
# md2doc → md2doc_kit(pimd/markdown-it-py,fpdf2;extra
|
|
51
|
-
# anydoc → anydoc_kit(firecrawl-anydoc + pikepdf,显式声明)
|
|
52
|
-
# docx → docx_kit(docxtpl + python-docx,模板动态填充)
|
|
53
|
-
# mineru → mineru_kit(requests)
|
|
54
|
-
# marker → marker_kit(marker-pdf[full],本地模型解析 PDF→Markdown+Images)
|
|
50
|
+
# md2doc → kit.docs.md2doc_kit(pimd/markdown-it-py,fpdf2;extra 名沿用模块族名)
|
|
51
|
+
# anydoc → kit.docs.anydoc_kit(firecrawl-anydoc + pikepdf,显式声明)
|
|
52
|
+
# docx → kit.docs.docx_kit(docxtpl + python-docx,模板动态填充)
|
|
53
|
+
# mineru → kit.docs.mineru_kit(requests)
|
|
54
|
+
# marker → kit.docs.marker_kit(marker-pdf[full],本地模型解析 PDF→Markdown+Images)
|
|
55
55
|
# laya → laya_kit(laya + torch,本地类型化决策引擎 choice/score/noul)
|
|
56
56
|
# web-article → crawl/web_article_kit(trafilatura,通用网页文章抓取转存)
|
|
57
57
|
# 基础版 = config/log + kit.db 核心,SQLite 常规操作用标准库 sqlite3,零额外依赖。
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|