sdpy-kit 0.6.11__tar.gz → 0.7.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.
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/PKG-INFO +34 -32
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/README.md +11 -8
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/__init__.py +1 -1
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/__init__.py +2 -1
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/chat_service.py +33 -16
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/failover.py +13 -5
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/image_service.py +18 -6
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/model_client.py +25 -5
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/pool.py +2 -2
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/rerank_service.py +22 -10
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/runner.py +40 -14
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/sdk_client.py +33 -27
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/__init__.py +1 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/pyproject.toml +8 -2
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/.gitignore +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/_retry_after.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/_service_base.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/embedding_service.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/profiles.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/rerank_transport.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/ai/types.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/common_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/config/kit_config.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/crawl/__init__.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/crawl/article_io.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/crawl/article_models.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/crawl/deepseek_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/crawl/web_article_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/crawl/wx_article_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/__init__.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/audit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/base_db_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/crud.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/engine.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/exceptions.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/mysql_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/pg_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/query.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/session.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/sqlite_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/decima_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/anydoc_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/docx_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/marker_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/md2doc_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/mineru_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/docs/rc_docparser_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/file_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/laya_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/license_kit.py +0 -0
- {sdpy_kit-0.6.11 → sdpy_kit-0.7.0}/kit/redis_kit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sdpy-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
|
|
5
5
|
Author-email: Clark <changhongyuan@126.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -26,27 +26,6 @@ Provides-Extra: ai
|
|
|
26
26
|
Requires-Dist: httpx>=0.28.1; extra == 'ai'
|
|
27
27
|
Requires-Dist: openai<4,>=3.44; extra == 'ai'
|
|
28
28
|
Requires-Dist: python-dotenv>=1.2.4; extra == 'ai'
|
|
29
|
-
Provides-Extra: all
|
|
30
|
-
Requires-Dist: decima-ai>=2.5.1; extra == 'all'
|
|
31
|
-
Requires-Dist: docxtpl>=0.20.2; extra == 'all'
|
|
32
|
-
Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'all'
|
|
33
|
-
Requires-Dist: httpx>=0.28.1; extra == 'all'
|
|
34
|
-
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'all'
|
|
35
|
-
Requires-Dist: laya>=0.4.22; extra == 'all'
|
|
36
|
-
Requires-Dist: markdown-it-py>=4.2.0; extra == 'all'
|
|
37
|
-
Requires-Dist: numpy>=2.5.4; extra == 'all'
|
|
38
|
-
Requires-Dist: onnxruntime>=1.18; extra == 'all'
|
|
39
|
-
Requires-Dist: openai<4,>=3.44; extra == 'all'
|
|
40
|
-
Requires-Dist: pikepdf>=10.17.0; extra == 'all'
|
|
41
|
-
Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'all'
|
|
42
|
-
Requires-Dist: psycopg2-binary>=2.9.13; extra == 'all'
|
|
43
|
-
Requires-Dist: pymysql>=1.2.3; extra == 'all'
|
|
44
|
-
Requires-Dist: python-dotenv>=1.2.4; extra == 'all'
|
|
45
|
-
Requires-Dist: rc-docparser>=0.2.2; extra == 'all'
|
|
46
|
-
Requires-Dist: redis>=8.1.0; extra == 'all'
|
|
47
|
-
Requires-Dist: requests>=2.34.2; extra == 'all'
|
|
48
|
-
Requires-Dist: sqliteai-vector>=1.2.0; extra == 'all'
|
|
49
|
-
Requires-Dist: trafilatura>=2.3.1; extra == 'all'
|
|
50
29
|
Provides-Extra: anydoc
|
|
51
30
|
Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'anydoc'
|
|
52
31
|
Requires-Dist: pikepdf>=10.17.0; extra == 'anydoc'
|
|
@@ -61,10 +40,9 @@ Requires-Dist: rc-docparser>=0.2.2; extra == 'docx-parse'
|
|
|
61
40
|
Provides-Extra: laya
|
|
62
41
|
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'laya'
|
|
63
42
|
Requires-Dist: laya>=0.4.22; extra == 'laya'
|
|
64
|
-
Provides-Extra: marker
|
|
65
|
-
Requires-Dist: marker-pdf>=2.0.0; extra == 'marker'
|
|
66
43
|
Provides-Extra: md2doc
|
|
67
44
|
Requires-Dist: markdown-it-py>=4.2.0; extra == 'md2doc'
|
|
45
|
+
Requires-Dist: pillow>=13.0.0; extra == 'md2doc'
|
|
68
46
|
Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'md2doc'
|
|
69
47
|
Provides-Extra: mineru
|
|
70
48
|
Requires-Dist: requests>=2.34.2; extra == 'mineru'
|
|
@@ -72,6 +50,27 @@ Provides-Extra: mysql
|
|
|
72
50
|
Requires-Dist: pymysql>=1.2.3; extra == 'mysql'
|
|
73
51
|
Provides-Extra: pg
|
|
74
52
|
Requires-Dist: psycopg2-binary>=2.9.13; extra == 'pg'
|
|
53
|
+
Provides-Extra: recommended
|
|
54
|
+
Requires-Dist: decima-ai>=2.5.1; extra == 'recommended'
|
|
55
|
+
Requires-Dist: docxtpl>=0.20.2; extra == 'recommended'
|
|
56
|
+
Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'recommended'
|
|
57
|
+
Requires-Dist: httpx>=0.28.1; extra == 'recommended'
|
|
58
|
+
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'recommended'
|
|
59
|
+
Requires-Dist: laya>=0.4.22; extra == 'recommended'
|
|
60
|
+
Requires-Dist: markdown-it-py>=4.2.0; extra == 'recommended'
|
|
61
|
+
Requires-Dist: numpy>=2.5.4; extra == 'recommended'
|
|
62
|
+
Requires-Dist: onnxruntime>=1.18; extra == 'recommended'
|
|
63
|
+
Requires-Dist: openai<4,>=3.44; extra == 'recommended'
|
|
64
|
+
Requires-Dist: pikepdf>=10.17.0; extra == 'recommended'
|
|
65
|
+
Requires-Dist: pimd[pdfa]>=2.2.5; extra == 'recommended'
|
|
66
|
+
Requires-Dist: psycopg2-binary>=2.9.13; extra == 'recommended'
|
|
67
|
+
Requires-Dist: pymysql>=1.2.3; extra == 'recommended'
|
|
68
|
+
Requires-Dist: python-dotenv>=1.2.4; extra == 'recommended'
|
|
69
|
+
Requires-Dist: rc-docparser>=0.2.2; extra == 'recommended'
|
|
70
|
+
Requires-Dist: redis>=8.1.0; extra == 'recommended'
|
|
71
|
+
Requires-Dist: requests>=2.34.2; extra == 'recommended'
|
|
72
|
+
Requires-Dist: sqliteai-vector>=1.2.0; extra == 'recommended'
|
|
73
|
+
Requires-Dist: trafilatura>=2.3.1; extra == 'recommended'
|
|
75
74
|
Provides-Extra: redis
|
|
76
75
|
Requires-Dist: redis>=8.1.0; extra == 'redis'
|
|
77
76
|
Provides-Extra: sqliteai
|
|
@@ -107,7 +106,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
107
106
|
| `kit.docs.rc_docparser_kit` | `RcDocParserKit` | 离线 docx → Markdown + Images(无模型) | `docx-parse` |
|
|
108
107
|
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML(含图片) | `md2doc` |
|
|
109
108
|
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
110
|
-
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images
|
|
109
|
+
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images);**0.7.0 起暂停发布**,需要请用 `sdpy-kit==0.6.10` | — |
|
|
111
110
|
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / multi_choice 多选 / score / noul,另含 decide 多字段 / decide_batch 批量 / predict_long 长文本 | `laya` |
|
|
112
111
|
| `kit.decima_kit` | `DecimaKit` | 决策模型:choice / score / noul / multi_choice(原生 rank);选项乱序不变、置信度可门控;另含 predict / predict_long / brief / choice_batch(方法名与 `LayaKit` 对齐) | `decima` |
|
|
113
112
|
| `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(三个 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
|
|
@@ -138,11 +137,10 @@ uv add sdpy-kit
|
|
|
138
137
|
| `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
|
|
139
138
|
| `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
|
|
140
139
|
| `mineru` | MinerUKit 解析 API(精准解析 + Agent 轻量解析) | `pip install "sdpy-kit[mineru]"` |
|
|
141
|
-
| `marker` | MarkerKit 本地解析(marker-pdf → Markdown + Images;PDF + 图片,依赖体积大)。**与 `[ai]` 互斥**:marker-pdf 锁 `openai<3`,不能与 `openai>=3.44` 同环境安装 | `pip install "sdpy-kit[marker]"` |
|
|
142
140
|
| `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / multi_choice / score / noul;支持多字段 / 批量 / 长文本) | `pip install "sdpy-kit[laya]"` |
|
|
143
141
|
| `decima` | DecimaKit 决策模型(decima-ai + onnxruntime,**无 torch**;选项乱序不变、置信度可门控;磁盘约 137 MB) | `pip install "sdpy-kit[decima]"` |
|
|
144
142
|
| `web-article` | WebArticleKit 通用网页文章抓取转存(trafilatura,纯 HTTP 无浏览器) | `pip install "sdpy-kit[web-article]"` |
|
|
145
|
-
| `
|
|
143
|
+
| `recommended` | 常用能力集合(DB / 缓存 / AI / 文档 / 抓取);不含重依赖(laya / decima)与 Marker(0.7.0 起暂停发布) | `pip install "sdpy-kit[recommended]"` |
|
|
146
144
|
|
|
147
145
|
## 配置(.env)
|
|
148
146
|
|
|
@@ -840,8 +838,8 @@ shutdown() # 应用退出前
|
|
|
840
838
|
| 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` |
|
|
841
839
|
| embedding | `embed_text(text) -> list[float]` | 单文本向量化 |
|
|
842
840
|
| embedding | `embed_texts(texts) -> list[list[float]]` | 批量向量化 |
|
|
843
|
-
| rerank | `rerank(query, documents, top_n=None) -> list[
|
|
844
|
-
| image | `generate_image(prompt, *, size=None, n=1) ->
|
|
841
|
+
| rerank | `rerank(query, documents, top_n=None) -> list[RerankHit]` | 返回 `RerankHit`(`index` / `score` / `document`)降序列表 |
|
|
842
|
+
| image | `generate_image(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` | 文生图,返回 `ImageResult`(`url` / `b64` 至少一个非空) |
|
|
845
843
|
| 管理 | `use(model_type, profile) -> None` | 切换该类型当前档案(`chat` / `embedding` / `rerank` / `image`) |
|
|
846
844
|
| 管理 | `available(model_type) -> list[ProfileStatus]` | 各档案状态快照(含未配置与被剔除者) |
|
|
847
845
|
| 管理 | `reload_config(*, model_type=None, reload_file=False, probe=True, immediate=False) -> ReloadResult` | 配置热重载,改密钥 / 档案后无需重启 |
|
|
@@ -965,7 +963,7 @@ if stats.truncated: # finish_reason 为 length / max_tokens
|
|
|
965
963
|
from kit.ai import (
|
|
966
964
|
ChatService, EmbeddingService, RerankService, ImageService, # 服务类
|
|
967
965
|
ChatRequest, EmbeddingRequest, RerankRequest, ImageRequest, # 请求模型
|
|
968
|
-
RerankHit, StreamStats, ModelBuilder,
|
|
966
|
+
RerankHit, ImageResult, StreamStats, ModelBuilder, # 结果类型 / 构建器
|
|
969
967
|
)
|
|
970
968
|
|
|
971
969
|
service = ModelBuilder.build_chat() # 老按类型构建:build_chat / build_embedding / build_rerank / build_image
|
|
@@ -978,7 +976,7 @@ await service.close()
|
|
|
978
976
|
| `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` 语义与门面一致 |
|
|
979
977
|
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
|
|
980
978
|
| `RerankService` | `rerank(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
|
|
981
|
-
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) ->
|
|
979
|
+
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` |
|
|
982
980
|
| `ModelBuilder` | `build(model_type, model=None, *, api_key=None)` 及各类型专用 `build_chat / build_embedding / build_rerank / build_image` |
|
|
983
981
|
| `BaseAIService` | `profile` 属性、`await close()`、支持 `async with` |
|
|
984
982
|
|
|
@@ -1278,7 +1276,11 @@ if r.state == "done":
|
|
|
1278
1276
|
|
|
1279
1277
|
## kit.docs.marker_kit —— 本地解析:PDF + 图片 → Markdown + Images
|
|
1280
1278
|
|
|
1281
|
-
|
|
1279
|
+
> ⚠ **0.7.0 起暂停发布**:`marker-pdf` 仍锁 `openai<3`,与 `ai` extra 冲突。本包**不包含**该模块,
|
|
1280
|
+
> 也不提供 `marker` extra。需要此能力请用 `pip install "sdpy-kit[marker]==0.6.10"`。
|
|
1281
|
+
> 以下接口说明仅作参考。
|
|
1282
|
+
|
|
1283
|
+
0.6.10 的安装方式:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf`,含 torch 等 80+ 包,体积较大)
|
|
1282
1284
|
|
|
1283
1285
|
支持面:`.pdf`、`.png`、`.jpg`、`.jpeg`、`.webp`、`.gif`、`.bmp`。docx/pptx/xlsx/html/epub 不在支持面
|
|
1284
1286
|
(marker 侧实现依赖 weasyprint/GTK),传入会抛 `ValueError`。
|
|
@@ -24,7 +24,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
24
24
|
| `kit.docs.rc_docparser_kit` | `RcDocParserKit` | 离线 docx → Markdown + Images(无模型) | `docx-parse` |
|
|
25
25
|
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML(含图片) | `md2doc` |
|
|
26
26
|
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
27
|
-
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images
|
|
27
|
+
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images);**0.7.0 起暂停发布**,需要请用 `sdpy-kit==0.6.10` | — |
|
|
28
28
|
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / multi_choice 多选 / score / noul,另含 decide 多字段 / decide_batch 批量 / predict_long 长文本 | `laya` |
|
|
29
29
|
| `kit.decima_kit` | `DecimaKit` | 决策模型:choice / score / noul / multi_choice(原生 rank);选项乱序不变、置信度可门控;另含 predict / predict_long / brief / choice_batch(方法名与 `LayaKit` 对齐) | `decima` |
|
|
30
30
|
| `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(三个 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
|
|
@@ -55,11 +55,10 @@ uv add sdpy-kit
|
|
|
55
55
|
| `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
|
|
56
56
|
| `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
|
|
57
57
|
| `mineru` | MinerUKit 解析 API(精准解析 + Agent 轻量解析) | `pip install "sdpy-kit[mineru]"` |
|
|
58
|
-
| `marker` | MarkerKit 本地解析(marker-pdf → Markdown + Images;PDF + 图片,依赖体积大)。**与 `[ai]` 互斥**:marker-pdf 锁 `openai<3`,不能与 `openai>=3.44` 同环境安装 | `pip install "sdpy-kit[marker]"` |
|
|
59
58
|
| `laya` | LayaKit 类型化决策(laya + torch,本地小模型 choice / multi_choice / score / noul;支持多字段 / 批量 / 长文本) | `pip install "sdpy-kit[laya]"` |
|
|
60
59
|
| `decima` | DecimaKit 决策模型(decima-ai + onnxruntime,**无 torch**;选项乱序不变、置信度可门控;磁盘约 137 MB) | `pip install "sdpy-kit[decima]"` |
|
|
61
60
|
| `web-article` | WebArticleKit 通用网页文章抓取转存(trafilatura,纯 HTTP 无浏览器) | `pip install "sdpy-kit[web-article]"` |
|
|
62
|
-
| `
|
|
61
|
+
| `recommended` | 常用能力集合(DB / 缓存 / AI / 文档 / 抓取);不含重依赖(laya / decima)与 Marker(0.7.0 起暂停发布) | `pip install "sdpy-kit[recommended]"` |
|
|
63
62
|
|
|
64
63
|
## 配置(.env)
|
|
65
64
|
|
|
@@ -757,8 +756,8 @@ shutdown() # 应用退出前
|
|
|
757
756
|
| 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` |
|
|
758
757
|
| embedding | `embed_text(text) -> list[float]` | 单文本向量化 |
|
|
759
758
|
| embedding | `embed_texts(texts) -> list[list[float]]` | 批量向量化 |
|
|
760
|
-
| rerank | `rerank(query, documents, top_n=None) -> list[
|
|
761
|
-
| image | `generate_image(prompt, *, size=None, n=1) ->
|
|
759
|
+
| rerank | `rerank(query, documents, top_n=None) -> list[RerankHit]` | 返回 `RerankHit`(`index` / `score` / `document`)降序列表 |
|
|
760
|
+
| image | `generate_image(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` | 文生图,返回 `ImageResult`(`url` / `b64` 至少一个非空) |
|
|
762
761
|
| 管理 | `use(model_type, profile) -> None` | 切换该类型当前档案(`chat` / `embedding` / `rerank` / `image`) |
|
|
763
762
|
| 管理 | `available(model_type) -> list[ProfileStatus]` | 各档案状态快照(含未配置与被剔除者) |
|
|
764
763
|
| 管理 | `reload_config(*, model_type=None, reload_file=False, probe=True, immediate=False) -> ReloadResult` | 配置热重载,改密钥 / 档案后无需重启 |
|
|
@@ -882,7 +881,7 @@ if stats.truncated: # finish_reason 为 length / max_tokens
|
|
|
882
881
|
from kit.ai import (
|
|
883
882
|
ChatService, EmbeddingService, RerankService, ImageService, # 服务类
|
|
884
883
|
ChatRequest, EmbeddingRequest, RerankRequest, ImageRequest, # 请求模型
|
|
885
|
-
RerankHit, StreamStats, ModelBuilder,
|
|
884
|
+
RerankHit, ImageResult, StreamStats, ModelBuilder, # 结果类型 / 构建器
|
|
886
885
|
)
|
|
887
886
|
|
|
888
887
|
service = ModelBuilder.build_chat() # 老按类型构建:build_chat / build_embedding / build_rerank / build_image
|
|
@@ -895,7 +894,7 @@ await service.close()
|
|
|
895
894
|
| `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` 语义与门面一致 |
|
|
896
895
|
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
|
|
897
896
|
| `RerankService` | `rerank(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
|
|
898
|
-
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) ->
|
|
897
|
+
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` |
|
|
899
898
|
| `ModelBuilder` | `build(model_type, model=None, *, api_key=None)` 及各类型专用 `build_chat / build_embedding / build_rerank / build_image` |
|
|
900
899
|
| `BaseAIService` | `profile` 属性、`await close()`、支持 `async with` |
|
|
901
900
|
|
|
@@ -1195,7 +1194,11 @@ if r.state == "done":
|
|
|
1195
1194
|
|
|
1196
1195
|
## kit.docs.marker_kit —— 本地解析:PDF + 图片 → Markdown + Images
|
|
1197
1196
|
|
|
1198
|
-
|
|
1197
|
+
> ⚠ **0.7.0 起暂停发布**:`marker-pdf` 仍锁 `openai<3`,与 `ai` extra 冲突。本包**不包含**该模块,
|
|
1198
|
+
> 也不提供 `marker` extra。需要此能力请用 `pip install "sdpy-kit[marker]==0.6.10"`。
|
|
1199
|
+
> 以下接口说明仅作参考。
|
|
1200
|
+
|
|
1201
|
+
0.6.10 的安装方式:`pip install "sdpy-kit[marker]"`(依赖 `marker-pdf`,含 torch 等 80+ 包,体积较大)
|
|
1199
1202
|
|
|
1200
1203
|
支持面:`.pdf`、`.png`、`.jpg`、`.jpeg`、`.webp`、`.gif`、`.bmp`。docx/pptx/xlsx/html/epub 不在支持面
|
|
1201
1204
|
(marker 侧实现依赖 weasyprint/GTK),传入会抛 `ValueError`。
|
|
@@ -29,7 +29,7 @@ Author: Clark Chang
|
|
|
29
29
|
|
|
30
30
|
from kit.ai.chat_service import ChatRequest, ChatService, StreamStats
|
|
31
31
|
from kit.ai.embedding_service import EmbeddingRequest, EmbeddingService
|
|
32
|
-
from kit.ai.image_service import ImageRequest, ImageService
|
|
32
|
+
from kit.ai.image_service import ImageRequest, ImageResult, ImageService
|
|
33
33
|
from kit.ai.model_builder import ModelBuilder
|
|
34
34
|
from kit.ai.rerank_service import RerankHit, RerankRequest, RerankService
|
|
35
35
|
|
|
@@ -39,6 +39,7 @@ __all__ = [
|
|
|
39
39
|
"EmbeddingRequest",
|
|
40
40
|
"EmbeddingService",
|
|
41
41
|
"ImageRequest",
|
|
42
|
+
"ImageResult",
|
|
42
43
|
"ImageService",
|
|
43
44
|
"ModelBuilder",
|
|
44
45
|
"RerankHit",
|
|
@@ -55,37 +55,51 @@ _WARNED: set[tuple[str, str]] = set()
|
|
|
55
55
|
_WARNED_LOCK: Final[threading.Lock] = threading.Lock()
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def _warn_once(
|
|
59
|
-
|
|
58
|
+
def _warn_once(
|
|
59
|
+
profile_name: str,
|
|
60
|
+
reason: str,
|
|
61
|
+
*,
|
|
62
|
+
kind: str = "思考开关",
|
|
63
|
+
level: int = logging.WARNING,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""线程安全的告警去重:``(档案名, 原因)`` 只报一次(可指定级别)。"""
|
|
60
66
|
key = (profile_name, reason)
|
|
61
67
|
with _WARNED_LOCK:
|
|
62
68
|
if key in _WARNED:
|
|
63
69
|
return
|
|
64
70
|
_WARNED.add(key)
|
|
65
|
-
logger.
|
|
71
|
+
logger.log(level, f"{kind}:{reason}(档案 {profile_name})")
|
|
66
72
|
|
|
67
73
|
|
|
68
74
|
def _resolve_thinking(
|
|
69
75
|
profile: ModelProfile, thinking: bool | None
|
|
70
|
-
) -> tuple[object, bool | None, str | None]:
|
|
76
|
+
) -> tuple[object, bool | None, str | None, int]:
|
|
71
77
|
"""解析本次调用的思考开关。
|
|
72
78
|
|
|
73
79
|
逐调用 ``thinking`` 优先,未传时回落档案 ``thinking.default``(三态)。
|
|
74
80
|
|
|
75
81
|
Returns:
|
|
76
|
-
``(注入值 | _UNSET, 是否生效,
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
``(注入值 | _UNSET, 是否生效, 提示语, 日志级别)``。
|
|
83
|
+
是否生效为 None 表示本次未对请求体做思考相关注入(未要求,或档案
|
|
84
|
+
只声明了 ``off`` 而没声明 ``on``——按厂商默认处理);False 表示
|
|
85
|
+
要求了但档案完全没有 ``thinking`` 声明(不支持、不注入、不报错、
|
|
86
|
+
不影响候选链冷却)。
|
|
79
87
|
"""
|
|
80
88
|
spec = profile.thinking
|
|
81
89
|
mode = thinking if thinking is not None else (spec.default if spec else None)
|
|
82
90
|
if mode is None:
|
|
83
|
-
return _UNSET, None, None
|
|
91
|
+
return _UNSET, None, None, logging.WARNING
|
|
84
92
|
if spec is None:
|
|
85
|
-
return _UNSET, False, "档案未声明思考开关,本次请求被忽略"
|
|
93
|
+
return _UNSET, False, "档案未声明思考开关,本次请求被忽略", logging.WARNING
|
|
86
94
|
if mode and spec.on is None:
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
# 只声明 off:开启只能走厂商默认(主流模型默认开启),不算「未生效」
|
|
96
|
+
return (
|
|
97
|
+
_UNSET,
|
|
98
|
+
None,
|
|
99
|
+
"档案未声明开启取值,按厂商默认处理",
|
|
100
|
+
logging.INFO,
|
|
101
|
+
)
|
|
102
|
+
return (spec.off if mode is False else spec.on), True, None, logging.WARNING
|
|
89
103
|
|
|
90
104
|
|
|
91
105
|
@dataclass
|
|
@@ -106,8 +120,9 @@ class StreamStats:
|
|
|
106
120
|
content_chars: 已产出正文字符数(等于调用方收到的字符数)
|
|
107
121
|
finish_reason: 结束原因(stop / length / content_filter / …);
|
|
108
122
|
provider 未给出时为 None
|
|
109
|
-
thinking_applied: 思考开关是否生效——None
|
|
110
|
-
|
|
123
|
+
thinking_applied: 思考开关是否生效——None 本次未对请求体做思考注入
|
|
124
|
+
(未要求,或档案只声明 off 未声明 on,按厂商默认处理)/
|
|
125
|
+
True 已按档案声明注入 / False 要求了但档案完全没有 thinking 声明
|
|
111
126
|
json_mode_applied: 结构化输出是否生效——None 本次未要求注入 /
|
|
112
127
|
True 已注入 / False 要求了但档案声明不支持(已忽略)
|
|
113
128
|
"""
|
|
@@ -206,11 +221,13 @@ class ChatService(SdkServiceBase):
|
|
|
206
221
|
if key not in _RESERVED_PAYLOAD_KEYS
|
|
207
222
|
}
|
|
208
223
|
|
|
209
|
-
thinking_value, thinking_applied, thinking_reason =
|
|
210
|
-
self._profile, request.thinking
|
|
224
|
+
thinking_value, thinking_applied, thinking_reason, thinking_level = (
|
|
225
|
+
_resolve_thinking(self._profile, request.thinking)
|
|
211
226
|
)
|
|
212
227
|
if thinking_reason is not None:
|
|
213
|
-
_warn_once(
|
|
228
|
+
_warn_once(
|
|
229
|
+
self._profile.name, thinking_reason, level=thinking_level
|
|
230
|
+
)
|
|
214
231
|
if thinking_value is not _UNSET:
|
|
215
232
|
spec = self._profile.thinking
|
|
216
233
|
if spec is None: # 类型收窄:applied 为 True 时 spec 必非 None
|
|
@@ -40,7 +40,7 @@ from typing import Any, Final, TypeVar, cast
|
|
|
40
40
|
from kit.ai.chat_service import ChatService, StreamStats
|
|
41
41
|
from kit.ai.embedding_service import EmbeddingService
|
|
42
42
|
from kit.ai.exceptions import AIAggregateError, AIError, AIRequestError, AITimeoutError
|
|
43
|
-
from kit.ai.image_service import ImageService
|
|
43
|
+
from kit.ai.image_service import ImageResult, ImageService
|
|
44
44
|
from kit.ai.model_builder import Service
|
|
45
45
|
from kit.ai.pool import AIServicePool
|
|
46
46
|
from kit.ai.rerank_service import RerankHit, RerankService
|
|
@@ -422,11 +422,19 @@ class FailoverImageService(_FailoverBase):
|
|
|
422
422
|
*,
|
|
423
423
|
size: str | None = None,
|
|
424
424
|
n: int = 1,
|
|
425
|
+
return_base64: bool = False,
|
|
426
|
+
ratio: str | None = None,
|
|
425
427
|
deadline: float | None = None,
|
|
426
|
-
) ->
|
|
427
|
-
"""
|
|
428
|
+
) -> ImageResult:
|
|
429
|
+
"""文生图,返回 :class:`ImageResult`(链式故障切换)。"""
|
|
428
430
|
|
|
429
|
-
async def _run(service: Service) ->
|
|
430
|
-
return await cast(ImageService, service).generate(
|
|
431
|
+
async def _run(service: Service) -> ImageResult:
|
|
432
|
+
return await cast(ImageService, service).generate(
|
|
433
|
+
prompt,
|
|
434
|
+
size=size,
|
|
435
|
+
n=n,
|
|
436
|
+
return_base64=return_base64,
|
|
437
|
+
ratio=ratio,
|
|
438
|
+
)
|
|
431
439
|
|
|
432
440
|
return await self._attempt("图像生成", _run, deadline=deadline)
|
|
@@ -26,7 +26,19 @@ from typing import ClassVar
|
|
|
26
26
|
|
|
27
27
|
from kit.ai.sdk_client import SdkServiceBase
|
|
28
28
|
|
|
29
|
-
__all__ = ["ImageRequest", "ImageService"]
|
|
29
|
+
__all__ = ["ImageRequest", "ImageResult", "ImageService"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ImageResult:
|
|
34
|
+
"""图像生成结果。
|
|
35
|
+
|
|
36
|
+
``url`` 与 ``b64`` 至少一个非空;两者都有时都填充(不同厂商回退行为不同)。
|
|
37
|
+
需要二进制时:有 ``b64`` 直接解码,否则下载 ``url``。
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
url: str | None
|
|
41
|
+
b64: str | None
|
|
30
42
|
|
|
31
43
|
|
|
32
44
|
@dataclass(frozen=True)
|
|
@@ -50,8 +62,7 @@ class ImageService(SdkServiceBase):
|
|
|
50
62
|
"""图像生成服务(全异步)。
|
|
51
63
|
|
|
52
64
|
``n > 1`` 时仅返回第一张图片,调用方如需多图请自行扩展。
|
|
53
|
-
返回 ``
|
|
54
|
-
返回 Base64 数据,调用方按返回值是否以 ``http`` 开头区分形式。
|
|
65
|
+
返回 :class:`ImageResult`(``url`` / ``b64`` 至少一个非空)。
|
|
55
66
|
"""
|
|
56
67
|
|
|
57
68
|
_LABEL: ClassVar[str] = "图像生成模型"
|
|
@@ -64,8 +75,8 @@ class ImageService(SdkServiceBase):
|
|
|
64
75
|
n: int = 1,
|
|
65
76
|
return_base64: bool = False,
|
|
66
77
|
ratio: str | None = None,
|
|
67
|
-
) ->
|
|
68
|
-
"""
|
|
78
|
+
) -> ImageResult:
|
|
79
|
+
"""文生图,返回 :class:`ImageResult`。"""
|
|
69
80
|
extra: dict[str, object] = {}
|
|
70
81
|
if return_base64:
|
|
71
82
|
extra["return_base64"] = True
|
|
@@ -81,9 +92,10 @@ class ImageService(SdkServiceBase):
|
|
|
81
92
|
}
|
|
82
93
|
)
|
|
83
94
|
with self._log_call():
|
|
84
|
-
|
|
95
|
+
url, b64 = await self._client.image(
|
|
85
96
|
prompt,
|
|
86
97
|
size=size,
|
|
87
98
|
n=n,
|
|
88
99
|
extra_body=extra or None,
|
|
89
100
|
)
|
|
101
|
+
return ImageResult(url=url, b64=b64)
|
|
@@ -18,23 +18,31 @@ Author: Clark Chang
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
# date: 2026-11-04
|
|
21
|
-
"""模型档案轻探测客户端(0.
|
|
21
|
+
"""模型档案轻探测客户端(0.7.0 起更名 ``ProbeClient``)。
|
|
22
22
|
|
|
23
23
|
破坏性变更:``post_json`` / ``stream_json`` 已删除——请求发送改由
|
|
24
24
|
:class:`kit.ai.sdk_client.SdkClient` 承担,rerank 走
|
|
25
25
|
:class:`kit.ai.rerank_transport.RerankTransport`。本类只保留
|
|
26
26
|
``list_models()`` / ``close()``,供业务侧做「密钥是否有效」的连通性测试。
|
|
27
|
+
|
|
28
|
+
``ModelClient`` 作为弃用别名保留一个版本(访问时触发 ``DeprecationWarning``)。
|
|
27
29
|
"""
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
import warnings
|
|
32
|
+
|
|
30
33
|
from kit.ai.profiles import ModelProfile
|
|
34
|
+
from kit.ai.sdk_client import SdkClient
|
|
31
35
|
from kit.ai.types import JSONObject
|
|
32
36
|
|
|
33
|
-
__all__ = ["
|
|
37
|
+
__all__ = ["ProbeClient"]
|
|
34
38
|
|
|
35
39
|
|
|
36
|
-
class
|
|
37
|
-
"""单档案连通性探测客户端(全异步)。
|
|
40
|
+
class ProbeClient:
|
|
41
|
+
"""单档案连通性探测客户端(全异步)。
|
|
42
|
+
|
|
43
|
+
0.7.0 起由 ``ModelClient`` 改名:它只做 ``list_models`` 探活,
|
|
44
|
+
真正的传输在 :class:`kit.ai.sdk_client.SdkClient`。
|
|
45
|
+
"""
|
|
38
46
|
|
|
39
47
|
def __init__(self, profile: ModelProfile) -> None:
|
|
40
48
|
self._profile: ModelProfile = profile
|
|
@@ -57,3 +65,15 @@ class ModelClient:
|
|
|
57
65
|
|
|
58
66
|
async def close(self) -> None:
|
|
59
67
|
await self._client.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def __getattr__(name: str) -> object:
|
|
71
|
+
"""0.7.0 起 ``ModelClient`` 更名为 ``ProbeClient``:保留别名并告警。"""
|
|
72
|
+
if name == "ModelClient":
|
|
73
|
+
warnings.warn(
|
|
74
|
+
"ModelClient 已更名为 ProbeClient(0.7.0),请改用 ProbeClient",
|
|
75
|
+
DeprecationWarning,
|
|
76
|
+
stacklevel=2,
|
|
77
|
+
)
|
|
78
|
+
return ProbeClient
|
|
79
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -45,7 +45,7 @@ from kit.ai.embedding_service import EmbeddingService
|
|
|
45
45
|
from kit.ai.exceptions import AIConfigError, AIError, AIRequestError
|
|
46
46
|
from kit.ai.image_service import ImageService
|
|
47
47
|
from kit.ai.model_builder import Service
|
|
48
|
-
from kit.ai.model_client import
|
|
48
|
+
from kit.ai.model_client import ProbeClient
|
|
49
49
|
from kit.ai.profiles import (
|
|
50
50
|
ModelProfile,
|
|
51
51
|
ModelRegistry,
|
|
@@ -85,7 +85,7 @@ async def _probe_profile(profile: ModelProfile) -> None:
|
|
|
85
85
|
|
|
86
86
|
独立模块级函数便于测试替换。探测客户端用毕即关。
|
|
87
87
|
"""
|
|
88
|
-
client =
|
|
88
|
+
client = ProbeClient(profile)
|
|
89
89
|
try:
|
|
90
90
|
await client.list_models()
|
|
91
91
|
finally:
|
|
@@ -43,10 +43,15 @@ class RerankRequest:
|
|
|
43
43
|
|
|
44
44
|
@dataclass(frozen=True)
|
|
45
45
|
class RerankHit:
|
|
46
|
-
"""重排序单条命中结果。
|
|
46
|
+
"""重排序单条命中结果。
|
|
47
|
+
|
|
48
|
+
``index`` 保留是为了让调用方映射回自己的原始对象(如 dict 列表);
|
|
49
|
+
``document`` 是命中原文,只要文本时免去二次索引。
|
|
50
|
+
"""
|
|
47
51
|
|
|
48
52
|
index: int
|
|
49
53
|
score: float
|
|
54
|
+
document: str
|
|
50
55
|
|
|
51
56
|
|
|
52
57
|
class RerankService(LoggingServiceBase):
|
|
@@ -68,7 +73,9 @@ class RerankService(LoggingServiceBase):
|
|
|
68
73
|
payload["top_n"] = request.top_n
|
|
69
74
|
return cast(JSONObject, payload)
|
|
70
75
|
|
|
71
|
-
def _parse_response(
|
|
76
|
+
def _parse_response(
|
|
77
|
+
self, data: JSONObject, documents: list[str]
|
|
78
|
+
) -> list[RerankHit]:
|
|
72
79
|
items = safe_get(data, "results")
|
|
73
80
|
if not isinstance(items, list):
|
|
74
81
|
raise AIResponseError(f"重排序响应 results 必须是数组: {str(data)[:200]}")
|
|
@@ -80,7 +87,18 @@ class RerankService(LoggingServiceBase):
|
|
|
80
87
|
score = item.get("relevance_score")
|
|
81
88
|
if not isinstance(index, int) or not isinstance(score, (int, float)):
|
|
82
89
|
raise AIResponseError(f"重排序响应字段异常: {str(item)[:200]}")
|
|
83
|
-
|
|
90
|
+
# 先校验越界再构造:否则 documents[index] 会抛 IndexError
|
|
91
|
+
if index < 0 or index >= len(documents):
|
|
92
|
+
raise AIResponseError(
|
|
93
|
+
f"重排序 index 越界: {index} (文档数 {len(documents)})"
|
|
94
|
+
)
|
|
95
|
+
hits.append(
|
|
96
|
+
RerankHit(
|
|
97
|
+
index=index,
|
|
98
|
+
score=float(score),
|
|
99
|
+
document=documents[index],
|
|
100
|
+
)
|
|
101
|
+
)
|
|
84
102
|
hits.sort(key=lambda h: h.score, reverse=True)
|
|
85
103
|
return hits
|
|
86
104
|
|
|
@@ -103,13 +121,7 @@ class RerankService(LoggingServiceBase):
|
|
|
103
121
|
payload = self._build_payload(RerankRequest(query, documents, top_n=top_n))
|
|
104
122
|
with self._log_call():
|
|
105
123
|
data = await self._client.post_json(payload, deadline=deadline)
|
|
106
|
-
|
|
107
|
-
for hit in hits:
|
|
108
|
-
if hit.index < 0 or hit.index >= len(documents):
|
|
109
|
-
raise AIResponseError(
|
|
110
|
-
f"重排序 index 越界: {hit.index} (文档数 {len(documents)})"
|
|
111
|
-
)
|
|
112
|
-
return hits
|
|
124
|
+
return self._parse_response(data, documents)
|
|
113
125
|
|
|
114
126
|
async def close(self) -> None:
|
|
115
127
|
await self._client.close()
|
|
@@ -57,7 +57,7 @@ from __future__ import annotations
|
|
|
57
57
|
而非经本门面阻塞事件循环线程。
|
|
58
58
|
- ``chat_stream`` 提供同步流式迭代(每次取下一帧),适合同步线程内逐 token 消费。
|
|
59
59
|
- ``AI.use`` / ``AI.available`` 为进程内内存态(不持久化),进程重启后
|
|
60
|
-
回到
|
|
60
|
+
回到 JSON 声明顺序。
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
63
|
import asyncio
|
|
@@ -81,6 +81,7 @@ from kit.ai.failover import (
|
|
|
81
81
|
from kit.ai.model_builder import Service
|
|
82
82
|
from kit.ai.pool import AIServicePool
|
|
83
83
|
from kit.ai.profiles import ProfileStatus, get_model_registry
|
|
84
|
+
from kit.ai.image_service import ImageResult
|
|
84
85
|
from kit.ai.rerank_service import RerankHit
|
|
85
86
|
from kit.config.kit_config import get_settings
|
|
86
87
|
from kit.config.log_config import get_default_logger
|
|
@@ -545,27 +546,38 @@ async def _rerank(
|
|
|
545
546
|
top_n: int | None,
|
|
546
547
|
*,
|
|
547
548
|
deadline: float | None = None,
|
|
548
|
-
) -> list[
|
|
549
|
+
) -> list[RerankHit]:
|
|
550
|
+
"""对候选文档重排序,返回 ``RerankHit`` 降序列表。"""
|
|
549
551
|
if not documents:
|
|
550
552
|
return []
|
|
551
553
|
service: FailoverRerankService = cast(
|
|
552
554
|
FailoverRerankService,
|
|
553
555
|
await _background._get_service(_RERANK), # noqa: SLF001
|
|
554
556
|
)
|
|
555
|
-
|
|
556
|
-
query, documents, top_n=top_n, deadline=deadline
|
|
557
|
-
)
|
|
558
|
-
return [(hit.index, hit.score) for hit in hits]
|
|
557
|
+
return await service.rerank(query, documents, top_n=top_n, deadline=deadline)
|
|
559
558
|
|
|
560
559
|
|
|
561
560
|
async def _generate_image(
|
|
562
|
-
prompt: str,
|
|
563
|
-
|
|
561
|
+
prompt: str,
|
|
562
|
+
*,
|
|
563
|
+
size: str | None,
|
|
564
|
+
n: int,
|
|
565
|
+
return_base64: bool = False,
|
|
566
|
+
ratio: str | None = None,
|
|
567
|
+
deadline: float | None = None,
|
|
568
|
+
) -> ImageResult:
|
|
564
569
|
service: FailoverImageService = cast(
|
|
565
570
|
FailoverImageService,
|
|
566
571
|
await _background._get_service(_IMAGE), # noqa: SLF001
|
|
567
572
|
)
|
|
568
|
-
return await service.generate(
|
|
573
|
+
return await service.generate(
|
|
574
|
+
prompt,
|
|
575
|
+
size=size,
|
|
576
|
+
n=n,
|
|
577
|
+
return_base64=return_base64,
|
|
578
|
+
ratio=ratio,
|
|
579
|
+
deadline=deadline,
|
|
580
|
+
)
|
|
569
581
|
|
|
570
582
|
|
|
571
583
|
class AI:
|
|
@@ -668,8 +680,8 @@ class AI:
|
|
|
668
680
|
@staticmethod
|
|
669
681
|
def rerank(
|
|
670
682
|
query: str, documents: list[str], top_n: int | None = None
|
|
671
|
-
) -> list[
|
|
672
|
-
"""对候选文档重排序,返回
|
|
683
|
+
) -> list[RerankHit]:
|
|
684
|
+
"""对候选文档重排序,返回 ``RerankHit`` 降序列表(原索引、分数、原文)。"""
|
|
673
685
|
timeout = _facade_timeout(_RERANK)
|
|
674
686
|
deadline = time.monotonic() + timeout
|
|
675
687
|
return _background.run(
|
|
@@ -678,12 +690,26 @@ class AI:
|
|
|
678
690
|
)
|
|
679
691
|
|
|
680
692
|
@staticmethod
|
|
681
|
-
def generate_image(
|
|
682
|
-
|
|
693
|
+
def generate_image(
|
|
694
|
+
prompt: str,
|
|
695
|
+
*,
|
|
696
|
+
size: str | None = None,
|
|
697
|
+
n: int = 1,
|
|
698
|
+
return_base64: bool = False,
|
|
699
|
+
ratio: str | None = None,
|
|
700
|
+
) -> ImageResult:
|
|
701
|
+
"""文生图,返回 :class:`ImageResult`(``url`` / ``b64`` 至少一个非空)。"""
|
|
683
702
|
timeout = _facade_timeout(_IMAGE)
|
|
684
703
|
deadline = time.monotonic() + timeout
|
|
685
704
|
return _background.run(
|
|
686
|
-
lambda: _generate_image(
|
|
705
|
+
lambda: _generate_image(
|
|
706
|
+
prompt,
|
|
707
|
+
size=size,
|
|
708
|
+
n=n,
|
|
709
|
+
return_base64=return_base64,
|
|
710
|
+
ratio=ratio,
|
|
711
|
+
deadline=deadline,
|
|
712
|
+
),
|
|
687
713
|
timeout=timeout,
|
|
688
714
|
)
|
|
689
715
|
|
|
@@ -262,21 +262,26 @@ class SdkClient:
|
|
|
262
262
|
except openai.OpenAIError as exc:
|
|
263
263
|
raise self._map_error(exc) from exc
|
|
264
264
|
try:
|
|
265
|
-
async
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
content if isinstance(content, str) else None,
|
|
277
|
-
reasoning or None,
|
|
278
|
-
finish if isinstance(finish, str) and finish else None,
|
|
265
|
+
# 用 async with 托管流:业务侧提前 break/取消时也能关闭连接,
|
|
266
|
+
# 否则只能等 GC,长跑进程会堆积半开连接
|
|
267
|
+
async with stream:
|
|
268
|
+
async for chunk in stream:
|
|
269
|
+
choices = getattr(chunk, "choices", None)
|
|
270
|
+
if not choices:
|
|
271
|
+
continue
|
|
272
|
+
choice = choices[0]
|
|
273
|
+
delta = getattr(choice, "delta", None)
|
|
274
|
+
content = (
|
|
275
|
+
getattr(delta, "content", None) if delta is not None else None
|
|
279
276
|
)
|
|
277
|
+
reasoning = _reasoning_of(delta) if delta is not None else ""
|
|
278
|
+
finish = getattr(choice, "finish_reason", None)
|
|
279
|
+
if content or reasoning or finish:
|
|
280
|
+
yield (
|
|
281
|
+
content if isinstance(content, str) else None,
|
|
282
|
+
reasoning or None,
|
|
283
|
+
finish if isinstance(finish, str) and finish else None,
|
|
284
|
+
)
|
|
280
285
|
except openai.OpenAIError as exc:
|
|
281
286
|
raise self._map_error(exc) from exc
|
|
282
287
|
|
|
@@ -312,8 +317,8 @@ class SdkClient:
|
|
|
312
317
|
n: int = 1,
|
|
313
318
|
extra_body: JSONObject | None = None,
|
|
314
319
|
deadline: float | None = None,
|
|
315
|
-
) -> str:
|
|
316
|
-
"""文生图,返回
|
|
320
|
+
) -> tuple[str | None, str | None]:
|
|
321
|
+
"""文生图,返回 ``(url, b64)``:两者可能同时有值,至少一个非空。"""
|
|
317
322
|
params: dict[str, Any] = {
|
|
318
323
|
"model": self._profile.model,
|
|
319
324
|
"prompt": prompt,
|
|
@@ -333,23 +338,24 @@ class SdkClient:
|
|
|
333
338
|
if not items:
|
|
334
339
|
raise AIResponseError("图像生成响应缺少 data")
|
|
335
340
|
first = items[0]
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
b64 =
|
|
340
|
-
if
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
raw_url = getattr(first, "url", None)
|
|
342
|
+
raw_b64 = getattr(first, "b64_json", None)
|
|
343
|
+
url = raw_url if isinstance(raw_url, str) and raw_url else None
|
|
344
|
+
b64 = raw_b64 if isinstance(raw_b64, str) and raw_b64 else None
|
|
345
|
+
if url is None and b64 is None:
|
|
346
|
+
raise AIResponseError("图像生成响应缺少 url/b64_json")
|
|
347
|
+
return url, b64
|
|
343
348
|
|
|
344
349
|
async def list_models(self) -> JSONObject:
|
|
345
350
|
"""轻探测 ``GET {base_url}/models``:仅验证密钥有效性。
|
|
346
351
|
|
|
347
352
|
独立 10 秒超时;返回值只表达成功(失败抛异常),统一返回空对象。
|
|
348
353
|
"""
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
354
|
+
# 探活不重试:失败即返回,避免「10s 超时 × (retries+1)」把配置页拖住
|
|
355
|
+
try:
|
|
356
|
+
await self._client.models.list(timeout=_LIST_MODELS_TIMEOUT)
|
|
357
|
+
except openai.OpenAIError as exc:
|
|
358
|
+
raise self._map_error(exc) from exc
|
|
353
359
|
return {}
|
|
354
360
|
|
|
355
361
|
async def close(self) -> None:
|
|
@@ -26,6 +26,7 @@ Author: Clark Chang
|
|
|
26
26
|
- docx_kit: DocxKit Word 模板动态填充(docxtpl,extra: docx)
|
|
27
27
|
- mineru_kit: MinerUKit MinerU 文档解析 API(requests,extra: mineru)
|
|
28
28
|
- marker_kit: MarkerKit PDF + 图片本地解析(marker-pdf,extra: marker)
|
|
29
|
+
⚠ 0.7.0 起**暂停发布**:不进 wheel、不可安装;需要请用 sdpy-kit==0.6.10
|
|
29
30
|
- rc_docparser_kit: RcDocParserKit 离线 docx 解析(rc-docparser,extra: docx-parse)
|
|
30
31
|
|
|
31
32
|
本 __init__ 不导入任何成员:五个模块均携带重型可选依赖,保持
|
|
@@ -74,6 +74,8 @@ ai = [
|
|
|
74
74
|
md2doc = [
|
|
75
75
|
"pimd[pdfa]>=2.2.5",
|
|
76
76
|
"markdown-it-py>=4.2.0",
|
|
77
|
+
# 0.7.0 起 marker 摘除后解除 <11 封顶,显式抬到最新
|
|
78
|
+
"pillow>=13.0.0",
|
|
77
79
|
]
|
|
78
80
|
anydoc = [
|
|
79
81
|
"firecrawl-anydoc>=0.2.4",
|
|
@@ -82,7 +84,6 @@ anydoc = [
|
|
|
82
84
|
docx = ["docxtpl>=0.20.2"]
|
|
83
85
|
docx-parse = ["rc-docparser>=0.2.2"]
|
|
84
86
|
mineru = ["requests>=2.34.2"]
|
|
85
|
-
marker = ["marker-pdf>=2.0.0"]
|
|
86
87
|
laya = [
|
|
87
88
|
"laya>=0.4.22",
|
|
88
89
|
"huggingface-hub>=1.5,<2",
|
|
@@ -95,7 +96,9 @@ decima = [
|
|
|
95
96
|
"huggingface-hub>=1.5,<2",
|
|
96
97
|
]
|
|
97
98
|
web-article = ["trafilatura>=2.3.1"]
|
|
98
|
-
all
|
|
99
|
+
# 0.7.0 起 all 更名为 recommended:只含「轻量、互不冲突、覆盖主场景」的 extras。
|
|
100
|
+
# 重依赖(laya / decima)与暂停发布的 marker 单独声明。
|
|
101
|
+
recommended = [
|
|
99
102
|
"psycopg2-binary>=2.9.13",
|
|
100
103
|
"pymysql>=1.2.3",
|
|
101
104
|
"numpy>=2.5.4",
|
|
@@ -122,6 +125,9 @@ path = "kit/__init__.py"
|
|
|
122
125
|
|
|
123
126
|
[tool.hatch.build.targets.wheel]
|
|
124
127
|
packages = ["kit"]
|
|
128
|
+
# 0.7.0 起暂停发布 MarkerKit(marker-pdf 仍锁 openai<3):源码保留在仓库,
|
|
129
|
+
# 但不进 wheel。恢复时删除本行并恢复 marker extra。
|
|
130
|
+
exclude = ["kit/docs/marker_kit.py"]
|
|
125
131
|
|
|
126
132
|
[tool.hatch.build.targets.sdist]
|
|
127
133
|
only-include = ["kit"]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|