sdpy-kit 0.7.3__tar.gz → 0.7.5__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 (62) hide show
  1. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/PKG-INFO +39 -39
  2. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/README.md +1610 -1610
  3. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/__init__.py +98 -98
  4. sdpy_kit-0.7.5/kit/ai/__init__.py +89 -0
  5. sdpy_kit-0.7.5/kit/ai/_internal/__init__.py +27 -0
  6. {sdpy_kit-0.7.3/kit/ai → sdpy_kit-0.7.5/kit/ai/_internal}/failover.py +440 -440
  7. {sdpy_kit-0.7.3/kit/ai → sdpy_kit-0.7.5/kit/ai/_internal}/rerank_transport.py +137 -138
  8. {sdpy_kit-0.7.3/kit/ai → sdpy_kit-0.7.5/kit/ai/_internal}/sdk_client.py +383 -383
  9. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/chat_service.py +614 -615
  10. sdpy_kit-0.7.5/kit/ai/decision/__init__.py +57 -0
  11. sdpy_kit-0.7.3/kit/decima_kit.py → sdpy_kit-0.7.5/kit/ai/decision/decima.py +853 -853
  12. sdpy_kit-0.7.3/kit/laya_kit.py → sdpy_kit-0.7.5/kit/ai/decision/laya.py +552 -552
  13. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/embedding_service.py +206 -206
  14. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/image_service.py +101 -101
  15. sdpy_kit-0.7.5/kit/ai/messages.py +89 -0
  16. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/model_client.py +62 -62
  17. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/rerank_service.py +136 -136
  18. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/runner.py +1127 -1126
  19. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/pyproject.toml +3 -3
  20. sdpy_kit-0.7.3/kit/ai/__init__.py +0 -49
  21. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/.gitignore +0 -0
  22. {sdpy_kit-0.7.3/kit/ai → sdpy_kit-0.7.5/kit/ai/_internal}/_retry_after.py +0 -0
  23. {sdpy_kit-0.7.3/kit/ai → sdpy_kit-0.7.5/kit/ai/_internal}/_service_base.py +0 -0
  24. {sdpy_kit-0.7.3/kit/ai → sdpy_kit-0.7.5/kit/ai/_internal}/pool.py +0 -0
  25. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/exceptions.py +0 -0
  26. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/model_builder.py +0 -0
  27. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/profiles.py +0 -0
  28. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/ai/types.py +0 -0
  29. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/common_kit.py +0 -0
  30. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/config/__init__.py +0 -0
  31. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/config/kit_config.py +0 -0
  32. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/config/log_config.py +0 -0
  33. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/crawl/__init__.py +0 -0
  34. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/crawl/article_io.py +0 -0
  35. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/crawl/article_models.py +0 -0
  36. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/crawl/deepseek_kit.py +0 -0
  37. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/crawl/web_article_kit.py +0 -0
  38. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/crawl/wx_article_kit.py +0 -0
  39. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/__init__.py +0 -0
  40. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/audit.py +0 -0
  41. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/base_db_kit.py +0 -0
  42. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/crud.py +0 -0
  43. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/engine.py +0 -0
  44. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/exceptions.py +0 -0
  45. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/models/__init__.py +0 -0
  46. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/models/base.py +0 -0
  47. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/mysql_kit.py +0 -0
  48. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/pg_kit.py +0 -0
  49. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/query.py +0 -0
  50. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/session.py +0 -0
  51. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/sqlite_kit.py +0 -0
  52. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/db/transaction.py +0 -0
  53. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/__init__.py +0 -0
  54. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/anydoc_kit.py +0 -0
  55. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/docx_kit.py +0 -0
  56. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/marker_kit.py +0 -0
  57. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/md2doc_kit.py +0 -0
  58. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/mineru_kit.py +0 -0
  59. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/docs/rc_docparser_kit.py +0 -0
  60. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/file_kit.py +0 -0
  61. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/license_kit.py +0 -0
  62. {sdpy_kit-0.7.3 → sdpy_kit-0.7.5}/kit/redis_kit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sdpy-kit
