sdpy-kit 0.3.0__tar.gz → 0.4.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 (50) hide show
  1. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/.gitignore +1 -3
  2. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/PKG-INFO +97 -141
  3. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/README.md +91 -132
  4. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/__init__.py +1 -1
  5. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/config/kit_config.py +0 -32
  6. sdpy_kit-0.4.0/kit/crawl/__init__.py +63 -0
  7. sdpy_kit-0.4.0/kit/crawl/article_models.py +162 -0
  8. sdpy_kit-0.4.0/kit/crawl/web_article_kit.py +784 -0
  9. {sdpy_kit-0.3.0/kit → sdpy_kit-0.4.0/kit/crawl}/wx_article_kit.py +38 -76
  10. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/mysql_kit.py +1 -1
  11. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/pyproject.toml +5 -8
  12. sdpy_kit-0.3.0/kit/mcp_kit.py +0 -547
  13. sdpy_kit-0.3.0/kit/zvec_kit.py +0 -671
  14. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/__init__.py +0 -0
  15. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/base_service.py +0 -0
  16. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/chat_service.py +0 -0
  17. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/embedding_service.py +0 -0
  18. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/exceptions.py +0 -0
  19. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/failover.py +0 -0
  20. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/image_service.py +0 -0
  21. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/model_builder.py +0 -0
  22. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/model_client.py +0 -0
  23. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/pool.py +0 -0
  24. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/profiles.py +0 -0
  25. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/rerank_service.py +0 -0
  26. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/runner.py +0 -0
  27. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/ai/types.py +0 -0
  28. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/anydoc_kit.py +0 -0
  29. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/common_kit.py +0 -0
  30. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/config/__init__.py +0 -0
  31. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/config/log_config.py +0 -0
  32. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/__init__.py +0 -0
  33. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/base_db_kit.py +0 -0
  34. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/crud.py +0 -0
  35. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/engine.py +0 -0
  36. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/exceptions.py +0 -0
  37. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/models/__init__.py +0 -0
  38. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/models/base.py +0 -0
  39. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/pg_kit.py +0 -0
  40. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/query.py +0 -0
  41. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/session.py +0 -0
  42. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/sqlite_kit.py +0 -0
  43. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/db/transaction.py +0 -0
  44. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/docx_kit.py +0 -0
  45. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/file_kit.py +0 -0
  46. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/laya_kit.py +0 -0
  47. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/marker_kit.py +0 -0
  48. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/md2doc_kit.py +0 -0
  49. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/mineru_kit.py +0 -0
  50. {sdpy_kit-0.3.0 → sdpy_kit-0.4.0}/kit/redis_kit.py +0 -0
@@ -55,12 +55,10 @@ target/
55
55
  Thumbs.db
56
56
  Servers
57
57
  .metadata
58
- # 根目录 data/ 运行时数据(zvec 向量库、SQLite 测试库等,不入库)
58
+ # 根目录 data/ 运行时数据(SQLite 测试库等,不入库)
59
59
  data/
60
60
  # AI 文生图 e2e 测试产物(生成的海报图片,不入库)
61
61
  tests/image/
62
- # AMiner 检索测试产物(md 报告,不入库)
63
- tests/aminer/
64
62
  # SQLite 测试库(运行时生成,不入库)
65
63
  metadata_log.db
66
64
  upload
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sdpy-kit
3
- Version: 0.3.0
4
- Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/MCP/Redis/文件/文档/向量工具集
3
+ Version: 0.4.0
4
+ Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
5
5
  Author-email: Clark <changhongyuan@126.com>
6
6
  License-Expression: MIT
7
- Keywords: ai,kit,mcp,mysql,postgresql,redis,sdpy,sqlalchemy,sqlite,sqlmodel,向量检索,工具集,开发工具,数据库
7
+ Keywords: ai,kit,mysql,postgresql,redis,sdpy,sqlalchemy,sqlite,sqlmodel,向量检索,工具集,开发工具,数据库
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: License :: OSI Approved :: MIT License
@@ -26,7 +26,6 @@ Requires-Dist: httpx>=0.28.1; extra == 'ai'
26
26
  Requires-Dist: python-dotenv>=1.2.4; extra == 'ai'
27
27
  Provides-Extra: all
28
28
  Requires-Dist: docxtpl>=0.20.2; extra == 'all'
29
- Requires-Dist: fastmcp>=4.1.0; extra == 'all'
30
29
  Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'all'
31
30
  Requires-Dist: httpx>=0.28.1; extra == 'all'
32
31
  Requires-Dist: huggingface-hub<2,>=1.5; extra == 'all'
