sdpy-kit 0.6.7__tar.gz → 0.6.9__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.6.7 → sdpy_kit-0.6.9}/PKG-INFO +218 -14
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/README.md +209 -11
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/__init__.py +1 -1
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/base_service.py +36 -9
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/chat_service.py +156 -15
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/embedding_service.py +8 -1
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/failover.py +27 -3
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/pool.py +5 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/profiles.py +88 -2
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/runner.py +90 -11
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/config/kit_config.py +43 -0
- sdpy_kit-0.6.9/kit/decima_kit.py +853 -0
- sdpy_kit-0.6.9/kit/laya_kit.py +552 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/pyproject.toml +12 -2
- sdpy_kit-0.6.7/kit/laya_kit.py +0 -302
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/.gitignore +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/__init__.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/image_service.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/model_client.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/rerank_service.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/ai/types.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/common_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/crawl/__init__.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/crawl/article_io.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/crawl/article_models.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/crawl/deepseek_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/crawl/web_article_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/crawl/wx_article_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/__init__.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/audit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/base_db_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/crud.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/engine.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/exceptions.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/mysql_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/pg_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/query.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/session.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/sqlite_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/__init__.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/anydoc_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/docx_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/marker_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/md2doc_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/mineru_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/docs/rc_docparser_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/file_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/license_kit.py +0 -0
- {sdpy_kit-0.6.7 → sdpy_kit-0.6.9}/kit/redis_kit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sdpy-kit
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.9
|
|
4
4
|
Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
|
|
5
5
|
Author-email: Clark <changhongyuan@126.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -26,14 +26,16 @@ Provides-Extra: ai
|
|
|
26
26
|
Requires-Dist: httpx>=0.28.1; extra == 'ai'
|
|
27
27
|
Requires-Dist: python-dotenv>=1.2.4; extra == 'ai'
|
|
28
28
|
Provides-Extra: all
|
|
29
|
+
Requires-Dist: decima-ai>=2.5.1; extra == 'all'
|
|
29
30
|
Requires-Dist: docxtpl>=0.20.2; extra == 'all'
|
|
30
31
|
Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'all'
|
|
31
32
|
Requires-Dist: httpx>=0.28.1; extra == 'all'
|
|
32
33
|
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'all'
|
|
33
|
-
Requires-Dist: laya>=0.4.
|
|
34
|
+
Requires-Dist: laya>=0.4.21; extra == 'all'
|
|
34
35
|
Requires-Dist: markdown-it-py>=4.2.0; extra == 'all'
|
|
35
36
|
Requires-Dist: marker-pdf>=2.0.0; extra == 'all'
|
|
36
37
|
Requires-Dist: numpy>=2.5.4; extra == 'all'
|
|
38
|
+
Requires-Dist: onnxruntime>=1.18; extra == 'all'
|
|
37
39
|
Requires-Dist: pikepdf>=10.17.0; extra == 'all'
|
|
38
40
|
Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'all'
|
|
39
41
|
Requires-Dist: psycopg2-binary>=2.9.13; extra == 'all'
|
|
@@ -47,13 +49,17 @@ Requires-Dist: trafilatura>=2.3.1; extra == 'all'
|
|
|
47
49
|
Provides-Extra: anydoc
|
|
48
50
|
Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'anydoc'
|
|
49
51
|
Requires-Dist: pikepdf>=10.17.0; extra == 'anydoc'
|
|
52
|
+
Provides-Extra: decima
|
|
53
|
+
Requires-Dist: decima-ai>=2.5.1; extra == 'decima'
|
|
54
|
+
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'decima'
|
|
55
|
+
Requires-Dist: onnxruntime>=1.18; extra == 'decima'
|
|
50
56
|
Provides-Extra: docx
|
|
51
57
|
Requires-Dist: docxtpl>=0.20.2; extra == 'docx'
|
|
52
58
|
Provides-Extra: docx-parse
|
|
53
59
|
Requires-Dist: rc-docparser>=0.2.2; extra == 'docx-parse'
|
|
54
60
|
Provides-Extra: laya
|
|
55
61
|
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'laya'
|
|
56
|
-
Requires-Dist: laya>=0.4.
|
|
62
|
+
Requires-Dist: laya>=0.4.21; extra == 'laya'
|
|
57
63
|
Provides-Extra: marker
|
|
58
64
|
Requires-Dist: marker-pdf>=2.0.0; extra == 'marker'
|
|
59
65
|
Provides-Extra: md2doc
|
|
@@ -80,7 +86,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
80
86
|
|
|
81
87
|
- **统一数据库访问层**:PostgreSQL / MySQL / SQLite,三个 Kit API 完全一致,切换数据库只改一行 import;内置审计字段自动填充(`created_*` / `updated_*` / `deleted`)与软删除查询过滤;
|
|
82
88
|
- **AI 多模型服务池**:chat / embedding / rerank / image 四类能力,多厂商候选链 + 故障自动转移;
|
|
83
|
-
- **工具集**:Redis / 文件 / 雪花 ID / 离线授权签发与校验 / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(PDF + 图片 → md + images/) / 离线 docx 解析(md + images/) / Laya 类型化决策 / 微信公众号文章抓取存储 / 通用网页文章抓取存储 / DeepSeek 搜索与抓取。
|
|
89
|
+
- **工具集**:Redis / 文件 / 雪花 ID / 离线授权签发与校验 / 文档互转 / Word 模板填充 / MinerU 解析 / Marker 本地解析(PDF + 图片 → md + images/) / 离线 docx 解析(md + images/) / Laya 类型化决策 / Decima 决策模型(选项乱序不变、置信度可门控) / 微信公众号文章抓取存储 / 通用网页文章抓取存储 / DeepSeek 搜索与抓取。
|
|
84
90
|
|
|
85
91
|
> 要求 Python 3.13+
|
|
86
92
|
|
|
@@ -101,7 +107,8 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
101
107
|
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML(含图片) | `md2doc` |
|
|
102
108
|
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
103
109
|
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images) | `marker` |
|
|
104
|
-
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul
|
|
110
|
+
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / multi_choice 多选 / score / noul,另含 decide 多字段 / decide_batch 批量 / predict_long 长文本 | `laya` |
|
|
111
|
+
| `kit.decima_kit` | `DecimaKit` | 决策模型:choice / score / noul / multi_choice(原生 rank);选项乱序不变、置信度可门控;另含 predict / predict_long / brief / choice_batch(方法名与 `LayaKit` 对齐) | `decima` |
|
|
105
112
|
| `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(三个 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
|
|
106
113
|
| `kit.crawl.article_io` | `resolve_stem` / `save_article` 等 | 落盘公共层(目录命名 / 图片本地化 / YAML 头 / `meta.json`),三个 `*ArticleKit` 共用 | 无 |
|
|
107
114
|
| `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
|
|
@@ -131,7 +138,8 @@ uv add sdpy-kit
|
|
|
131
138
|
| `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
|
|
132
139
|
| `mineru` | MinerUKit 解析 API(精准解析 + Agent 轻量解析) | `pip install "sdpy-kit[mineru]"` |
|
|
133
140
|
| `marker` | MarkerKit 本地解析(marker-pdf → Markdown + Images;PDF + 图片,依赖体积大) | `pip install "sdpy-kit[marker]"` |
|
|
134
|
-
| `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / score / noul
|
|
141
|
+
| `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / multi_choice / score / noul;支持多字段 / 批量 / 长文本) | `pip install "sdpy-kit[laya]"` |
|
|
142
|
+
| `decima` | DecimaKit 决策模型(decima-ai + onnxruntime,**无 torch**;选项乱序不变、置信度可门控;磁盘约 137 MB) | `pip install "sdpy-kit[decima]"` |
|
|
135
143
|
| `web-article` | WebArticleKit 通用网页文章抓取转存(trafilatura,纯 HTTP 无浏览器) | `pip install "sdpy-kit[web-article]"` |
|
|
136
144
|
| `all` | 全部能力 | `pip install "sdpy-kit[all]"` |
|
|
137
145
|
|
|
@@ -184,6 +192,16 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
184
192
|
# LAYA_DEVICE= # 留空由 laya 自动选择
|
|
185
193
|
# LAYA_HF_ENDPOINT=https://hf-mirror.com
|
|
186
194
|
# LAYA_HF_DISABLE_XET=true
|
|
195
|
+
# LAYA_PRELOAD= # true 时只预热当前 LAYA_MODEL
|
|
196
|
+
# LAYA_MAX_LOADED= # 同时驻留检查点上限,留空用 laya 默认 2
|
|
197
|
+
|
|
198
|
+
# Decima(决策模型,可选依赖 sdpy-kit[decima])
|
|
199
|
+
# DECIMA_MODEL=amyrmahdy/decima-small
|
|
200
|
+
# DECIMA_PRECISION=int8 # int8(127 MB)/ fp32(489 MB)
|
|
201
|
+
# DECIMA_THREADS=1 # batch-1 单线程最快,不建议调大
|
|
202
|
+
# DECIMA_LANG=zh # 仅 fa/ar 有实质归一化
|
|
203
|
+
# DECIMA_HF_ENDPOINT=https://hf-mirror.com
|
|
204
|
+
# DECIMA_LOCAL_ONLY=false # true = 只用本地缓存,离线部署
|
|
187
205
|
```
|
|
188
206
|
|
|
189
207
|
### 环境变量 ↔ 能力对照表
|
|
@@ -269,9 +287,33 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
269
287
|
| `LAYA_DEVICE` | 无(不写入 config) | 推理设备(`cpu` / `cuda`);不配置则由 laya 自动选择 |
|
|
270
288
|
| `LAYA_HF_ENDPOINT` | `https://hf-mirror.com` | 检查点权重下载入口(国内镜像);置空则用 HuggingFace 官方源 |
|
|
271
289
|
| `LAYA_HF_DISABLE_XET` | `true` | 禁用 HF Xet 存储后端(国内镜像不支持,开启会导致权重下载挂起) |
|
|
290
|
+
| `LAYA_PRELOAD` | 无(不写入 config) | `true` 时创建 Router 后预热当前 `LAYA_MODEL`(只预热这一份) |
|
|
291
|
+
| `LAYA_MAX_LOADED` | 无(不写入 config) | Router 同时驻留的检查点上限;不配置则用 laya 默认 2 |
|
|
292
|
+
|
|
293
|
+
> 能力边界:`decide` 的 schema 仅支持 `enum` / `boolean` / 带 `minimum`+`maximum` 的数值字段
|
|
294
|
+
> (数组、嵌套对象、联合类型不支持);`decide` 中的 `score` 投影为最置信档位(argmax),
|
|
295
|
+
> 不是序数期望值;`decide` 返回 `DecisionResult` 的 7 个键,`min_confidence` 低于阈值时字段值为 `None`;
|
|
296
|
+
> 长文本用 `predict_long`,批量用 `decide_batch`(上游没有 `decide_long`)。
|
|
297
|
+
> 多选用 `multi_choice`:每个候选标签独立做 `noul` 判断,返回 0-N 个标签;
|
|
298
|
+
> `threshold`(默认 0.5)由调用方给定且未标定,可能出现 0 个或全部命中,请按 `probabilities` 复核。
|
|
272
299
|
|
|
273
300
|
> `LAYA_HF_*` 同样由 kit 在**导入 laya 之前**经 `os.environ.setdefault` 应用,不覆盖显式配置;
|
|
274
|
-
> `LAYA_MODEL` / `LAYA_DEVICE` 由 `Settings` 读取(进程内缓存,改后需重启进程)。
|
|
301
|
+
> `LAYA_MODEL` / `LAYA_DEVICE` / `LAYA_PRELOAD` / `LAYA_MAX_LOADED` 由 `Settings` 读取(进程内缓存,改后需重启进程)。
|
|
302
|
+
|
|
303
|
+
**Decima** → `kit.decima_kit`
|
|
304
|
+
|
|
305
|
+
| 环境变量 | 默认值 | 作用 |
|
|
306
|
+
|----------|--------|------|
|
|
307
|
+
| `DECIMA_MODEL` | `amyrmahdy/decima-small` | HF repo id 或本地 ONNX 导出目录 |
|
|
308
|
+
| `DECIMA_PRECISION` | `int8` | `int8`(127 MB,与 fp32 一致率 99.4%)/ `fp32`(489 MB) |
|
|
309
|
+
| `DECIMA_THREADS` | `1` | ONNX Runtime intra-op 线程数;batch-1 单线程最快 |
|
|
310
|
+
| `DECIMA_LANG` | `zh` | 传给 `Question.lang`;仅 fa/ar 有实质归一化 |
|
|
311
|
+
| `DECIMA_HF_ENDPOINT` | `https://hf-mirror.com` | 权重下载入口(国内镜像);置空则用官方源 |
|
|
312
|
+
| `DECIMA_LOCAL_ONLY` | `false` | `true` 时只用本地缓存、不联网 |
|
|
313
|
+
|
|
314
|
+
> `DECIMA_HF_ENDPOINT` 由 kit 在**导入 decima 之前**经 `os.environ.setdefault` 应用,不覆盖显式配置;
|
|
315
|
+
> `HF_HUB_DISABLE_XET=1` 由 kit 固定设置(国内镜像不支持 xet,会挂起)。
|
|
316
|
+
> 不占用上游环境变量 `DECIMA_MODELS`(短名→路径覆盖)与 `DECIMA_DECODE`(自动解码)。
|
|
275
317
|
|
|
276
318
|
**DeepSeek Search / Fetch** → `kit.crawl.deepseek_kit`
|
|
277
319
|
|
|
@@ -320,6 +362,7 @@ SQLITE_DB_PATH=./data/sqlite.db
|
|
|
320
362
|
| `LOG_*`、`DB_*`、`MYSQL_*`、`REDIS_*`、`FILE_STORAGE_ROOT`、`MINERU_*`、`MARKER_MODE` | `get_settings()` 带 `lru_cache`,进程内首次读取后固定 → **重启进程**(或调用 `AI.reload_config()`,它会顺带清空该缓存) |
|
|
321
363
|
| `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.docs.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
322
364
|
| `LAYA_HF_ENDPOINT` / `LAYA_HF_DISABLE_XET` | 仅在 `import kit.laya_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
365
|
+
| `DECIMA_HF_ENDPOINT` / `HF_HUB_DISABLE_XET` | 仅在 `import kit.decima_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
|
|
323
366
|
| AI 密钥写在 `.env` 里 | `.env` 只在加载档案时注入一次 → 重启,或 `AI.reload_config(reload_file=True)` |
|
|
324
367
|
| AI 密钥由自己写入 `os.environ` | `AI.reload_config()`(默认 `reload_file=False`,只重读 `os.environ`) |
|
|
325
368
|
| `ai_models.json`(增删档案、换 model) | `AI.reload_config(reload_file=True)` |
|
|
@@ -783,9 +826,9 @@ shutdown() # 应用退出前
|
|
|
783
826
|
|
|
784
827
|
| 分组 | 方法 | 说明 |
|
|
785
828
|
|------|------|------|
|
|
786
|
-
| chat | `chat_complete(prompt, *, max_tokens=None, thinking=None) -> str` | 单轮对话(仅 user 消息) |
|
|
787
|
-
| chat | `chat_ask(messages, *, max_tokens=None, thinking=None) -> str` | 多轮对话,`messages` 为 `[{"role": "user", "content": "..."}]` |
|
|
788
|
-
| chat | `chat_stream(messages, *, max_tokens=None, thinking=None) -> Iterator[str]` | 同步流式逐 token 迭代器(建议 `with` 使用);结束后可读 `reasoning_chars` / `content_chars` / `finish_reason` / `truncated` / `thinking_applied` |
|
|
829
|
+
| chat | `chat_complete(prompt, *, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats=None) -> str` | 单轮对话(仅 user 消息) |
|
|
830
|
+
| chat | `chat_ask(messages, *, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats=None) -> str` | 多轮对话,`messages` 为 `[{"role": "user", "content": "..."}]` |
|
|
831
|
+
| chat | `chat_stream(messages, *, max_tokens=None, thinking=None, json_mode=None, response_format=None) -> Iterator[str]` | 同步流式逐 token 迭代器(建议 `with` 使用);结束后可读 `reasoning_chars` / `content_chars` / `finish_reason` / `truncated` / `thinking_applied` / `json_mode_applied` |
|
|
789
832
|
| embedding | `embed_text(text) -> list[float]` | 单文本向量化 |
|
|
790
833
|
| embedding | `embed_texts(texts) -> list[list[float]]` | 批量向量化 |
|
|
791
834
|
| rerank | `rerank(query, documents, top_n=None) -> list[tuple[int, float]]` | 返回 `[(原索引, 相关分)]` 降序列表 |
|
|
@@ -804,9 +847,10 @@ with AI.chat_stream([{"role": "user", "content": "写一首短诗"}]) as stream:
|
|
|
804
847
|
|
|
805
848
|
#### 流式收尾统计(思考型模型必备)
|
|
806
849
|
|
|
807
|
-
|
|
808
|
-
`truncated` / `thinking_applied`),用于区分「模型真的没返回」与「输出预算被思考吃光」——思考型模型的推理
|
|
809
|
-
token 计入 `max_tokens`,长上下文 + 小预算时常出现「返回 200
|
|
850
|
+
迭代器在流结束后可读六个只读属性(`reasoning_chars` / `content_chars` / `finish_reason` /
|
|
851
|
+
`truncated` / `thinking_applied` / `json_mode_applied`),用于区分「模型真的没返回」与「输出预算被思考吃光」——思考型模型的推理
|
|
852
|
+
token 计入 `max_tokens`,长上下文 + 小预算时常出现「返回 200 但正文为空」(同一套字段也可用于非流式 `ask` / `complete`,
|
|
853
|
+
见下文「结构化输出」):
|
|
810
854
|
|
|
811
855
|
```python
|
|
812
856
|
with AI.chat_stream(messages, max_tokens=450) as stream:
|
|
@@ -852,6 +896,52 @@ kit 不认识任何厂商字段名,请求体字段由档案 `thinking` 声明
|
|
|
852
896
|
> 首帧等待语义:每候选 60 秒内**一帧都没有**(含推理帧)才切下一候选;思考阶段的
|
|
853
897
|
> 推理帧算活动并重置窗口,不会误判健康模型失败、也不会重放整段 prompt。
|
|
854
898
|
|
|
899
|
+
#### 结构化输出(JSON 模式,0.6.8)
|
|
900
|
+
|
|
901
|
+
只要「合法 JSON 对象」用 `json_mode=True`;要约束字段用协议的 `response_format` 原样透传:
|
|
902
|
+
|
|
903
|
+
```python
|
|
904
|
+
answer = AI.chat_ask(messages, json_mode=True) # 要求返回 JSON 对象
|
|
905
|
+
|
|
906
|
+
fmt = {
|
|
907
|
+
"type": "json_schema",
|
|
908
|
+
"json_schema": {
|
|
909
|
+
"name": "extract",
|
|
910
|
+
"strict": True,
|
|
911
|
+
"schema": {
|
|
912
|
+
"type": "object",
|
|
913
|
+
"properties": {"tags": {"type": "array", "items": {"type": "string"}}},
|
|
914
|
+
"required": ["tags"],
|
|
915
|
+
"additionalProperties": False,
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
}
|
|
919
|
+
answer = AI.chat_ask(messages, response_format=fmt) # 严格模式:schema 由你负责
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
- **缺省即注入(开箱即用)**:档案未声明 `structured_output` 时,`json_mode=True` 按 OpenAI 兼容默认写
|
|
923
|
+
`{"response_format": {"type": "json_object"}}`;**实测不支持该字段的端点必须在档案写
|
|
924
|
+
`"structured_output": false`**,否则会 400 并触发冷却。
|
|
925
|
+
- **kit 不校验 JSON**:`json_mode=True` 只是「要求」,返回可能仍是散文或被截断的 JSON;schema 的生成、
|
|
926
|
+
校验、修补都不在 kit(`response_format` 写错即端点 400)。
|
|
927
|
+
- **互斥**:`json_mode` 与 `response_format` 同时给 → `ValueError`;`json_mode=False` 与 `None` 等效(不注入)。
|
|
928
|
+
- **跨候选降级**:切到已写 `false` 的备胎时本次调用退回普通文本,只在日志与 `json_mode_applied` 中体现。
|
|
929
|
+
- **`stream=True` 兼容性未知**:部分端点会静默忽略 `response_format`,kit 不探测;流式 JSON 只能是半成品,kit 不做增量解析。
|
|
930
|
+
|
|
931
|
+
`json_mode_applied`(流式迭代器属性 / `StreamStats` 字段):`None` 未要求注入 / `True` 已注入 / `False` 要求了但该档案已关闭。
|
|
932
|
+
|
|
933
|
+
**非流式收尾统计**:`ask` / `complete` 也接受 `stats=StreamStats()`(类名沿用历史,流式与非流式共用同一累加器),
|
|
934
|
+
可读 `finish_reason` / `truncated` / `content_chars` / `reasoning_chars` / `json_mode_applied`:
|
|
935
|
+
|
|
936
|
+
```python
|
|
937
|
+
from kit.ai import StreamStats
|
|
938
|
+
|
|
939
|
+
stats = StreamStats()
|
|
940
|
+
body = AI.chat_ask(messages, json_mode=True, stats=stats)
|
|
941
|
+
if stats.truncated: # finish_reason 为 length / max_tokens
|
|
942
|
+
... # 半截 JSON:直接重试或调大 max_tokens,不要尝试修复
|
|
943
|
+
```
|
|
944
|
+
|
|
855
945
|
> **非流式(`ask` / `complete`)的同款诊断**:provider 在截断时可能完全不返回 `content` 键,
|
|
856
946
|
> 此时 kit 抛 `AIResponseError`,消息里带 `finish_reason` 与推理字符数(不再只报「content
|
|
857
947
|
> 类型异常」,可直接判断是否被思考吃光);正文为空串时返回 `""` 并打 `WARNING`。
|
|
@@ -876,7 +966,7 @@ await service.close()
|
|
|
876
966
|
|
|
877
967
|
| 类 | 方法 |
|
|
878
968
|
|----|------|
|
|
879
|
-
| `ChatService` | `ask(messages, *, temperature=None, max_tokens=None, thinking=None, deadline=None) -> str`、`complete(prompt, *, ...) -> str`、`stream(messages, *, ...,
|
|
969
|
+
| `ChatService` | `ask(messages, *, temperature=None, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats: StreamStats \| None = None, deadline=None) -> str`、`complete(prompt, *, ...) -> str`、`stream(messages, *, ..., stats: StreamStats \| None = None) -> AsyncIterator[str]`(传 `StreamStats()` 即可读收尾统计);`thinking` / `json_mode` / `response_format` 语义与门面一致 |
|
|
880
970
|
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
|
|
881
971
|
| `RerankService` | `rerank(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
|
|
882
972
|
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> str` |
|
|
@@ -1250,6 +1340,110 @@ if result.success:
|
|
|
1250
1340
|
> 前提:中文标题依赖 `w:outlineLvl` 或「标题 N」样式(都没有时退化为正文);合并单元格表格会重建为
|
|
1251
1341
|
> 等宽 GFM(覆盖位留空,不输出 HTML `<table>`);页眉页脚/脚注不提取。纯本地,无需模型或 GPU。
|
|
1252
1342
|
|
|
1343
|
+
## kit.decima_kit —— 选项乱序不变、置信度可门控的决策模型
|
|
1344
|
+
|
|
1345
|
+
122M 参数的 late-interaction 决策模型(int8 ONNX,单 CPU 线程,**不依赖 torch**)。
|
|
1346
|
+
每个选项独立编码、独立与 state 打分,因此**调换选项顺序不会改变答案**。
|
|
1347
|
+
|
|
1348
|
+
```bash
|
|
1349
|
+
pip install "sdpy-kit[decima]"
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
```python
|
|
1353
|
+
from kit.decima_kit import DecimaKit
|
|
1354
|
+
|
|
1355
|
+
state = "我们三月份被重复扣费了两次,请把重复的那笔退款。"
|
|
1356
|
+
|
|
1357
|
+
# 单选题:选项顺序不影响结果
|
|
1358
|
+
r = DecimaKit.choice(
|
|
1359
|
+
state,
|
|
1360
|
+
"这封来信应该由哪个部门处理?",
|
|
1361
|
+
{"账务": "账单、付款、退款", "技术": "故障、报错"},
|
|
1362
|
+
)
|
|
1363
|
+
r["decision"], r["answer_confidence"], r["probabilities"]
|
|
1364
|
+
|
|
1365
|
+
# 是否题 / 评分题:第一个参数是待判断文本,第二个是命题
|
|
1366
|
+
DecimaKit.noul(state, "客户是否明确要求退款?")
|
|
1367
|
+
DecimaKit.score(state, "有多紧急?", ["不紧急", "较快处理", "紧急"])
|
|
1368
|
+
|
|
1369
|
+
# 多选:原生 rank(每项独立 sigmoid),返回 0-N 个
|
|
1370
|
+
DecimaKit.multi_choice(state, "涉及哪些事项?", {"账务": "账单", "技术": "故障"})
|
|
1371
|
+
|
|
1372
|
+
# 批量 / 多问题 / 长文本(方法名与 LayaKit 对齐)
|
|
1373
|
+
DecimaKit.choice_batch(
|
|
1374
|
+
[state, "系统登录后一直报错。"], "哪个部门?", {"账务": "账单", "技术": "故障"}
|
|
1375
|
+
)
|
|
1376
|
+
raw = DecimaKit.predict(
|
|
1377
|
+
state,
|
|
1378
|
+
{"dept": {"type": "choice", "instructions": "哪个部门?",
|
|
1379
|
+
"criteria": {"账务": "账单", "技术": "故障"}}},
|
|
1380
|
+
)
|
|
1381
|
+
DecimaKit.brief(raw)
|
|
1382
|
+
DecimaKit.predict_long(state * 50, {"r": {"type": "noul", "instructions": "是否要求退款?"}})
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
**返回字段**(`answer_confidence` 与 `LayaKit` 同名同口径):
|
|
1386
|
+
|
|
1387
|
+
| 字段 | 含义 |
|
|
1388
|
+
|------|------|
|
|
1389
|
+
| `decision` | 选中标签 / 序数期望值 / P(true) |
|
|
1390
|
+
| `answer_confidence` | top-1 概率 `max(p)`;**与 laya 同名同口径,但在本 Kit 上已标定、可做门控** |
|
|
1391
|
+
| `confidence` | 上游归一化置信度 `(k·max(p)−1)/(k−1)`,0~1(本 Kit 特有) |
|
|
1392
|
+
| `top_prob` | 原始 top-1 概率 `max(p)`,与 `answer_confidence` 相同 |
|
|
1393
|
+
| `probabilities` | 完整分布(`score` 的键为档位文本) |
|
|
1394
|
+
| `abstained` | 仅当传了 `min_confidence` 才有布尔值,否则为 `None` |
|
|
1395
|
+
|
|
1396
|
+
**从 LayaKit 迁移**:`choice` / `score` / `noul` / `multi_choice` / `predict` / `predict_long` /
|
|
1397
|
+
`brief` / `router` / `unload` 同名同形,**只改 import 即可**;
|
|
1398
|
+
`decide`(schema 多字段)、`decide_batch`、`route` 是 laya 独有能力,本 Kit 不提供
|
|
1399
|
+
(`decide_batch` 显式抛 `NotImplementedError`,不会静默按单选跑)。
|
|
1400
|
+
|
|
1401
|
+
### 与 `LayaKit` 的取舍(同环境实测)
|
|
1402
|
+
|
|
1403
|
+
| 维度 | `LayaKit`(0.4.21) | `DecimaKit`(small 1.1) |
|
|
1404
|
+
|---|---|---|
|
|
1405
|
+
| 选项顺序导致的决策变化 | 中文工单 15.0%、文献 17% | **0%**(构造性) |
|
|
1406
|
+
| 门控覆盖率(放行准确率 ≥90%) | 25.0% | **52.5%** |
|
|
1407
|
+
| 中文工单分诊(40 条) | 0.700 | **0.750** |
|
|
1408
|
+
| 其中三个实质类(剔除「其他」,**推算**) | **0.867** | 0.667 |
|
|
1409
|
+
| JevBench 231 题 | 0.485 | **0.563** |
|
|
1410
|
+
| JevBench easy 48 题 | **0.896** | 0.812 |
|
|
1411
|
+
| JevBench hard 111 题 | 0.333 | **0.441** |
|
|
1412
|
+
| 磁盘(单检查点) | 617 MB | **137.5 MB** |
|
|
1413
|
+
| 依赖 | laya + torch | decima-ai + onnxruntime |
|
|
1414
|
+
|
|
1415
|
+
**怎么选**:要「动态选项 + 顺序稳定 + 低置信转人工」用 `DecimaKit`;要「固定标签 + 简单明确分诊 +
|
|
1416
|
+
多字段 schema / GPU / 语言自动路由」用 `LayaKit`。两者**并列存在**,不互相替代。
|
|
1417
|
+
(`unload()` 两边都不该当内存回收 API:laya 实测只回落 0.4%,decima 回落约 50% 但也不回基线。)
|
|
1418
|
+
|
|
1419
|
+
### 限制
|
|
1420
|
+
|
|
1421
|
+
- **不提供默认门控阈值**:阈值随选项数与业务分布变化(4 选项约 0.80、6 选项约 0.85、二选一约 0.54),
|
|
1422
|
+
必须用自有数据标定后传入 `min_confidence`。
|
|
1423
|
+
- **不适用于复杂推理**:JevBench 231 题 0.563,同基准 Jev 1.13.0 为 0.986;
|
|
1424
|
+
`multi_hop` 0.28、`trap` 0.12。多跳策略判断请用大模型。
|
|
1425
|
+
- **`score` 最弱**(上游自认 benchmark 0.447):只返回分布与期望值,不承诺准确性。
|
|
1426
|
+
- **`multi_choice` 的 0.5 阈值偏严**:`rank` 是每选项独立 sigmoid,概率普遍低于 laya 的 `noul`
|
|
1427
|
+
(实测「账务 0.49 / 技术 0.12」会返回空列表);先看 `probabilities` 再定 `threshold`。
|
|
1428
|
+
- **无 schema 多字段能力**:`decide` / `decide_batch` 留在 laya 侧。
|
|
1429
|
+
- **仅 CPU**;**无真实释放接口**:上游没有 `unload`,`onnxruntime` 的 `InferenceSession`
|
|
1430
|
+
也没有 `close()`,`unload()` 只能丢引用 + GC。实测 WorkingSet 882.7 → 440.2 MB(约 50%),
|
|
1431
|
+
不回基线,反复加载/释放以 14~29 MB/轮 累积——长驻进程建议只加载一次。
|
|
1432
|
+
- **窗口 512 token 且不可调**:超长文本截断,或传 `long="chunk"` / `predict_long` 分窗。
|
|
1433
|
+
分窗固定「512 token 窗口 + 64 token 重叠」,`predict_long` 的 `window` / `stride` /
|
|
1434
|
+
`batch_size` **只是为兼容 LayaKit 签名而接受,内部丢弃**(首次传参会告警一次);
|
|
1435
|
+
需要更细粒度扫描请自行切分文本后逐段调用。长文档优先只喂标题 + 摘要。
|
|
1436
|
+
- **倾向选兜底类**:业务有「其他 / 无法判定」类时表现正常(实测该类 10/10 全对),
|
|
1437
|
+
但代价是三个实质类低于 laya(账务 0.50 vs 0.80、销售 0.60 vs 0.80)——
|
|
1438
|
+
**业务没有兜底类时准确率会下降**。
|
|
1439
|
+
|
|
1440
|
+
> 实测数据来自 `research/decima/`(中文工单 40 条由 AI 标注、未人工复核,只作方向参考;
|
|
1441
|
+
> JevBench 为本轮独立复现,231 题实测 0.563 与模型卡自报 0.571 同量级)。
|
|
1442
|
+
|
|
1443
|
+
> **许可提示**:权重为 Apache-2.0,但 `decima-small` 训练数据含 CC BY-NC 数据集
|
|
1444
|
+
> (ANLI / XNLI / multilingual-NLI-26lang)。商用前请自行复核许可条款;纯商用数据版本为
|
|
1445
|
+
> `decima-base-clean`(体积与延迟更大,改 `DECIMA_MODEL` 后需重新验证效果)。
|
|
1446
|
+
|
|
1253
1447
|
## kit.crawl.wx_article_kit —— 微信公众号文章抓取存储
|
|
1254
1448
|
|
|
1255
1449
|
零第三方依赖(标准库 `urllib` + 内置 HTML→Markdown 转换器),**无需 extra**,按需 import:
|
|
@@ -1411,6 +1605,7 @@ AI 功能需要在项目根目录提供 `config/ai_models.json`(chat / embeddi
|
|
|
1411
1605
|
| 档案级 | `retries` | 5xx 重试次数(默认 2;超时不重试,候选链切换才是有效重试) |
|
|
1412
1606
|
| 档案级 | `extra_body` | 厂商专有请求体字段透传(如 GLM/Qwen 关闭思考的 `{"thinking": {"type": "disabled"}}`、`{"reasoning_effort": "low"}`);与服务构造的同名键冲突时**以服务的值为准**,故 `model` / `messages` / `stream` 等保留键不可覆盖;改名类需求(如 OpenAI o 系列的 `max_completion_tokens`)无法用它实现。逐调用 `thinking` 与之同键时**以逐调用为准**并打 `WARNING`。改文件后需 `reload_config(reload_file=True)` 生效 |
|
|
1413
1607
|
| 档案级 | `thinking` | 思考开关的注入声明(0.6.7):`{"arg": "thinking", "off": {"type": "disabled"}, "on": {"type": "enabled"}, "default": false}`。`arg` 是请求体顶层键名(不得占用 `model` / `messages` / `stream` / `max_tokens` / `temperature` 等保留键,配置期与运行期双重校验);`off` / `on` 可为**任意 JSON 值**(双方都是 dict 时浅合并,否则整键替换);`on` 缺省表示不能显式开启;`default` 为档案默认三态(`null` 不干预 / `false` 默认关 / `true` 默认开)。**未声明 = 不支持按调用切换**:逐调用 `thinking` 被忽略并告警(不报错、不影响候选链冷却)。kit 只做翻译,不含任何厂商名 |
|
|
1608
|
+
| 档案级 | `structured_output` | 结构化输出的注入声明(0.6.8):`{"arg": "response_format", "json_mode": {"type": "json_object"}}`。`arg` 是请求体顶层键名(不得占用保留键);`json_mode` 是 `json_mode=True` 时写入该键的取值。**未声明 = 用内置 OpenAI 兼容默认**(`response_format: {"type":"json_object"}`,开箱即用);`"structured_output": false` 表示该档案不支持(`json_mode=True` 被忽略 + 告警,不发任何字段)。逐调用 `response_format` 原样透传优先于本声明。kit 只做翻译,不含任何厂商名 |
|
|
1414
1609
|
| 类型级 | `chain_timeout` | 整链预算秒数(与 `default_profile` 同级);门面同步等待上限。未配置时按 `sum(候选 timeout) + 10s` 自适应,也可用环境变量 `KIT_AI_CHAIN_TIMEOUT` 全局覆盖 |
|
|
1415
1610
|
|
|
1416
1611
|
`thinking` 声明两种实测形态(按平台各自写入档案):
|
|
@@ -1424,10 +1619,19 @@ AI 功能需要在项目根目录提供 `config/ai_models.json`(chat / embeddi
|
|
|
1424
1619
|
- **强制思考模型**(如只接受 `low/high/max` 的平台)不写 `thinking` 声明,逐调用开关会被忽略并告警;
|
|
1425
1620
|
- 声明写错(`arg` 拼错、值形态不符)由厂商判定,可能 400 → 冷却;语义正确性由使用方的模型能力清单保证。
|
|
1426
1621
|
|
|
1622
|
+
`structured_output` 声明与关闭形态:
|
|
1623
|
+
|
|
1624
|
+
```json
|
|
1625
|
+
"structured_output": {"arg": "response_format", "json_mode": {"type": "json_object"}}
|
|
1626
|
+
"structured_output": false // 该端点实测不支持:json_mode=True 时忽略并告警
|
|
1627
|
+
```
|
|
1628
|
+
|
|
1427
1629
|
> **`extra_body` 的用法边界**:`extra_body` 是**档案级**的固有配置,无法按调用点切换。需要
|
|
1428
1630
|
> 「同一模型,长文开思考、短文关思考」并存时用档案 `thinking` 声明 + 逐调用 `thinking=False/True`
|
|
1429
1631
|
> (0.6.7 起);仅在无法用开关表达时才考虑复制档案 + `ModelBuilder.build_chat("<档案名>")`
|
|
1430
1632
|
> (门面侧 `AI.use("chat", "<档案名>")` 是进程级切换,非并发安全)。
|
|
1633
|
+
> 结构化输出同理:档案声明只给 `json_mode=True` 提供取值,按调用点变化的 schema 一律走
|
|
1634
|
+
> 逐调用 `response_format`(0.6.8 起)。
|
|
1431
1635
|
|
|
1432
1636
|
### 候选链切换语义(两类,勿混淆)
|
|
1433
1637
|
|