3
- Version: 0.7.3
3
+ Version: 0.7.5
4
4
  Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
5
5
  Author-email: Clark <changhongyuan@126.com>
6
6
  License-Expression: MIT
@@ -107,8 +107,8 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
107
107
  | `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML(含图片) | `md2doc` |
108
108
  | `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
109
109
  | `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images);**0.7.0 起暂停发布**,需要请用 `sdpy-kit==0.6.10` | — |
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` |
110
+ | `kit.ai.decision.laya` | `LayaKit` | 类型化决策:choice / multi_choice 多选 / score / noul,另含 decide 多字段 / decide_batch 批量 / predict_long 长文本 | `laya` |
111
+ | `kit.ai.decision.decima` | `DecimaKit` | 决策模型:choice / score / noul / multi_choice(原生 rank);选项乱序不变、置信度可门控;另含 predict / predict_long / brief / choice_batch(方法名与 `LayaKit` 对齐) | `decima` |
112
112
  | `kit.crawl.article_models` | `ArticleInfo` / `ArticleResult` / `SavedOutput` 等 | 文章抓取通用模型(三个 `*ArticleKit` 共用,含 `reason` 分类与非致命 `warnings`) | 无 |
113
113
  | `kit.crawl.article_io` | `resolve_stem` / `save_article` 等 | 落盘公共层(目录命名 / 图片本地化 / YAML 头 / `meta.json`),三个 `*ArticleKit` 共用 | 无 |
114
114
  | `kit.crawl.wx_article_kit` | `WxArticleKit` | 微信公众号文章 → Markdown + 图片转存 | 无 |
@@ -180,7 +180,7 @@ SQLITE_DB_PATH=./data/sqlite.db
180
180
  # MinerU(仅精准解析需要 Key,Agent 轻量解析免登录)
181
181
  # MINERU_API_KEY=
182
182
 
183
- # Marker(本地解析,可选依赖 sdpy-kit[marker])
183
+ # Marker(0.7.0 起暂停发布,无 marker extra;需要请用 sdpy-kit==0.6.10)
184
184
  # MARKER_MODE=fast # None=按设备自动(GPU→balanced / CPU、MPS→fast)
185
185
  # MARKER_HF_ENDPOINT=https://hf-mirror.com
186
186
  # MARKER_HF_DISABLE_XET=true
@@ -278,7 +278,7 @@ SQLITE_DB_PATH=./data/sqlite.db
278
278
 
279
279
  > 这四项由 kit 在**导入 marker 之前**经 `os.environ.setdefault` 应用:仅当用户未显式设置对应环境变量时生效,不覆盖显式配置。
280
280
 
281
- **Laya** → `kit.laya_kit`
281
+ **Laya** → `kit.ai.decision.laya`
282
282
 
283
283
  | 环境变量 | 默认值 | 作用 |
284
284
  |----------|--------|------|
@@ -299,7 +299,7 @@ SQLITE_DB_PATH=./data/sqlite.db
299
299
  > `LAYA_HF_*` 同样由 kit 在**导入 laya 之前**经 `os.environ.setdefault` 应用,不覆盖显式配置;
300
300
  > `LAYA_MODEL` / `LAYA_DEVICE` / `LAYA_PRELOAD` / `LAYA_MAX_LOADED` 由 `Settings` 读取(进程内缓存,改后需重启进程)。
301
301
 
302
- **Decima** → `kit.decima_kit`
302
+ **Decima** → `kit.ai.decision.decima`
303
303
 
304
304
  | 环境变量 | 默认值 | 作用 |
305
305
  |----------|--------|------|
@@ -360,8 +360,8 @@ SQLITE_DB_PATH=./data/sqlite.db
360
360
  |----------|----------|
361
361
  | `LOG_*`、`DB_*`、`MYSQL_*`、`REDIS_*`、`FILE_STORAGE_ROOT`、`MINERU_*`、`MARKER_MODE` | `get_settings()` 带 `lru_cache`,进程内首次读取后固定 → **重启进程**(或调用 `AI.reload_config()`,它会顺带清空该缓存) |