@@ -42,7 +41,7 @@ Requires-Dist: python-dotenv>=1.2.4; extra == 'all'
42
41
  Requires-Dist: redis>=8.1.0; extra == 'all'
43
42
  Requires-Dist: requests>=2.34.2; extra == 'all'
44
43
  Requires-Dist: sqliteai-vector>=1.1.2; extra == 'all'
45
- Requires-Dist: zvec>=0.7.0; extra == 'all'
44
+ Requires-Dist: trafilatura>=2.3.1; extra == 'all'
46
45
  Provides-Extra: anydoc
47
46
  Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'anydoc'
48
47
  Requires-Dist: pikepdf>=10.17.0; extra == 'anydoc'
@@ -53,8 +52,6 @@ Requires-Dist: huggingface-hub<2,>=1.5; extra == 'laya'
53
52
  Requires-Dist: laya>=0.4.6; extra == 'laya'
54
53
  Provides-Extra: marker
55
54
  Requires-Dist: marker-pdf[full]>=2.0.0; extra == 'marker'
56
- Provides-Extra: mcp
57
- Requires-Dist: fastmcp>=4.1.0; extra == 'mcp'
58
55
  Provides-Extra: md2doc
59
56
  Requires-Dist: markdown-it-py>=4.2.0; extra == 'md2doc'
60
57
  Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'md2doc'
@@ -69,8 +66,8 @@ Requires-Dist: redis>=8.1.0; extra == 'redis'
69
66
  Provides-Extra: sqliteai
70
67
  Requires-Dist: numpy>=2.5.4; extra == 'sqliteai'
71
68
  Requires-Dist: sqliteai-vector>=1.1.2; extra == 'sqliteai'
72
- Provides-Extra: zvec
73
- Requires-Dist: zvec>=0.7.0; extra == 'zvec'
69
+ Provides-Extra: web-article
70
+ Requires-Dist: trafilatura>=2.3.1; extra == 'web-article'
74
71
  Description-Content-Type: text/markdown
75
72
 
76
73
  # sdpy-kit
@@ -79,8 +76,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
79
76
 
80
77
  - **统一数据库访问层**:PostgreSQL / MySQL / SQLite,三个 Kit API 完全一致,切换数据库只改一行 import;
81
78
  - **AI 多模型服务池**:chat / embedding / rerank / image 四类能力,多厂商候选链 + 故障自动转移;
82
- - **MCP 客户端**:streamable-http / sse 双传输,档案驱动多服务器;
83
- - **工具集**:Redis / 文件 / 雪花 ID / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(md + images) / 向量库 / 微信公众号文章抓取存储。
79
+ - **工具集**:Redis / 文件 / 雪花 ID / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(md + images) / Laya 类型化决策 / 微信公众号文章抓取存储 / 通用网页文章抓取存储。
84
80
 
85
81
  > 要求 Python 3.13+
86
82
 
@@ -91,7 +87,6 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
91
87
  | `kit.config` | `get_settings` / `get_default_logger` | 配置单例与统一日志 | 无 |
92
88
  | `kit.db` | `SQLiteKit` / `PgKit` / `MySqlKit` / `CRUDBase` | 统一 CRUD、原生 SQL、原生表操作、事务、表管理 | `pg` / `mysql` / `sqliteai` |
93
89
  | `kit.ai` | `AI`(同步门面)/ 4 个原生异步 Service | 对话 / 向量化 / 重排序 / 文生图 | `ai` |
94
- | `kit.mcp_kit` | `McpKit` | MCP 工具发现与调用 | `mcp` |
95
90
  | `kit.redis_kit` | `RedisKit` | Redis string / hash / list / set / zset | `redis` |
96
91
  | `kit.file_kit` | `FileKit` | 文件与目录读写、移动、遍历 | 无 |
97
92
  | `kit.common_kit` | `CommonKit` | 雪花 ID、时间戳 ID、时间格式化 | 无 |
@@ -100,8 +95,10 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
100
95
  | `kit.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
101
96
  | `kit.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
102
97
  | `kit.marker_kit` | `MarkerKit` | 本地文档解析(PDF / 图片 / Office → Markdown + Images) | `marker` |
103
- | `kit.zvec_kit` | `ZvecKit` | zvec 本地向量库 | `zvec` |
104
- | `kit.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
98
+ | `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
99
+ | `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(两 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
100
+ | `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
101
+ | `kit.crawl.web_article_kit` | `WebArticleKit` | 通用网页文章 → Markdown + 图片转存(**不含微信**,微信请用 `WxArticleKit`;明确支持头条) | `web-article` |
105
102
 
106
103
  ## 安装
107
104
 
@@ -118,15 +115,15 @@ uv add sdpy-kit
118
115
  | `pg` | PgKit(PostgreSQL 驱动;向量方法另需服务端 pgvector) | `pip install "sdpy-kit[pg]"` |
