sycommon-python-lib 0.2.10__py3-none-any.whl → 0.2.10a3__py3-none-any.whl
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.
- sycommon/agent/deep_agent.py +6 -0
- sycommon/agent/middleware/sensitive_guard.py +7 -1
- sycommon/agent/multi_agent_team.py +1 -0
- sycommon/agent/skills_retry_patch.py +93 -0
- sycommon/middleware/traceid.py +20 -5
- {sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/RECORD +10 -9
- {sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/top_level.txt +0 -0
sycommon/agent/deep_agent.py
CHANGED
|
@@ -65,6 +65,7 @@ from deepagents.middleware.summarization import create_summarization_tool_middle
|
|
|
65
65
|
from sycommon.agent.summarization_utils import build_summarization_middleware
|
|
66
66
|
from sycommon.agent.resume_compactor import maybe_compact_before_resume
|
|
67
67
|
import sycommon.agent.reasoning_content_patch # noqa: F401 提取 reasoning_content/reasoning 字段,避免思考型模型被判空响应
|
|
68
|
+
import sycommon.agent.skills_retry_patch # noqa: F401 技能列表加载失败不永久缓存空结果(沙箱 ls 500 时整会话丢技能)
|
|
68
69
|
|
|
69
70
|
# 流式逐 token 诊断日志开关(默认关)。开启后每个 chunk 都打印
|
|
70
71
|
# chunk_type/content 等(仅供排障)。LLM 最终结果已由 LLMLogger.on_llm_end
|
|
@@ -179,6 +180,10 @@ class AgentConfig(BaseModel):
|
|
|
179
180
|
sensitive_guard: bool = True # 是否启用敏感信息检测守卫
|
|
180
181
|
# 敏感降级目标内部模型名(由应用层透传,避免基础库硬编码应用常量)
|
|
181
182
|
internal_model_name: Optional[str] = None
|
|
183
|
+
# 敏感检测器专用模型名(可选,与降级模型解耦):
|
|
184
|
+
# - 传了 → 检测分类器用它(适合配更快/更准的小模型),降级生成仍用 internal_model_name
|
|
185
|
+
# - None → 沿用 internal_model_name(原行为,兼容存量调用方)
|
|
186
|
+
sensitive_detector_model_name: Optional[str] = None
|
|
182
187
|
# 敏感检测提示词(应用层可覆盖);未传(None)用基础库 DEFAULT_DETECT_PROMPT
|
|
183
188
|
sensitive_detect_prompt: Optional[str] = None
|
|
184
189
|
# 命中敏感后的动作:"allow"=不检测放行;"downgrade"=降级内部模型继续;
|
|
@@ -1245,6 +1250,7 @@ async def create_deep_agent(
|
|
|
1245
1250
|
model_tier=config.model_tier,
|
|
1246
1251
|
enabled=config.sensitive_guard,
|
|
1247
1252
|
internal_model_name=config.internal_model_name,
|
|
1253
|
+
detector_model_name=config.sensitive_detector_model_name,
|
|
1248
1254
|
detect_prompt=config.sensitive_detect_prompt,
|
|
1249
1255
|
action=config.sensitive_action,
|
|
1250
1256
|
)
|
|
@@ -264,6 +264,9 @@ class SensitiveGuardMiddleware(AgentMiddleware):
|
|
|
264
264
|
self,
|
|
265
265
|
model_tier: str,
|
|
266
266
|
internal_model_name: Optional[str] = None,
|
|
267
|
+
# 检测器专用模型名:解耦「检测分类器」与「命中后的降级生成模型」。
|
|
268
|
+
# None 时沿用 internal_model_name(原行为,兼容所有存量调用方)。
|
|
269
|
+
detector_model_name: Optional[str] = None,
|
|
267
270
|
on_downgrade: Optional[Callable[[], Awaitable[None]]] = None,
|
|
268
271
|
enabled: bool = True,
|
|
269
272
|
state: Optional[DowngradeState] = None,
|
|
@@ -286,6 +289,9 @@ class SensitiveGuardMiddleware(AgentMiddleware):
|
|
|
286
289
|
self._model_tier = model_tier
|
|
287
290
|
# None 时检测器/降级目标都用 get_llm() 读 nacos default 模型,不写死模型名
|
|
288
291
|
self._internal_name = internal_model_name
|
|
292
|
+
# 检测器模型:detector_model_name 优先(应用层想用更快/更准的分类器时传),
|
|
293
|
+
# 未传(None)回退 internal_model_name——与原行为完全一致。
|
|
294
|
+
self._detector_name = detector_model_name or internal_model_name
|
|
289
295
|
self._on_downgrade = on_downgrade
|
|
290
296
|
self._on_block = on_block
|
|
291
297
|
# 默认 allow(不检测):应用层未显式指定时按"放行"处理,避免误伤。
|
|
@@ -396,7 +402,7 @@ class SensitiveGuardMiddleware(AgentMiddleware):
|
|
|
396
402
|
# 流式回调;且这里用独立 config(不取 agent 的 StreamMessagesHandler),
|
|
397
403
|
# 从源头断开检测器与用户事件流。
|
|
398
404
|
self._detector = get_llm(
|
|
399
|
-
self.
|
|
405
|
+
self._detector_name, temperature=0.0, wrap_structured=False)
|
|
400
406
|
return self._detector
|
|
401
407
|
|
|
402
408
|
async def _get_internal_raw(self):
|
|
@@ -684,6 +684,7 @@ async def create_multi_agent_team(
|
|
|
684
684
|
enabled=config.sensitive_guard,
|
|
685
685
|
state=shared_downgrade_state,
|
|
686
686
|
internal_model_name=config.internal_model_name,
|
|
687
|
+
detector_model_name=config.sensitive_detector_model_name,
|
|
687
688
|
detect_prompt=config.sensitive_detect_prompt,
|
|
688
689
|
action=config.sensitive_action,
|
|
689
690
|
detect=detect,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""deepagents SkillsMiddleware 的重试补丁。
|
|
3
|
+
|
|
4
|
+
修复「会话首轮沙箱 ls 失败 → skills_metadata=[] 被当已加载缓存进
|
|
5
|
+
checkpoint → 整个会话从此看不到技能」的问题。
|
|
6
|
+
|
|
7
|
+
背景(2026-09-01 线上案例):
|
|
8
|
+
- deepagents SkillsMiddleware.abefore_agent 每会话只加载一次技能:
|
|
9
|
+
`if "skills_metadata" in state: return None` —— 空列表也算"已加载"。
|
|
10
|
+
- 会话首条消息若撞上沙箱 /v1/sandbox/ls 500(发版恢复期/沙箱故障转移),
|
|
11
|
+
middleware 写入 skills_metadata=[] + skills_load_errors,并随 checkpoint
|
|
12
|
+
持久化。后续每一轮命中"in state"直接跳过,永不重试。
|
|
13
|
+
- 用户视角:该会话从此没有技能,直到新建会话。
|
|
14
|
+
|
|
15
|
+
修复语义:skills_metadata 为空**且**存在加载错误(或本应有 system 技能源)
|
|
16
|
+
→ 视为未加载成功,清除旧值后重新加载。真正的零技能(所有源都正常列空)
|
|
17
|
+
不重试,避免每轮白打一次 ls。
|
|
18
|
+
|
|
19
|
+
打补丁方式:替换 deepagents.graph 模块里的 SkillsMiddleware 符号(graph
|
|
20
|
+
在 create_deep_agent 内部 `SkillsMiddleware(backend=..., sources=...)`
|
|
21
|
+
引用的是模块全局名),子类化原类、只重载 abefore_agent,其余行为不动。
|
|
22
|
+
在 sycommon.agent.deep_agent 导入本模块即生效(幂等,可重复导入)。
|
|
23
|
+
"""
|
|
24
|
+
import logging
|
|
25
|
+
|
|
26
|
+
from deepagents.middleware import skills as _skills_module
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("sycommon")
|
|
29
|
+
|
|
30
|
+
_ORIG_MIDDLEWARE = _skills_module.SkillsMiddleware
|
|
31
|
+
_PATCHED = getattr(_ORIG_MIDDLEWARE, "_sycommon_retry_patch", False)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RetrySkillsMiddleware(_ORIG_MIDDLEWARE):
|
|
35
|
+
"""加载失败(空列表+错误)不缓存的 SkillsMiddleware。
|
|
36
|
+
|
|
37
|
+
上游缓存条件 `if "skills_metadata" in state: return None` 的漏洞:
|
|
38
|
+
空列表也是合法 dict key 存在,首轮 ls 失败写进去后永不再加载。
|
|
39
|
+
这里放宽为「空列表且带加载错误 → 重试」,其余行为与上游一致。
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
async def abefore_agent(self, state, runtime, config):
|
|
43
|
+
# 未加载过 → 走上游逻辑(会 fall through 到正常加载)
|
|
44
|
+
if "skills_metadata" not in state:
|
|
45
|
+
return await super().abefore_agent(state, runtime, config)
|
|
46
|
+
|
|
47
|
+
cached = state.get("skills_metadata") or []
|
|
48
|
+
cached_errors = state.get("skills_load_errors") or []
|
|
49
|
+
|
|
50
|
+
if cached:
|
|
51
|
+
# 已有技能列表(哪怕部分源失败) → 尊重上游缓存语义,不重试
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
# 空列表:两种可能——
|
|
55
|
+
# a) 曾加载失败(ls 500 等)带 errors → 需要重试
|
|
56
|
+
# b) 所有源正常但确实零技能(无 errors) → 不重试
|
|
57
|
+
if not cached_errors:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
logger.warning(
|
|
61
|
+
"[SkillsRetry] 检测到缓存为空的技能列表(加载曾失败),"
|
|
62
|
+
"本会话轮次重新加载 skills: %s", self.sources)
|
|
63
|
+
# 清掉旧值让上游 super() 不再命中 "skills_metadata" in state
|
|
64
|
+
# (dict 可变,删 key 即可;state 是 langgraph 传入的 dict 视图)
|
|
65
|
+
try:
|
|
66
|
+
state.pop("skills_metadata", None)
|
|
67
|
+
state.pop("skills_load_errors", None)
|
|
68
|
+
except Exception:
|
|
69
|
+
# state 不可变视图等极端情况:构造副本调 super 不可行,
|
|
70
|
+
# 退化为直接调用内部加载逻辑(与上游 for-source 循环等价)
|
|
71
|
+
pass
|
|
72
|
+
return await super().abefore_agent(state, runtime, config)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _apply() -> None:
|
|
76
|
+
"""把 deepagents.graph 引用的 SkillsMiddleware 换成重试版(幂等)。"""
|
|
77
|
+
if _PATCHED:
|
|
78
|
+
return
|
|
79
|
+
import deepagents.graph as _graph
|
|
80
|
+
|
|
81
|
+
if getattr(_graph.SkillsMiddleware, "_sycommon_retry_patch", False):
|
|
82
|
+
# 已打过(例如热重载场景)——只补标记
|
|
83
|
+
_graph.SkillsMiddleware = RetrySkillsMiddleware
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
RetrySkillsMiddleware._sycommon_retry_patch = True
|
|
87
|
+
_graph.SkillsMiddleware = RetrySkillsMiddleware
|
|
88
|
+
logger.info(
|
|
89
|
+
"[SkillsRetry] SkillsMiddleware 已启用空列表重试补丁"
|
|
90
|
+
"(加载失败不再永久缓存空技能列表)")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
_apply()
|
sycommon/middleware/traceid.py
CHANGED
|
@@ -41,6 +41,21 @@ def _filtered_headers(raw_headers) -> list:
|
|
|
41
41
|
return []
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
def _omit_base64_content(obj: Any) -> None:
|
|
45
|
+
"""把结构化响应体里超长的 content 值(PDF/图片 base64)替换为省略占位。
|
|
46
|
+
|
|
47
|
+
就地修改,dict/list 逐层递归。sanitize_deep_str 已把 JSON 字符串还原成
|
|
48
|
+
结构化 dict/list——不能再 json.loads(传容器进去会 TypeError)。
|
|
49
|
+
"""
|
|
50
|
+
if isinstance(obj, dict):
|
|
51
|
+
c = obj.get("content")
|
|
52
|
+
if c and len(str(c)) > 1000:
|
|
53
|
+
obj["content"] = f"... (base64/binary content omitted, {len(str(c))} chars)"
|
|
54
|
+
elif isinstance(obj, list):
|
|
55
|
+
for item in obj:
|
|
56
|
+
_omit_base64_content(item)
|
|
57
|
+
|
|
58
|
+
|
|
44
59
|
def setup_trace_id_handler(app):
|
|
45
60
|
@app.middleware("http")
|
|
46
61
|
async def trace_id_and_log_middleware(request: Request, call_next):
|
|
@@ -324,11 +339,11 @@ def setup_trace_id_handler(app):
|
|
|
324
339
|
response_log_body = _log_masker.sanitize_deep_str(
|
|
325
340
|
response_body.decode('utf-8', errors='ignore'))
|
|
326
341
|
# 检测是否包含 base64 二进制内容(如 PDF、图片等),如有则省略。
|
|
327
|
-
# sanitize_deep_str 已把 JSON 字符串还原成结构化 dict
|
|
328
|
-
#
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
342
|
+
# sanitize_deep_str 已把 JSON 字符串还原成结构化 dict/list——直接在
|
|
343
|
+
# 结构上改(不要再 json.loads 一个 dict/list,会 TypeError);仅字符串
|
|
344
|
+
# 回退路径(非 JSON 响应)走解析。
|
|
345
|
+
if isinstance(response_log_body, (dict, list)):
|
|
346
|
+
_omit_base64_content(response_log_body)
|
|
332
347
|
else:
|
|
333
348
|
try:
|
|
334
349
|
resp_json = json.loads(response_log_body)
|
|
@@ -137,10 +137,11 @@ sycommon/services.py,sha256=d1HAavs5HWmiSKVjlObu8ApcV-ag9yYCQr87qY10EcE,27919
|
|
|
137
137
|
sycommon/agent/__init__.py,sha256=P5ZQ6R9dpqCjl0HeviWuQo2KDbXGOd9xPXXZHWDmMnQ,4554
|
|
138
138
|
sycommon/agent/agent_manager.py,sha256=-5SzPHYeo8v3Tt6yDA53W1UZ6HqTSc157-rmJjHlrMk,14065
|
|
139
139
|
sycommon/agent/chat_events.py,sha256=V5W21cLwcSm3X2lejUxOUQY_e4h_-30_mkDagxA4JD4,14709
|
|
140
|
-
sycommon/agent/deep_agent.py,sha256=
|
|
141
|
-
sycommon/agent/multi_agent_team.py,sha256=
|
|
140
|
+
sycommon/agent/deep_agent.py,sha256=IVMCeSsW36FEhxG4NdorMFMvqJJ7U7axgMFGWhRfz84,89613
|
|
141
|
+
sycommon/agent/multi_agent_team.py,sha256=ga6iUb3cQxPNLsLyczldHZ8uthmHSYN8Bie9HgfV6kE,35636
|
|
142
142
|
sycommon/agent/reasoning_content_patch.py,sha256=AQ3lKjV7zLrKlWCCIyH1-VehUmcpVpzXdwXBVR9idzs,4033
|
|
143
143
|
sycommon/agent/resume_compactor.py,sha256=4L27VcVpE8UrCrmR-RRJ75ixKT0U62TG62hQuaC5EMw,21249
|
|
144
|
+
sycommon/agent/skills_retry_patch.py,sha256=LwlNIQdl0Hz9tywT5foPipULReMlWJyRK5nGibgp948,4049
|
|
144
145
|
sycommon/agent/summarization_utils.py,sha256=-jQJNKZrb0np_L66qiaXWdXJC98Y4CUn9q8nzz8srIE,19960
|
|
145
146
|
sycommon/agent/acp/__init__.py,sha256=_5QYNAgO_6b0xQXzK8ICRegODEPDQ_AhHC6XCderljg,1908
|
|
146
147
|
sycommon/agent/acp/client.py,sha256=XEOoUsvVI67k0Lw0ZF6T-0Qut-vh5myVfCYQd8RJBwg,27787
|
|
@@ -153,7 +154,7 @@ sycommon/agent/mcp/models.py,sha256=ta9unJD0fPM3Ql10VMusAHchJLumd-0GmyVG5JEsuj0,
|
|
|
153
154
|
sycommon/agent/mcp/tool_loader.py,sha256=0gmpAoSgk6KI9NAsIHxta4DtThpFLUi6G4PHepy1w1M,18263
|
|
154
155
|
sycommon/agent/middleware/model_request_prep.py,sha256=jK6A5Isfxb9Yxf1XFmjDvoYswHiygbLjEBicDPYmplY,7772
|
|
155
156
|
sycommon/agent/middleware/sandbox_path_guard.py,sha256=238q_pI4ZScpuWQ6XOjx-nzEe40TrUy9Lp_VmETJ3xQ,8268
|
|
156
|
-
sycommon/agent/middleware/sensitive_guard.py,sha256=
|
|
157
|
+
sycommon/agent/middleware/sensitive_guard.py,sha256=TzQN1keNvlxfvsUPKyQiftPBdV2B-22M5UeX6w45IkY,32947
|
|
157
158
|
sycommon/agent/middleware/skill_write_guard.py,sha256=6LX6lpCa2ZVEzJ2AjfRXR3juJL2A9oQfE7shfUPqsQE,3172
|
|
158
159
|
sycommon/agent/sandbox/__init__.py,sha256=N6Mcg6Lyvk9dzZYD7Vh2yahStg3hYgtgCBb3ja9HCng,4724
|
|
159
160
|
sycommon/agent/sandbox/consistent_hash.py,sha256=ou6cS0f6Z79VGFE5Bk9PaiW2YsAKDVnG1P9pk9UmE4c,7452
|
|
@@ -242,7 +243,7 @@ sycommon/middleware/sandbox_gateway.py,sha256=K4Z0Mw2x76-xfD3pI1085ik-IbrOKHH2BZ
|
|
|
242
243
|
sycommon/middleware/timeout.py,sha256=lgbyCkrk_fMjl76LvKktBm4g90SOLQcBKCaMCMoS3pE,1477
|
|
243
244
|
sycommon/middleware/token_tracking.py,sha256=mQqfthsL2iyI4skO5S3TPl4oHPF6c80UYjKJz_2cqCE,6782
|
|
244
245
|
sycommon/middleware/tool_result_truncation.py,sha256=JHEmdkYfaA1WvWFxijCPYr-bKmYhwtlLAHSrPNG_ijc,13994
|
|
245
|
-
sycommon/middleware/traceid.py,sha256=
|
|
246
|
+
sycommon/middleware/traceid.py,sha256=Cm3qpyupIyP40DLMSPqQf_cEH4rUsXBuUoj5EO-XlCw,20286
|
|
246
247
|
sycommon/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
247
248
|
sycommon/models/base_http.py,sha256=FjJCRU6Po2OYu-8WVXKPbi9OM2vLFSrMKCk5DfV5hP0,4941
|
|
248
249
|
sycommon/models/log.py,sha256=NqBHue6-_dn97_8d5cKYzYzbI2G2RaMUE4ru-3HAd2s,530
|
|
@@ -334,8 +335,8 @@ sycommon/tools/timing.py,sha256=IVJVqQdCFcm2timqSvKu9Ht372i8pF3K7b2JbtkcBWg,2137
|
|
|
334
335
|
sycommon/tools/user_id.py,sha256=CfQ9bVRbtCYRHkizo-efx42DE600vNGAblXZJkJT7Nk,4867
|
|
335
336
|
sycommon/xxljob/__init__.py,sha256=1VrCsCpzEcU9JLEjpyrbzfL0g0Wu8pIFgkJZJMQjmuw,89
|
|
336
337
|
sycommon/xxljob/xxljob_service.py,sha256=cWLa-mrm7rJHigHrpj-xrSudDwBCtEfssv2pEntBxqA,7865
|
|
337
|
-
sycommon_python_lib-0.2.
|
|
338
|
-
sycommon_python_lib-0.2.
|
|
339
|
-
sycommon_python_lib-0.2.
|
|
340
|
-
sycommon_python_lib-0.2.
|
|
341
|
-
sycommon_python_lib-0.2.
|
|
338
|
+
sycommon_python_lib-0.2.10a3.dist-info/METADATA,sha256=jZ7yKV5_Lsfr8zwAese9wSPZiUHiv535bPGH3RGm5TI,8189
|
|
339
|
+
sycommon_python_lib-0.2.10a3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
340
|
+
sycommon_python_lib-0.2.10a3.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
|
|
341
|
+
sycommon_python_lib-0.2.10a3.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
|
|
342
|
+
sycommon_python_lib-0.2.10a3.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{sycommon_python_lib-0.2.10.dist-info → sycommon_python_lib-0.2.10a3.dist-info}/top_level.txt
RENAMED
|
File without changes
|