362
362
  | `MARKER_HF_ENDPOINT` / `MARKER_HF_DISABLE_XET` / `MARKER_LLAMA_CPP_BINARY` | 仅在 `import kit.docs.marker_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
363
- | `LAYA_HF_ENDPOINT` / `LAYA_HF_DISABLE_XET` | 仅在 `import kit.laya_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
364
- | `DECIMA_HF_ENDPOINT` / `HF_HUB_DISABLE_XET` | 仅在 `import kit.decima_kit` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
363
+ | `LAYA_HF_ENDPOINT` / `LAYA_HF_DISABLE_XET` | 仅在 `import kit.ai.decision.laya` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
364
+ | `DECIMA_HF_ENDPOINT` / `HF_HUB_DISABLE_XET` | 仅在 `import kit.ai.decision.decima` 时经 `os.environ.setdefault` 应用 → 改动需**重启进程**重新导入模块 |
365
365
  | AI 密钥写在 `.env` 里 | `.env` 只在加载档案时注入一次 → 重启,或 `AI.reload_config(reload_file=True)` |
366
366
  | AI 密钥由自己写入 `os.environ` | `AI.reload_config()`(默认 `reload_file=False`,只重读 `os.environ`) |
367
367
  | `ai_models.json`(增删档案、换 model) | `AI.reload_config(reload_file=True)` |
@@ -812,7 +812,7 @@ page = qb.paginate(page=1, per_page=20) # {"items", "total", ...}
812
812
  传输:chat / embedding / image 走 `openai` SDK(`>=3.44,<4`,
813
813
  自带 `httpx2`),rerank 走 httpx 自研传输(SDK 无 `/rerank` 资源)。
814
814
  档案里的 `endpoint` **对前三类不再生效**——SDK 固定拼 `/chat/completions`、
815
- `/embeddings`、`/images/generations`,仅 rerank 与探活仍使用该字段。
815
+ `/embeddings`、`/images/generations`,仅 rerank 仍使用该字段(探活走 openai SDK 的 `GET {base_url}/models`,不读本字段)。
816
816
 
817
817
  > 安装提示:`openai` 3.x 会引入 `httpx2`,与 rerank 用的 `httpx` 并存(两个包名不同,不冲突)。
818
818
 
@@ -821,11 +821,11 @@ page = qb.paginate(page=1, per_page=20) # {"items", "total", ...}
821
821
  ### 1. 同步门面 `AI`(推荐)
822
822
 
823
823
  ```python
824
- from kit.ai.runner import AI, shutdown
824
+ from kit.ai import AI, shutdown
825
825
 
826
- text = AI.chat_complete("用一句话介绍 Python") # 单轮对话
826
+ text = AI.chat_once("用一句话介绍 Python") # 单轮对话
827
827
  hits = AI.rerank("水声通信", ["文档A", "文档B"], top_n=2) # 重排序
828
- url = AI.generate_image("一只在写代码的猫") # 文生图
828
+ url = AI.image("一只在写代码的猫") # 文生图
829
829
  AI.use("chat", "glm") # 切换当前档案(进程内长期生效)
830
830
  print(AI.available("chat")) # 查看各档案状态快照
831
831
  shutdown() # 应用退出前释放资源(幂等)
@@ -833,22 +833,22 @@ shutdown() # 应用退出前
833
833
 
834
834
  | 分组 | 方法 | 说明 |
835
835
  |------|------|------|
836
- | chat | `chat_complete(prompt, *, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats=None) -> str` | 单轮对话(仅 user 消息) |
837
- | chat | `chat_ask(messages, *, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats=None) -> str` | 多轮对话,`messages` 为 `[{"role": "user", "content": "..."}]` |
836
+ | chat | `chat_once(prompt, *, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats=None) -> str` | 单轮对话(仅 user 消息) |
837
+ | chat | `chat(messages, *, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats=None) -> str` | 多轮对话,`messages` 为 `[{"role": "user", "content": "..."}]` |
838
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` / `requested_max_tokens` / `truncated` / `empty_kind` / `thinking_applied` / `json_mode_applied` |
839
- | embedding | `embed_text(text) -> list[float]` | 单文本向量化 |
840
- | embedding | `embed_texts(texts) -> list[list[float]]` | 批量向量化 |
841
- | rerank | `rerank(query, documents, top_n=None) -> list[RerankHit]` | 返回 `RerankHit`(`index` / `score` / `document`)降序列表;**`top_n` 是返回条数上限,由 kit 本地裁剪**(服务端返回超量也按分数截断),`top_n=None` 返回全部命中 |
842
- | image | `generate_image(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` | 文生图,返回 `ImageResult`(`url` / `b64` 至少一个非空) |
839
+ | embedding | `create(text) -> list[float]` | 单文本向量化 |
840
+ | embedding | `create_batch(texts) -> list[list[float]]` | 批量向量化 |
841
+ | rerank | `rerank(query, documents, top_n=None) -> list[RerankHit]` | 返回 `RerankHit`(`index` / `score` / `document`)降序列表;**`top_n` 是返回条数上限,由 kit 本地裁剪**(服务端返回超量也按分数截断),`top_n=None` 返回全部命中,`top_n<=0` 直接返回空列表 |
842
+ | image | `image(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` | 文生图,返回 `ImageResult`(`url` / `b64` 至少一个非空) |
843
843
  | 管理 | `use(model_type, profile) -> None` | 切换该类型当前档案(`chat` / `embedding` / `rerank` / `image`) |