119
116
  | `mysql` | MySqlKit(MySQL 驱动;向量仅存储,需 MySQL 9.x) | `pip install "sdpy-kit[mysql]"` |
120
117
  | `sqliteai` | SQLiteKit 向量方法(numpy / sqliteai-vector,SQLite 向量扩展) | `pip install "sdpy-kit[sqliteai]"` |
121
- | `zvec` | ZvecKit(zvec,阿里独立向量库) | `pip install "sdpy-kit[zvec]"` |
122
118
  | `redis` | RedisKit | `pip install "sdpy-kit[redis]"` |
123
- | `mcp` | McpKit(fastmcp) | `pip install "sdpy-kit[mcp]"` |
124
119
  | `ai` | AI 服务池(httpx + python-dotenv) | `pip install "sdpy-kit[ai]"` |
125
120
  | `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2) | `pip install "sdpy-kit[md2doc]"` |
126
121
  | `anydoc` | AnyDocKit 文档转 Markdown(firecrawl-anydoc + pikepdf) | `pip install "sdpy-kit[anydoc]"` |
127
122
  | `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
128
123
  | `mineru` | MinerUKit 解析 API(精准解析 + Agent 轻量解析) | `pip install "sdpy-kit[mineru]"` |
129
124
  | `marker` | MarkerKit 本地文档解析(marker-pdf[full] → Markdown + Images,依赖体积大) | `pip install "sdpy-kit[marker]"` |
125
+ | `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / score / noul) | `pip install "sdpy-kit[laya]"` |
126
+ | `web-article` | WebArticleKit 通用网页文章抓取转存(trafilatura,纯 HTTP 无浏览器) | `pip install "sdpy-kit[web-article]"` |
130
127
  | `all` | 全部能力 | `pip install "sdpy-kit[all]"` |
131
128
 
132
129
  ## 配置(.env)
@@ -159,16 +156,11 @@ SQLITE_DB_PATH=./data/sqlite.db
159
156
  # REDIS_DB=0
160
157
  # REDIS_PASSWORD=
161
158
  # FILE_STORAGE_ROOT=./data
162
- # ZVEC_DATA_DIR=./data/zvec
163
159
 
164
160
  # AI:档案路径 + 你为各模型档案自定的密钥变量名(变量名在 ai_models.json 里声明)
165
161
  # AI_MODELS_CONFIG_PATH=./config/ai_models.json
166
162
  # SILICONFLOW_API_KEY=sk-xxxxxx
167
163
 
168
- # MCP:档案路径 + 你为各服务器自定的 token 变量名(变量名在 mcp_servers.json 里声明)
169
- # MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.json
170
- # AMINER_TOKEN=xxxxxx
171
-
172
164
  # MinerU(仅精准解析需要 Key,Agent 轻量解析免登录)
173
165
  # MINERU_API_KEY=
174
166
 
@@ -177,11 +169,17 @@ SQLITE_DB_PATH=./data/sqlite.db
177
169
  # MARKER_HF_ENDPOINT=https://hf-mirror.com
178
170
  # MARKER_HF_DISABLE_XET=true
179
171
  # MARKER_LLAMA_CPP_BINARY=
172
+
173
+ # Laya(类型化决策,可选依赖 sdpy-kit[laya])
174
+ # LAYA_MODEL=multilingual # multilingual / english / typed-decisions
175
+ # LAYA_DEVICE= # 留空由 laya 自动选择
176
+ # LAYA_HF_ENDPOINT=https://hf-mirror.com
177
+ # LAYA_HF_DISABLE_XET=true
180
178
  ```
181
179
 
182
180
  ### 环境变量 ↔ 能力对照表
183
181
 
184
- > 表中未出现的模块(`kit.common_kit`、`kit.docx_kit`、`kit.md2doc_kit`、`kit.wx_article_kit`)不读取任何环境变量。
182
+ > 表中未出现的模块(`kit.crawl.article_models`、`kit.common_kit`、`kit.docx_kit`、`kit.md2doc_kit`、`kit.crawl.wx_article_kit`、`kit.crawl.web_article_kit`)不读取任何环境变量。
185
183
 
186
184
  **基础与日志**(影响 `kit.config` 及所有模块的日志输出)
187
185
 
@@ -224,18 +222,6 @@ SQLITE_DB_PATH=./data/sqlite.db
224
222
  |----------|--------|------|
225
223
  | `FILE_STORAGE_ROOT` | `./data` | FileKit 全部相对路径的根目录。⚠ 该目录**必须已存在**,否则首次调用抛 `RuntimeError`(根路径不存在) |
