sdpy-kit 0.2.6__tar.gz → 0.3.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.2.6 → sdpy_kit-0.3.0}/PKG-INFO +6 -1
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/__init__.py +1 -1
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/config/kit_config.py +20 -0
- sdpy_kit-0.3.0/kit/laya_kit.py +302 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/pyproject.toml +7 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/.gitignore +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/README.md +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/__init__.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/base_service.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/chat_service.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/embedding_service.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/failover.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/image_service.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/model_client.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/pool.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/profiles.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/rerank_service.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/runner.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/ai/types.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/anydoc_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/common_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/__init__.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/base_db_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/crud.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/engine.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/exceptions.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/mysql_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/pg_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/query.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/session.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/sqlite_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/docx_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/file_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/marker_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/mcp_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/md2doc_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/mineru_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/redis_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/wx_article_kit.py +0 -0
- {sdpy_kit-0.2.6 → sdpy_kit-0.3.0}/kit/zvec_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.3.0
|
|
4
4
|
Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/MCP/Redis/文件/文档/向量工具集
|
|
5
5
|
Author-email: Clark <changhongyuan@126.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,6 +29,8 @@ Requires-Dist: docxtpl>=0.20.2; extra == 'all'
|
|
|
29
29
|
Requires-Dist: fastmcp>=4.1.0; extra == 'all'
|
|
30
30
|
Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'all'
|
|
31
31
|
Requires-Dist: httpx>=0.28.1; extra == 'all'
|
|
32
|
+
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'all'
|
|
33
|
+
Requires-Dist: laya>=0.4.6; extra == 'all'
|
|
32
34
|
Requires-Dist: markdown-it-py>=4.2.0; extra == 'all'
|
|
33
35
|
Requires-Dist: marker-pdf[full]>=2.0.0; extra == 'all'
|
|
34
36
|
Requires-Dist: numpy>=2.5.4; extra == 'all'
|
|
@@ -46,6 +48,9 @@ Requires-Dist: firecrawl-anydoc>=0.2.4; extra == 'anydoc'
|
|
|
46
48
|
Requires-Dist: pikepdf>=10.17.0; extra == 'anydoc'
|
|
47
49
|
Provides-Extra: docx
|
|
48
50
|
Requires-Dist: docxtpl>=0.20.2; extra == 'docx'
|
|
51
|
+
Provides-Extra: laya
|
|
52
|
+
Requires-Dist: huggingface-hub<2,>=1.5; extra == 'laya'
|
|
53
|
+
Requires-Dist: laya>=0.4.6; extra == 'laya'
|
|
49
54
|
Provides-Extra: marker
|
|
50
55
|
Requires-Dist: marker-pdf[full]>=2.0.0; extra == 'marker'
|
|
51
56
|
Provides-Extra: mcp
|
|
@@ -142,6 +142,26 @@ class Settings(BaseSettings):
|
|
|
142
142
|
description="llama-server 可执行文件路径(None 时沿用系统 LLAMA_CPP_BINARY)",
|
|
143
143
|
)
|
|
144
144
|
|
|
145
|
+
# Laya 决策引擎配置(本地小模型,把任意文本变成 choice/score/noul 类型化决策)
|
|
146
|
+
LAYA_MODEL: str = Field(
|
|
147
|
+
default="multilingual",
|
|
148
|
+
description=(
|
|
149
|
+
"生效检查点:multilingual(多语言含中文)/ english / typed-decisions"
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
LAYA_DEVICE: str | None = Field(
|
|
153
|
+
default=None,
|
|
154
|
+
description="推理设备(如 cpu / cuda),None 表示交给 laya 自动选择",
|
|
155
|
+
)
|
|
156
|
+
LAYA_HF_ENDPOINT: str = Field(
|
|
157
|
+
default="https://hf-mirror.com",
|
|
158
|
+
description="检查点权重下载入口(默认国内镜像),置空则使用 HuggingFace 官方源",
|
|
159
|
+
)
|
|
160
|
+
LAYA_HF_DISABLE_XET: bool = Field(
|
|
161
|
+
default=True,
|
|
162
|
+
description="禁用 HF Xet 存储后端(国内镜像不支持,开启会导致权重下载挂起)",
|
|
163
|
+
)
|
|
164
|
+
|
|
145
165
|
class Config:
|
|
146
166
|
env_file = ".env"
|
|
147
167
|
env_file_encoding = "utf-8"
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
Project: sdpy
|
|
17
|
+
Author: Clark Chang
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# date: 2026-10-13
|
|
21
|
+
"""laya 决策引擎工具类,把任意文本变成类型化决策
|
|
22
|
+
|
|
23
|
+
laya(https://github.com/NandhaKishorM/laya)是单次前向传播的类型化决策引擎
|
|
24
|
+
(多语言、非自回归),本工具类封装它的三类决策原语:
|
|
25
|
+
|
|
26
|
+
- `choice(state, instructions, criteria)`:候选标签里选一个,`decision` 为选中标签;
|
|
27
|
+
- `score(state, instructions, criteria)`:序数标尺上评分,`decision` 为期望值;
|
|
28
|
+
- `noul(state, instructions)`:判断是否成立,`decision` 为 P(true)(0.0-1.0)。
|
|
29
|
+
|
|
30
|
+
三者都返回 `{decision, answer_confidence, probabilities}`——判断结果 + 置信度;
|
|
31
|
+
需要完整字段(`routing` / `usage`)时用 `predict()` + `brief()`。
|
|
32
|
+
|
|
33
|
+
关键用法(实测得出,务必遵守):
|
|
34
|
+
|
|
35
|
+
- 判断规则必须写进 `instructions`,只靠 `criteria` 描述做语义推断会判错;
|
|
36
|
+
- 候选标签的文字本身会改变输出分布(中文标签与英文标签结果不同);
|
|
37
|
+
- **措辞敏感**:同一意思换同义词会明显改变 `answer_confidence`(如「退款」→「退费」
|
|
38
|
+
使置信度由 0.92 降到 0.57),因此**不要用绝对置信度做门控阈值**;需要稳定请在
|
|
39
|
+
业务侧做输入归一化——本工具类**不改写任何文本**;
|
|
40
|
+
- 上游检查点温度标定无效,置信度未经标定,仅供参考。
|
|
41
|
+
|
|
42
|
+
配置走 `kit.config.Settings`(`.env` / 环境变量):`LAYA_MODEL`(检查点)、
|
|
43
|
+
`LAYA_DEVICE`(推理设备)、`LAYA_HF_ENDPOINT`(权重下载入口)、`LAYA_HF_DISABLE_XET`。
|
|
44
|
+
**HF 相关环境变量必须在导入本模块前生效**(本模块导入即设置并加载 laya),
|
|
45
|
+
如需自定义请在 `.env` 或进程启动前设置。
|
|
46
|
+
|
|
47
|
+
用法:
|
|
48
|
+
from kit.laya_kit import LayaKit
|
|
49
|
+
|
|
50
|
+
result = LayaKit.choice(
|
|
51
|
+
state="我们三月份被重复扣费了两次,请把重复的那笔退款。",
|
|
52
|
+
instructions="这封来信应该由哪个部门处理?",
|
|
53
|
+
criteria={"账务": "账单、付款、退款", "技术": "故障、报错"},
|
|
54
|
+
)
|
|
55
|
+
print(result["decision"], result["answer_confidence"])
|
|
56
|
+
|
|
57
|
+
说明:本模块为可选依赖,需安装 `pip install "sdpy-kit[laya]"`;首次使用会下载
|
|
58
|
+
检查点权重(322M-421M 参数),模型常驻内存,用完可 `LayaKit.unload()` 释放。
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
import os
|
|
62
|
+
import threading
|
|
63
|
+
from typing import Any
|
|
64
|
+
|
|
65
|
+
from kit.config.kit_config import get_settings
|
|
66
|
+
from kit.config.log_config import get_default_logger
|
|
67
|
+
|
|
68
|
+
logger = get_default_logger(__name__)
|
|
69
|
+
|
|
70
|
+
_settings = get_settings()
|
|
71
|
+
|
|
72
|
+
# HuggingFace 相关环境必须在导入 laya / huggingface_hub 之前设置,否则不生效。
|
|
73
|
+
# 统一使用 setdefault:仅当用户未显式设置环境变量时生效,不覆盖显式配置。
|
|
74
|
+
if _settings.LAYA_HF_ENDPOINT:
|
|
75
|
+
os.environ.setdefault("HF_ENDPOINT", _settings.LAYA_HF_ENDPOINT)
|
|
76
|
+
if _settings.LAYA_HF_DISABLE_XET:
|
|
77
|
+
os.environ.setdefault("HF_HUB_DISABLE_XET", "1")
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
import laya
|
|
81
|
+
except ImportError as e:
|
|
82
|
+
raise ImportError(
|
|
83
|
+
"laya_kit 需要可选依赖,请安装:pip install sdpy-kit[laya]"
|
|
84
|
+
) from e
|
|
85
|
+
|
|
86
|
+
# 单问题封装使用的内部问题 id(调用方无需关心)
|
|
87
|
+
_QID: str = "answer"
|
|
88
|
+
|
|
89
|
+
# 决策原语的取值键(与 laya 返回体一致,按优先级取第一个存在的)
|
|
90
|
+
_DECISION_KEYS: tuple[str, ...] = ("choice", "score", "noul")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class LayaError(Exception):
|
|
94
|
+
"""laya 决策异常(模型加载失败 / 推理失败 / 返回体不符合预期)"""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LayaKit:
|
|
98
|
+
"""laya 决策引擎工具类(无状态,方法均为 `classmethod`)
|
|
99
|
+
|
|
100
|
+
Router 为类级单例(模型加载开销极大,必须复用),配置统一走
|
|
101
|
+
`kit.config.Settings` 的 `LAYA_*` 配置项;**不改写任何输入文本**。
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
_router: Any = None
|
|
105
|
+
_router_lock: threading.Lock = threading.Lock()
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def router(cls) -> Any:
|
|
109
|
+
"""惰性创建并缓存 Router(进程内单例,线程安全)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Any: laya.Router 实例
|
|
113
|
+
"""
|
|
114
|
+
with cls._router_lock:
|
|
115
|
+
if cls._router is None:
|
|
116
|
+
kwargs: dict[str, Any] = {"default": _settings.LAYA_MODEL}
|
|
117
|
+
if _settings.LAYA_DEVICE:
|
|
118
|
+
kwargs["device"] = _settings.LAYA_DEVICE
|
|
119
|
+
logger.info(
|
|
120
|
+
f"加载 laya Router:检查点={_settings.LAYA_MODEL},"
|
|
121
|
+
f"设备={_settings.LAYA_DEVICE or 'auto'}"
|
|
122
|
+
)
|
|
123
|
+
cls._router = laya.Router(**kwargs)
|
|
124
|
+
return cls._router
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def route(
|
|
128
|
+
cls, state: str | dict[str, Any], questions: dict[str, Any] | None = None
|
|
129
|
+
) -> dict[str, Any]:
|
|
130
|
+
"""纯路由决策(不加载检查点、不下载权重,可离线)
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
state: 任意文本或结构化 state
|
|
134
|
+
questions: 可选问题定义(None 时只做语言/脚本探测)
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
dict[str, Any]: 路由结果,含 `model` / `reason` / `detection`
|
|
138
|
+
"""
|
|
139
|
+
decision = cls.router().route(state, questions)
|
|
140
|
+
if isinstance(decision, dict):
|
|
141
|
+
return decision
|
|
142
|
+
raw = getattr(decision, "__dict__", None)
|
|
143
|
+
if isinstance(raw, dict):
|
|
144
|
+
return raw
|
|
145
|
+
return {
|
|
146
|
+
"model": getattr(decision, "model", None),
|
|
147
|
+
"reason": getattr(decision, "reason", None),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def predict(
|
|
152
|
+
cls,
|
|
153
|
+
state: str | dict[str, Any],
|
|
154
|
+
questions: dict[str, Any],
|
|
155
|
+
*,
|
|
156
|
+
min_confidence: float | None = None,
|
|
157
|
+
) -> dict[str, Any]:
|
|
158
|
+
"""多问题一次前向传播,返回 laya 原始结果(`answers` / `routing` / `usage`)
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
state: 任意文本或结构化 state
|
|
162
|
+
questions: 问题定义字典(choice / score / noul 可混合)
|
|
163
|
+
min_confidence: 置信度门控阈值,低于该值会在答案里标记 low_confidence;
|
|
164
|
+
注意上游置信度未标定(见模块 docstring),阈值需自行标定
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
dict[str, Any]: laya 原始返回(字段最全,便于调用方自行取用)
|
|
168
|
+
"""
|
|
169
|
+
kwargs: dict[str, Any] = {"model": _settings.LAYA_MODEL}
|
|
170
|
+
if min_confidence is not None:
|
|
171
|
+
kwargs["min_confidence"] = min_confidence
|
|
172
|
+
return cls.router().predict(state, questions, **kwargs)
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def brief(cls, raw: dict[str, Any]) -> dict[str, Any]:
|
|
176
|
+
"""把 predict 原始结果压成「判断结果 + 置信度」两部分
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
raw: `predict()` 的原始返回
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
dict[str, Any]: 问题 id → {decision, answer_confidence, probabilities}
|
|
183
|
+
"""
|
|
184
|
+
brief: dict[str, Any] = {}
|
|
185
|
+
for qid, answer in (raw.get("answers") or {}).items():
|
|
186
|
+
if not isinstance(answer, dict):
|
|
187
|
+
brief[qid] = answer
|
|
188
|
+
continue
|
|
189
|
+
decision = next(
|
|
190
|
+
(answer[key] for key in _DECISION_KEYS if key in answer),
|
|
191
|
+
None,
|
|
192
|
+
)
|
|
193
|
+
brief[qid] = {
|
|
194
|
+
"decision": decision,
|
|
195
|
+
"answer_confidence": answer.get("answer_confidence"),
|
|
196
|
+
"probabilities": answer.get("probabilities"),
|
|
197
|
+
}
|
|
198
|
+
return brief
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def choice(
|
|
202
|
+
cls,
|
|
203
|
+
state: str | dict[str, Any],
|
|
204
|
+
instructions: str,
|
|
205
|
+
criteria: dict[str, str],
|
|
206
|
+
*,
|
|
207
|
+
min_confidence: float | None = None,
|
|
208
|
+
) -> dict[str, Any]:
|
|
209
|
+
"""单选题:在 `criteria` 给出的候选标签里选一个
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
state: 待判断的文本或结构化 state
|
|
213
|
+
instructions: 判断规则与问题——**规则写这里**,不要只写在 criteria 描述里
|
|
214
|
+
criteria: 候选标签 → 该标签的说明文字
|
|
215
|
+
min_confidence: 置信度门控阈值
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
dict[str, Any]: {decision: 选中标签, answer_confidence, probabilities}
|
|
219
|
+
"""
|
|
220
|
+
question = {
|
|
221
|
+
"type": "choice",
|
|
222
|
+
"instructions": instructions,
|
|
223
|
+
"criteria": criteria,
|
|
224
|
+
}
|
|
225
|
+
return cls._ask(question, state, min_confidence)
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def score(
|
|
229
|
+
cls,
|
|
230
|
+
state: str | dict[str, Any],
|
|
231
|
+
instructions: str,
|
|
232
|
+
criteria: list[str],
|
|
233
|
+
*,
|
|
234
|
+
min_confidence: float | None = None,
|
|
235
|
+
) -> dict[str, Any]:
|
|
236
|
+
"""评分题:在 `criteria` 给出的序数标尺上给出期望值
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
state: 待判断的文本或结构化 state
|
|
240
|
+
instructions: 判断规则与问题
|
|
241
|
+
criteria: 从低到高的等级描述列表
|
|
242
|
+
min_confidence: 置信度门控阈值
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
dict[str, Any]: {decision: 序数期望值, answer_confidence, probabilities}
|
|
246
|
+
"""
|
|
247
|
+
question = {
|
|
248
|
+
"type": "score",
|
|
249
|
+
"instructions": instructions,
|
|
250
|
+
"criteria": criteria,
|
|
251
|
+
}
|
|
252
|
+
return cls._ask(question, state, min_confidence)
|
|
253
|
+
|
|
254
|
+
@classmethod
|
|
255
|
+
def noul(
|
|
256
|
+
cls,
|
|
257
|
+
state: str | dict[str, Any],
|
|
258
|
+
instructions: str,
|
|
259
|
+
*,
|
|
260
|
+
min_confidence: float | None = None,
|
|
261
|
+
) -> dict[str, Any]:
|
|
262
|
+
"""是否题:返回 P(true)
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
state: 待判断的文本或结构化 state
|
|
266
|
+
instructions: 待判断的命题(如「客户是否明确要求退款?」)
|
|
267
|
+
min_confidence: 置信度门控阈值
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
dict[str, Any]: {decision: P(true), answer_confidence, probabilities}
|
|
271
|
+
"""
|
|
272
|
+
question = {"type": "noul", "instructions": instructions}
|
|
273
|
+
return cls._ask(question, state, min_confidence)
|
|
274
|
+
|
|
275
|
+
@classmethod
|
|
276
|
+
def _ask(
|
|
277
|
+
cls,
|
|
278
|
+
question: dict[str, Any],
|
|
279
|
+
state: str | dict[str, Any],
|
|
280
|
+
min_confidence: float | None,
|
|
281
|
+
) -> dict[str, Any]:
|
|
282
|
+
"""单问题封装:构造 questions → predict → 取该问的 brief 结果
|
|
283
|
+
|
|
284
|
+
Raises:
|
|
285
|
+
LayaError: laya 未返回该问题的答案时抛出(返回体结构变化)
|
|
286
|
+
"""
|
|
287
|
+
raw = cls.predict(state, {_QID: question}, min_confidence=min_confidence)
|
|
288
|
+
brief = cls.brief(raw)
|
|
289
|
+
if _QID not in brief:
|
|
290
|
+
raise LayaError(f"laya 未返回问句 {_QID} 的答案:{raw!r}")
|
|
291
|
+
return brief[_QID]
|
|
292
|
+
|
|
293
|
+
@classmethod
|
|
294
|
+
def unload(cls) -> None:
|
|
295
|
+
"""释放 Router 与模型内存(先调用底层 unload,再置空单例)"""
|
|
296
|
+
with cls._router_lock:
|
|
297
|
+
if cls._router is not None:
|
|
298
|
+
unload_fn = getattr(cls._router, "unload", None)
|
|
299
|
+
if callable(unload_fn):
|
|
300
|
+
unload_fn()
|
|
301
|
+
cls._router = None
|
|
302
|
+
logger.info("laya Router 已释放")
|
|
@@ -52,6 +52,7 @@ dynamic = ["version"]
|
|
|
52
52
|
# docx → docx_kit(docxtpl + python-docx,模板动态填充)
|
|
53
53
|
# mineru → mineru_kit(requests)
|
|
54
54
|
# marker → marker_kit(marker-pdf[full],本地模型解析 PDF→Markdown+Images)
|
|
55
|
+
# laya → laya_kit(laya + torch,本地类型化决策引擎 choice/score/noul)
|
|
55
56
|
# 基础版 = config/log + kit.db 核心,SQLite 常规操作用标准库 sqlite3,零额外依赖。
|
|
56
57
|
[project.optional-dependencies]
|
|
57
58
|
pg = ["psycopg2-binary>=2.9.13"]
|
|
@@ -78,6 +79,10 @@ anydoc = [
|
|
|
78
79
|
docx = ["docxtpl>=0.20.2"]
|
|
79
80
|
mineru = ["requests>=2.34.2"]
|
|
80
81
|
marker = ["marker-pdf[full]>=2.0.0"]
|
|
82
|
+
laya = [
|
|
83
|
+
"laya>=0.4.6",
|
|
84
|
+
"huggingface-hub>=1.5,<2",
|
|
85
|
+
]
|
|
81
86
|
all = [
|
|
82
87
|
"psycopg2-binary>=2.9.13",
|
|
83
88
|
"pymysql>=1.2.3",
|
|
@@ -95,6 +100,8 @@ all = [
|
|
|
95
100
|
"docxtpl>=0.20.2",
|
|
96
101
|
"requests>=2.34.2",
|
|
97
102
|
"marker-pdf[full]>=2.0.0",
|
|
103
|
+
"laya>=0.4.6",
|
|
104
|
+
"huggingface-hub>=1.5,<2",
|
|
98
105
|
]
|
|
99
106
|
[tool.hatch.version]
|
|
100
107
|
path = "kit/__init__.py"
|
|
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
|