844
- | 管理 | `available(model_type) -> list[ProfileStatus]` | 各档案状态快照(含未配置密钥与被剔除者);**类型未在 `ai_models.json` 中配置时返回 `[]`**(不再抛异常),类型名非法仍抛 `AIConfigError` |
844
+ | 管理 | `available(model_type) -> list[ProfileStatus]` | 各档案状态快照(含未配置密钥与被剔除者);**类型未在 `ai_models.json` 中配置时返回 `[]`**(不再抛异常),类型名非法抛 `AIModelTypeNotConfiguredError`(`AIConfigError` 子类) |
845
845
  | 管理 | `reload_config(*, model_type=None, reload_file=False, probe=True, immediate=False) -> ReloadResult` | 配置热重载,改密钥 / 档案后无需重启;同时清空思考/结构化输出的告警去重表(修正档案后同一原因会重新提示) |
846
846
 
847
847
  **可用性查询(`available`)**:`available` 返回的是**状态快照**,不是「可用档案列表」。
848
848
  判断某类型是否已配置用 `if not AI.available(t)`,不要靠捕获异常:
849
849
 
850
850
  ```python
851
- from kit.ai.runner import AI
851
+ from kit.ai import AI
852
852
 
853
853
  profiles = AI.available("image") # ai_models.json 无 image 段 → []
854
854
  if not profiles:
@@ -860,7 +860,7 @@ current = next((p for p in profiles if p.current), None)
860
860
  > `AI.available(t)[0]` 在类型未配置时会抛 `IndexError`,请改用上面的判空写法。
861
861
 
862
862
  ```python
863
- from kit.ai.runner import AI
863
+ from kit.ai import AI
864
864
 
865
865
  with AI.chat_stream([{"role": "user", "content": "写一首短诗"}]) as stream:
866
866
  for token in stream: # 逐 token 消费
@@ -915,9 +915,9 @@ if not body:
915
915
  同一档案下按调用点切换思考(入库抽取关、章节生成开),不必复制档案:
916
916
 
917
917
  ```python
918
- AI.chat_ask(messages, thinking=False) # 关闭思考
919
- AI.chat_ask(messages, thinking=True) # 开启思考
920
- AI.chat_ask(messages) # None:不干预(按档案 thinking.default;未配置则请求体逐字节不变)
918
+ AI.chat(messages, thinking=False) # 关闭思考
919
+ AI.chat(messages, thinking=True) # 开启思考
920
+ AI.chat(messages) # None:不干预(按档案 thinking.default;未配置则请求体逐字节不变)
921
921
  ```
922
922
 