226
224
 
227
- **向量库** → `kit.zvec_kit`
228
-
229
- | 环境变量 | 默认值 | 作用 |
230
- |----------|--------|------|
231
- | `ZVEC_DATA_DIR` | `./data/zvec` | Collection 根目录 |
232
- | `ZVEC_DEFAULT_METRIC` | `cosine` | 默认距离度量(`cosine` / `l2` / `ip`) |
233
- | `ZVEC_QUERY_THREADS` | `None`(自适应) | 查询线程数 |
234
- | `ZVEC_OPTIMIZE_THREADS` | `None`(自适应) | 索引构建线程数 |
235
- | `ZVEC_MEMORY_LIMIT_MB` | `None`(自适应) | 内存软上限 |
236
- | `ZVEC_LOG_DIR` | `./logs/zvec` | zvec 引擎独立日志目录 |
237
- | `ZVEC_LOG_LEVEL` | `warn` | zvec 引擎日志级别 |
238
-
239
225
  **AI** → `kit.ai`
240
226
 
241
227
  | 环境变量 | 默认值 | 作用 |
@@ -244,14 +230,6 @@ SQLITE_DB_PATH=./data/sqlite.db
244
230
  | `KIT_AI_CHAIN_TIMEOUT` | 未设置 | 全局整链预算(秒)= 门面同步等待上限。⚠ **不是 `Settings` 字段**,由代码直读 `os.environ`,无类型/范围校验,非法值仅告警后回退 |
245
231
  | *(密钥,变量名由档案决定)* | 无 | 名字写在 `ai_models.json` 各档案的 `api_key` 字段(如 `SILICONFLOW_API_KEY`),值放 `.env` 或系统环境变量。未配置的档案会被剔除出候选链;该类型全部未配置则调用失败 |
246
232
 
247
- **MCP** → `kit.mcp_kit`
248
-
249
- | 环境变量 | 默认值 | 作用 |
250
- |----------|--------|------|
251
- | `MCP_SERVERS_CONFIG_PATH` | `./config/mcp_servers.json` | 服务器档案位置 |
252
- | `MCP_CALL_TIMEOUT` | `60`(1–600) | 档案未声明 `timeout` 时的调用超时 |
253
- | *(令牌,变量名由档案决定)* | 无 | 名字写在 `mcp_servers.json` 各服务器的 `token_env` 字段(如 `AMINER_TOKEN`);档案声明了却两处都未配置时抛 `McpKitError` |
254
-
255
233
  **MinerU** → `kit.mineru_kit`
256
234
 
257
235
  | 环境变量 | 默认值 | 作用 |
@@ -274,6 +252,18 @@ SQLITE_DB_PATH=./data/sqlite.db
274
252
 
275
253
  > 这四项由 kit 在**导入 marker 之前**经 `os.environ.setdefault` 应用:仅当用户未显式设置对应环境变量时生效,不覆盖显式配置。
276
254
 
255
+ **Laya** → `kit.laya_kit`
256
+
257
+ | 环境变量 | 默认值 | 作用 |
258
+ |----------|--------|------|
259
+ | `LAYA_MODEL` | `multilingual` | 检查点:`multilingual`(多语言含中文)/ `english` / `typed-decisions` |
260
+ | `LAYA_DEVICE` | 无(不写入 config) | 推理设备(`cpu` / `cuda`);不配置则由 laya 自动选择 |
261
+ | `LAYA_HF_ENDPOINT` | `https://hf-mirror.com` | 检查点权重下载入口(国内镜像);置空则用 HuggingFace 官方源 |
262
+ | `LAYA_HF_DISABLE_XET` | `true` | 禁用 HF Xet 存储后端(国内镜像不支持,开启会导致权重下载挂起) |
263
+
264
+ > `LAYA_HF_*` 同样由 kit 在**导入 laya 之前**经 `os.environ.setdefault` 应用,不覆盖显式配置;
265
+ > `LAYA_MODEL` / `LAYA_DEVICE` 由 `Settings` 读取(进程内缓存,改后需重启进程)。
266
+
277
267
  **云 OCR**(任何 `ocr="hosted"` 的转换)→ `kit.anydoc_kit`
278
268
 
279
269
  | 环境变量 | 默认值 | 作用 |
@@ -287,7 +277,7 @@ SQLITE_DB_PATH=./data/sqlite.db
287
277
  |------|----------|----------|--------------|
288
278
  | ① Settings 变量 | 固定,kit 定义 | 上表中除 `KIT_AI_CHAIN_TIMEOUT` 与密钥类之外的全部 | pydantic-settings 读 `.env` 与真实环境变量(后者优先),带类型与范围校验,缺失用默认值 |