923
923
  kit 不认识任何厂商字段名,请求体字段由档案 `thinking` 声明翻译(见「ai_models.json 可选字段」)。
@@ -941,7 +941,7 @@ kit 不认识任何厂商字段名,请求体字段由档案 `thinking` 声明
941
941
  只要「合法 JSON 对象」用 `json_mode=True`;要约束字段用协议的 `response_format` 原样透传:
942
942
 
943
943
  ```python
944
- answer = AI.chat_ask(messages, json_mode=True) # 要求返回 JSON 对象
944
+ answer = AI.chat(messages, json_mode=True) # 要求返回 JSON 对象
945
945
 
946
946
  fmt = {
947
947
  "type": "json_schema",
@@ -956,7 +956,7 @@ fmt = {
956
956
  },
957
957
  },
958
958
  }
959
- answer = AI.chat_ask(messages, response_format=fmt) # 严格模式:schema 由你负责
959
+ answer = AI.chat(messages, response_format=fmt) # 严格模式:schema 由你负责
960
960
  ```
961
961
 
962
962
  - **缺省即注入(开箱即用)**:档案未声明 `structured_output` 时,`json_mode=True` 按 OpenAI 兼容默认写
@@ -977,7 +977,7 @@ answer = AI.chat_ask(messages, response_format=fmt) # 严格模式:schema
977
977
  from kit.ai import StreamStats
978
978
 
979
979
  stats = StreamStats()
980
- body = AI.chat_ask(messages, json_mode=True, stats=stats)
980
+ body = AI.chat(messages, json_mode=True, stats=stats)
981
981
  if stats.truncated: # finish_reason 为 length / max_tokens
982
982
  ... # 半截 JSON:直接重试或调大 max_tokens,不要尝试修复
983
983
  ```
@@ -995,23 +995,22 @@ if stats.truncated: # finish_reason 为 length / max_tokens
995
995
  ```python
996
996
  from kit.ai import (
997
997
  ChatService, EmbeddingService, RerankService, ImageService, # 服务类
998
- ChatRequest, EmbeddingRequest, RerankRequest, ImageRequest, # 请求模型
999
998
  RerankHit, ImageResult, StreamStats, ModelBuilder, # 结果类型 / 构建器
1000
999
  )
1001
1000
 
1002
1001
  service = ModelBuilder.build_chat() # 老按类型构建:build_chat / build_embedding / build_rerank / build_image
1003
- answer = await service.ask(messages) # 或 await service.complete(prompt)
1002
+ answer = await service.create(messages) # 或 await service.create_once(prompt)
1004
1003
  await service.close()