289
279
  | ② 直读变量 | 固定,kit 代码直接读 `os.environ` | `KIT_AI_CHAIN_TIMEOUT` | 无类型校验,非法值告警后回退 |
290
- | ③ 档案声明变量 | **动态**,名字写在 JSON 里 | AI 的 `api_key`、MCP 的 `token_env` | **名字进 JSON、值进 `.env`**——所以档案文件可以安全入库,密钥留在环境里 |
280
+ | ③ 档案声明变量 | **动态**,名字写在 JSON 里 | AI 的 `api_key` | **名字进 JSON、值进 `.env`**——所以档案文件可以安全入库,密钥留在环境里 |
291
281
  | ④ 第三方库变量 | 由依赖库定义 | `FIRECRAWL_API_KEY`、`FIRECRAWL_API_URL`(anydoc);`HF_ENDPOINT` / `HF_HUB_DISABLE_XET` / `LLAMA_CPP_BINARY`(marker) | anydoc:kit 只透传 `None`,由其自行回落;marker:kit 在导入前按 `MARKER_*` 配置做 `os.environ.setdefault`(不覆盖显式设置) |
292
282
 
293
283
  ### 密钥解析优先级
@@ -295,7 +285,6 @@ SQLITE_DB_PATH=./data/sqlite.db
295
285
  | 能力 | 解析顺序 | 全部缺失时 |
296
286
  |------|----------|------------|
297
287
  | AI | 真实环境变量 > 项目根 `.env`(首次加载档案时经 `load_dotenv(override=False)` 注入 `os.environ`) | 该档案被剔除出候选链;该类型无可用档案则调用报 `AIAggregateError` |
298
- | MCP | `os.environ` > 项目根 `.env`(kit 自行解析文件) | 抛 `McpKitError` |
299
288
  | MinerU | 仅 `Settings`(`.env` / 真实环境变量) | 精准解析接口抛 `MinerUError` |
300
289
 
301
290
  > AI 的 `load_dotenv(override=False)` 表示已存在的真实环境变量不会被 `.env` 覆盖——生产用容器 / 系统环境变量注入时,可以放心保留开发用的 `.env`。
@@ -304,13 +293,13 @@ SQLITE_DB_PATH=./data/sqlite.db
304
293
 
305
294
  | 改动内容 | 生效时机 |
306
295
  |----------|----------|
307
- | `LOG_*`、`DB_*`、`MYSQL_*`、`REDIS_*`、`FILE_STORAGE_ROOT`、`ZVEC_*`、`MINERU_*`、`MARKER_MODE` | `get_settings()` 带 `lru_cache`,进程内首次读取后固定 → **重启进程**(或调用 `AI.reload_config()`,它会顺带清空该缓存) |
296
+ | `LOG_*`、`DB_*`、`MYSQL_*`、`REDIS_*`、`FILE_STORAGE_ROOT`、`MINERU_*`、`MARKER_MODE` | `get_settings()` 带 `lru_cache`,进程内首次读取后固定 → **重启进程**(或调用 `AI.reload_config()`,它会顺带清空该缓存) |
308
297
  | `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
298
+ | `LAYA_HF_ENDPOINT` / `LAYA_HF_DISABLE_XET` | 仅在 `import kit.laya_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
309
299
  | AI 密钥写在 `.env` 里 | `.env` 只在加载档案时注入一次 → 重启,或 `AI.reload_config(reload_file=True)` |
310
300
  | AI 密钥由自己写入 `os.environ` | `AI.reload_config()`(默认 `reload_file=False`,只重读 `os.environ`) |
311
301
  | `ai_models.json`(增删档案、换 model) | `AI.reload_config(reload_file=True)` |
312
302
  | `KIT_AI_CHAIN_TIMEOUT` | 每次调用现读 `os.environ` → 改 `os.environ` 立即生效;写在 `.env` 里的新值要等下一次档案加载 |
313
- | MCP token | 每次调用现读 `os.environ` 并回退解析 `.env` → **改 `.env` 立即生效,无需重启** |
314
303
 
315
304
  ---
316
305
 
@@ -704,7 +693,7 @@ page = qb.paginate(page=1, per_page=20) # {"items", "total", ...}
704
693
  ## kit.ai —— AI 多模型服务池
705
694
 
706
695
  安装:`pip install "sdpy-kit[ai]"`
707
- 档案:项目根 `config/ai_models.json`(见文末「AI / MCP 档案文件」)
696
+ 档案:项目根 `config/ai_models.json`(见文末「AI 档案文件」)
708
697
 
709
698
  ### 1. 同步门面 `AI`(推荐)
710
699
 