1005
1004
  ```
1006
1005
 
1007
1006
  | 类 | 方法 |
1008
1007
  |----|------|
1009
- | `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` 语义与门面一致 |
1010
- | `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
1011
- | `RerankService` | `rerank(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
1012
- | `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` |
1008
+ | `ChatService` | `create(messages, *, temperature=None, max_tokens=None, thinking=None, json_mode=None, response_format=None, stats: StreamStats \| None = None, deadline=None) -> str`、`create_once(prompt, *, ...) -> str`、`create_stream(messages, *, ..., stats: StreamStats \| None = None) -> AsyncIterator[str]`(传 `StreamStats()` 即可读收尾统计);`thinking` / `json_mode` / `response_format` 语义与门面一致 |
1009
+ | `EmbeddingService` | `create(text) -> list[float]`、`create_batch(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
1010
+ | `RerankService` | `create(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
1011
+ | `ImageService` | `create(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> ImageResult` |
1013
1012
  | `ModelBuilder` | `build(model_type, model=None, *, api_key=None)` 及各类型专用 `build_chat / build_embedding / build_rerank / build_image` |
1014
- | `BaseAIService` | `profile` 属性、`await close()`、支持 `async with` |
1013
+ | `SdkServiceBase` | `profile` 属性、`await close()`、支持 `async with` |
1015
1014
 
1016
1015
  **embedding 维度契约**:`ai_models.json` 里 embedding 档案的 `dimension` 是**目标向量维度**
1017
1016
  (不是模型原生维度),`EmbeddingService` 保证输出恒等于它:
@@ -1040,7 +1039,8 @@ from kit.ai.exceptions import (
1040
1039
  | 异常 | 触发场景 / 可用属性 |
1041
1040
  |------|---------------------|
1042
1041
  | `AIError` | 基类 |
1043
- | `AIConfigError` | 档案缺失或非法、档案不存在、密钥未配置、必填字段缺失 |
1042
+ | `AIConfigError` | 档案缺失或非法、档案不存在、密钥未配置、必填字段缺失、`default_profile` 未在 `profiles` 中定义(加载期校验) |
1043
+ | `AIModelTypeNotConfiguredError` | 模型类型名非法(合法类型但未配置返回 `[]` 不抛);`AIConfigError` 子类 |
1044
1044
  | `AIRequestError` | 单次请求失败;`status_code`、`retry_after`(429 时解析 `Retry-After`) |
1045
1045
  | `AITimeoutError` | 请求超时(继承 `AIRequestError`) |
1046
1046
  | `AIAggregateError` | 全部候选失败或整链预算耗尽;`tried`、`statuses`、`last_error`、`budget_exhausted` |
@@ -1384,7 +1384,7 @@ if result.success:
1384
1384
  > 前提:中文标题依赖 `w:outlineLvl` 或「标题 N」样式(都没有时退化为正文);合并单元格表格会重建为
1385
1385
  > 等宽 GFM(覆盖位留空,不输出 HTML `<table>`);页眉页脚/脚注不提取。纯本地,无需模型或 GPU。
1386
1386
 
1387
- ## kit.decima_kit —— 选项乱序不变、置信度可门控的决策模型
1387
+ ## kit.ai.decision.decima —— 选项乱序不变、置信度可门控的决策模型
1388
1388
 
1389
1389
  122M 参数的 late-interaction 决策模型(int8 ONNX,单 CPU 线程,**不依赖 torch**)。
1390
1390
  每个选项独立编码、独立与 state 打分,因此**调换选项顺序不会改变答案**。
@@ -1394,7 +1394,7 @@ pip install "sdpy-kit[decima]"
1394
1394
  ```
1395
1395
 
1396
1396
  ```python
1397
- from kit.decima_kit import DecimaKit
1397
+ from kit.ai.decision.decima import DecimaKit
1398
1398
 
1399
1399
  state = "我们三月份被重复扣费了两次,请把重复的那笔退款。"
1400
1400
 
@@ -1645,8 +1645,8 @@ AI 功能需要在项目根目录提供 `config/ai_models.json`(chat / embeddi
1645
1645
  | 层级 | 字段 | 说明 |
1646
1646
  |------|------|------|
1647
1647
  | 档案级 | `endpoint` | 请求路径。**仅 rerank 生效**:chat / embedding / image 由 openai SDK 固定拼路径,本字段被忽略(保留仅为兼容旧配置,不报错) |
1648
- | 档案级 | `timeout` | 请求超时秒数(默认 60);SDK 侧为单次调用总超时 |
1649
- | 档案级 | `max_tokens` | 输出上限(chat 用);`AI.chat_ask/chat_complete/chat_stream` 也可按调用点传参,逐级回落:调用点 → 档案 → provider 默认 |
1648
+ | 档案级 | `timeout` | 请求超时秒数(默认 60);SDK 侧为单次调用总超时。**流式例外**:流式请求 `timeout=None`,由门面首帧 60s + 帧间隔 120s 兜底 |
1649
+ | 档案级 | `max_tokens` | 输出上限(chat 用);`AI.chat/chat_once/chat_stream` 也可按调用点传参,逐级回落:调用点 → 档案 → provider 默认 |
1650
1650
  | 档案级 | `retries` | 5xx 重试次数(默认 2;超时不重试,候选链切换才是有效重试) |
1651
1651
  | 档案级 | `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)` 生效 |
1652
1652
  | 档案级 | `thinking` | 思考开关的注入声明:`{"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 只做翻译,不含任何厂商名 |