@@ -784,46 +773,6 @@ from kit.ai.exceptions import (
784
773
  | `AIAggregateError` | 全部候选失败或整链预算耗尽;`tried`、`statuses`、`last_error`、`budget_exhausted` |
785
774
  | `AIResponseError` | 响应结构解析失败、流式数据无法解析 |
786
775
 
787
- ---
788
-
789
- ## kit.mcp_kit —— 通用 MCP 客户端
790
-
791
- 安装:`pip install "sdpy-kit[mcp]"`
792
- 档案:项目根 `config/mcp_servers.json`;token 值放 `.env`(档案里只写环境变量名)
793
-
794
- ```python
795
- from kit.mcp_kit import McpKit
796
-
797
- tools = McpKit.list_tools("aminer") # [{name, description}]
798
- result = McpKit.call_tool("search_paper_by_title", {"title": "水声通信", "size": 10})
799
- print(result.data, result.elapsed_ms)
800
-
801
- # 直连临时服务器(不经档案)
802
- r = McpKit.call_tool_url("https://example.com/mcp", "query", {}, token="...")
803
-
804
- # 异步环境 / 一次握手复用多次调用
805
- async with McpKit.session("aminer") as call:
806
- r1 = await call("search_paper_by_title", {"title": "A"})
807
- r2 = await call("get_papers_info", {"id": "1"})
808
- ```
809
-
810
- | 方法 | 说明 |
811
- |------|------|
812
- | `list_tools(server=None) -> list[dict]` | 工具发现;`server=None` 用档案 `default_server` |
813
- | `call_tool(name, arguments, *, server=None) -> McpToolResult` | 同步短连接调用(主路径) |
814
- | `call_tool_async(name, arguments, *, server=None) -> McpToolResult` | 异步版;FastAPI 等已有事件循环的进程内必须用 |
815
- | `session(server=None)` | `async with` 长连接复用,产出 `call(name, arguments)` 协程函数 |
816
- | `call_tool_url(url, name, arguments, *, token=None, transport="streamable-http", timeout=None) -> McpToolResult` | 直连临时服务器,不经档案 |
817
-
818
- | 类型 | 说明 |
819
- |------|------|
820
- | `McpToolResult(server, tool, elapsed_ms, data)` | 调用结果;`data` 为 JSON 解析后对象,非 JSON 文本降级为字符串 |
821
- | `McpKitError(message, *, detail=None)` | 业务失败时 `detail` 为服务端原始响应;连接失败时为最后一次传输层错误 |
822
-
823
- > 传输方式仅 `streamable-http`(默认)与 `sse`;档案配 `fallback_url` 时主端点传输层失败会自动回退(业务失败不回退)。
824
-
825
- ---
826
-
827
776
  ## kit.redis_kit —— Redis 工具
828
777
 
829
778
  安装:`pip install "sdpy-kit[redis]"`
@@ -1059,42 +1008,12 @@ MarkerKit.shutdown() # 释放模型与推理服务(幂等)
1059
1008
  > `mode` 缺省时不写入配置,交由 marker 按设备自动选择;首次使用会下载模型权重(默认走 `MARKER_HF_ENDPOINT` 镜像)。
1060
1009
  > 非 NVIDIA 平台完整 VLM 能力(公式 / 扫描页 OCR)需 llama.cpp 的 `llama-server`,否则用 `disable_ocr=True` 跳过。
1061
1010
 
1062
- ---
1063
-
1064
- ## kit.zvec_kit —— 本地向量库
1065
-
1066
- 安装:`pip install "sdpy-kit[zvec]"`
1067
-
1068
- ```python
1069
- from kit.zvec_kit import ZvecKit
1070
-
1071
- schema = ZvecKit.make_text_embedding_schema("docs", dim=768)
1072
- ZvecKit.create_collection("docs", schema)
1073
- ZvecKit.insert("docs", {"id": "d1", "vectors": {"embedding": [0.1] * 768}, "fields": {"year": 2024}})
1074
-
1075
- results = ZvecKit.vector_query("docs", field="embedding", vector=[0.3] * 768, topk=10)
1076
- ZvecKit.flush("docs")
1077
- ```
1078
-
1079
- | 分组 | 方法 |
1080
- |------|------|
1081
- | 连接 | `connect() -> bool`、`close() -> None`、`is_connected() -> bool` |
1082
- | Collection | `create_collection(name_or_path, schema) -> Collection`、`open_collection(name_or_path)`、`collection_exists(name_or_path) -> bool`、`list_collections() -> list[str]`、`destroy_collection(name_or_path) -> bool`、`get_schema(name_or_path)`、`get_stats(name_or_path) -> dict` |
1083
- | 文档 CRUD | `insert(name_or_path, data) -> str`、`insert_multi(name_or_path, data_list) -> list[str]`、`upsert(name_or_path, data) -> str`、`update(name_or_path, data) -> str`、`fetch(name_or_path, ids, *, include_vector=True) -> dict`、`delete(name_or_path, ids) -> bool`、`delete_by_filter(name_or_path, filter_expr) -> bool`、`count(name_or_path) -> int`、`flush(name_or_path) -> None` |
1084
- | 检索 / 维护 | `vector_query(name_or_path, *, field, vector, topk=10, filter_expr=None, include_vector=False) -> list[dict]`、`optimize(name_or_path) -> None` |
1085
- | Schema | `make_text_embedding_schema(name, dim, metric=None, extra_fields=None) -> CollectionSchema` |
1086
-
1087
- > 文档字典键:`id`(必需)、`vectors`(可选)、`fields`(可选);`filter_expr` 中相等用单等号 `=`。
1088
- > 异常:`ZvecError`(独立于 `kit.db` 的 `DBError` 体系)。
1089
-
1090
- ---
1091
-
1092
- ## kit.wx_article_kit —— 微信公众号文章抓取存储
1011
+ ## kit.crawl.wx_article_kit —— 微信公众号文章抓取存储
1093
1012
 
1094
1013
  零第三方依赖(标准库 `urllib` + 内置 HTML→Markdown 转换器),**无需 extra**,按需 import:
1095
1014
 
1096
1015
  ```python
1097
- from kit.wx_article_kit import WxArticleKit
1016
+ from kit.crawl.wx_article_kit import WxArticleKit
1098
1017
 
1099
1018
  # 抓取解析(不落盘)
1100
1019
  result = WxArticleKit.fetch("https://mp.weixin.qq.com/s/xxxx")
@@ -1118,7 +1037,7 @@ WxArticleKit.save(url, "./output", "12345", write_meta=True)
1118
1037
 
1119
1038
  ```
1120
1039
  <output_dir>/<file_name>/
1121
- ├── <file_name>.md # 正文 Markdown(YAML 头:title / author / publish_time / source_url)
1040
+ ├── <file_name>.md # 正文 Markdown(YAML 头:title / author / publish_time / source / source_url)
1122
1041
  ├── images/ # 图片转存(NNN.ext,正文引用已本地化)
1123
1042
  └── meta.json # 可选:结构化元信息(write_meta=True 时产出)
1124
1043
  ```
@@ -1127,19 +1046,72 @@ WxArticleKit.save(url, "./output", "12345", write_meta=True)
1127
1046
  `ArticleInfo.publish_time` 与 `meta.json` 内仍为 ISO 8601(结构化数据)。
1128
1047
  - 无登录态抓取:命中微信风控(环境异常 / 访问过于频繁)时返回 `ok=False` + 原因,不破解验证码;
1129
1048
  抓取或图片下载失败不抛异常,单张图片失败时正文保留原远程 URL。
1049
+ - 模型与 `WebArticleKit` 共用 `kit.crawl.article_models`(`ArticleInfo` 含 `source` / `site_host`);
1050
+ 失败时 `reason` 为类型化分类(微信常用 `risk_control` / `no_content` / `fetch_failed`)。
1130
1051
  - 同名子目录**覆盖写**(不追加序号);`file_name` 带 `.md` 后缀会自动去除,为空则抛 `ValueError`。
1131
1052
 
1132
1053
  ---
1133
1054
 
1134
- ## AI / MCP 档案文件
1055
+ ## kit.crawl.web_article_kit —— 通用网页文章抓取存储
1056
+
1057
+ 可选依赖(`trafilatura`),需安装 `pip install "sdpy-kit[web-article]"`,按需 import:
1058
+
1059
+ ```python
1060
+ from kit.crawl.web_article_kit import WebArticleKit
1061
+
1062
+ # 抓取解析(不落盘)
1063
+ result = WebArticleKit.fetch("https://blog.csdn.net/xxx/article/details/123")
1064
+ if result.ok:
1065
+ print(result.info.source, result.info.title, result.info.publish_time)
1066
+
1067
+ # 抓取并存储:<output_dir>/<file_name>/ 下 <file_name>.md + images/
1068
+ saved = WebArticleKit.save(url, output_dir="./output", file_name="12345")
1069
+ print(saved.md_path, saved.images_saved)
1070
+
1071
+ # 需要结构化元信息时再产出 meta.json(默认不产出)
1072
+ WebArticleKit.save(url, "./output", "12345", write_meta=True)
1073
+ ```
1074
+
1075
+ | 方法 | 说明 |
1076
+ |------|------|
1077
+ | `fetch(url, timeout=20.0) -> ArticleResult` | 抓取并解析正文(不落盘);结果对象字段 `ok` / `error` / `info`(`ArticleInfo`)/ `markdown` |
1078
+ | `save(url, output_dir, file_name, write_meta=False, timeout=20.0) -> SavedOutput` | 抓取并存储到 `<output_dir>/<file_name>/`;字段与 `WxArticleKit.save` 一致 |
1079
+
1080
+ 落盘结构与 `WxArticleKit` 完全一致(`<file_name>.md` + `images/`,`write_meta=True` 时多出 `meta.json`);
1081
+ `<file_name>.md` 的 YAML 头为 `title / author / publish_time / source / source_url`(`publish_time` 渲染为中文日期)。
1082
+
1083
+ **站点覆盖范围**(规则表只允许「转交」与「接口适配」两类条目,不做逐站 DOM 补丁):
1084
+
1085
+ | 站点类型 | 行为 |
1086
+ |---|---|
1087
+ | 通用静态 / 服务端渲染页面(博客、文档站、CSDN 等) | ✅ 直接抽取 |
1088
+ | 头条(文章页为 JS 空壳) | ✅ **明确支持**:走移动端 JSON 接口 |
1089
+ | 微信公众号 | ❌ **不覆盖**:返回提示,请改用 `kit.crawl.wx_article_kit.WxArticleKit`(该来源有专属解析,二者各自独立) |
1090
+ | 正文依赖 JavaScript 动态渲染或需登录态的站点 | ❌ 失败并给出**类型化说明**(不维护域名名单) |
1091
+
1092
+ `ArticleInfo` 为平台中立的通用字段集:`url` / `title` / `author` / `publish_time`(ISO 8601)/ `source`
1093
+ (来源平台名)/ `site_host` / `summary` / `images` / `links` / `code_blocks`,便于多平台抓取后写入同一张表;
1094
+ `to_dict()` 可直接 JSON 化(即 `meta.json` 的内容)。模型定义在 `kit.crawl.article_models`,与
1095
+ `WxArticleKit` **共用同一套**。
1096
+
1097
+ 质量信号(均不改变成功语义):
1098
+
1099
+ | 字段 | 取值 / 含义 |
1100
+ |---|---|
1101
+ | `reason` | 失败时的类型化分类:`unsupported_source` / `fetch_blocked` / `fetch_failed` / `no_content` / `risk_control`(成功为空串) |
1102
+ | `warnings` | **非致命**抽取自检提示(`tuple[str, ...]`):正文疑似过短、页面含图片但产物无图;同时写入 warning 日志 |
1103
+
1104
+ 标题统一去除站点后缀(如 CSDN 的 `标题-CSDN博客`);刻意不做「与页面 h1 择优」(h1 可能是导航文案)。
1105
+
1106
+ ---
1107
+
1108
+ ## AI 档案文件
1135
1109
 
1136
- AI 与 MCP 功能需要在项目根目录提供两个 JSON 档案文件:
1110
+ AI 功能需要在项目根目录提供 `config/ai_models.json`(chat / embedding / rerank / image 四类型多厂商模型档案)。
1137
1111
 
1138
- - `config/ai_models.json` —— chat / embedding / rerank / image 四类型多厂商模型档案
1139
- - `config/mcp_servers.json` —— MCP 服务器档案
1140
1112
 
1141
1113
  文件缺失时,首次使用相应功能会报错,**报错信息中附带可直接复制的最小模板**;
1142
- 其中 `api_key` / `token_env` 字段填写环境变量名,真实密钥配置在 `.env`。
1114
+ 其中 `api_key` 字段填写环境变量名,真实密钥配置在 `.env`。
1143
1115
 
1144
1116
  ### ai_models.json 可选字段
1145
1117
 
@@ -1159,22 +1131,6 @@ AI 与 MCP 功能需要在项目根目录提供两个 JSON 档案文件:
1159
1131
  全候选状态码一致时透传(如整链 429 可用 `exc.status_code == 429` 判定),
1160
1132
  429 的 `Retry-After` 解析后挂于 `exc.retry_after`(冷却钳制 5–300s)。
1161
1133
 
1162
- ### mcp_servers.json 最小模板
1163
-
1164
- ```json
1165
- {
1166
- "default_server": "aminer",
1167
- "servers": {
1168
- "aminer": {
1169
- "transport": "streamable-http",
1170
- "url": "https://mcp.aminer.cn/mcp",
1171
- "token_env": "AMINER_TOKEN",
1172
- "timeout": 60
1173
- }
1174
- }
1175
- }
1176
- ```
1177
-
1178
1134
  ## License
1179
1135
 
1180
1136
  MIT