sycommon-python-lib 0.2.10a2__py3-none-any.whl → 0.2.10a4__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 +5 -0
- sycommon/agent/middleware/sensitive_guard.py +7 -1
- sycommon/agent/multi_agent_team.py +1 -0
- sycommon/database/async_database_service.py +5 -1
- sycommon/database/database_service.py +44 -2
- sycommon/database/redis_service.py +18 -0
- sycommon/database/token_usage_db_service.py +36 -2
- sycommon/heartbeat_process/heartbeat_process_worker.py +3 -0
- sycommon/middleware/traceid.py +1 -1
- sycommon/models/base_http.py +35 -7
- sycommon/notice/uvicorn_monitor.py +31 -20
- sycommon/synacos/nacos_client_base.py +6 -0
- sycommon/tests/test_db_keepalive.py +178 -0
- sycommon/tests/test_upgrade_verification.py +7 -7
- {sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/METADATA +13 -13
- {sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/RECORD +19 -18
- {sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/top_level.txt +0 -0
sycommon/agent/deep_agent.py
CHANGED
|
@@ -180,6 +180,10 @@ class AgentConfig(BaseModel):
|
|
|
180
180
|
sensitive_guard: bool = True # 是否启用敏感信息检测守卫
|
|
181
181
|
# 敏感降级目标内部模型名(由应用层透传,避免基础库硬编码应用常量)
|
|
182
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
|
|
183
187
|
# 敏感检测提示词(应用层可覆盖);未传(None)用基础库 DEFAULT_DETECT_PROMPT
|
|
184
188
|
sensitive_detect_prompt: Optional[str] = None
|
|
185
189
|
# 命中敏感后的动作:"allow"=不检测放行;"downgrade"=降级内部模型继续;
|
|
@@ -1246,6 +1250,7 @@ async def create_deep_agent(
|
|
|
1246
1250
|
model_tier=config.model_tier,
|
|
1247
1251
|
enabled=config.sensitive_guard,
|
|
1248
1252
|
internal_model_name=config.internal_model_name,
|
|
1253
|
+
detector_model_name=config.sensitive_detector_model_name,
|
|
1249
1254
|
detect_prompt=config.sensitive_detect_prompt,
|
|
1250
1255
|
action=config.sensitive_action,
|
|
1251
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,
|
|
@@ -212,7 +212,11 @@ class AsyncDatabaseConnector:
|
|
|
212
212
|
def tune_keepalive(dbapi_connection, connection_record):
|
|
213
213
|
import socket as _socket
|
|
214
214
|
try:
|
|
215
|
-
|
|
215
|
+
# SQLAlchemy 2.x 异步方言把 aiomysql 连接包在
|
|
216
|
+
# AsyncAdapt_aiomysql_connection._connection 里;老版本/裸 aiomysql
|
|
217
|
+
# 则直接挂 _writer。两级都探测。
|
|
218
|
+
inner = getattr(dbapi_connection, "_connection", dbapi_connection)
|
|
219
|
+
writer = getattr(inner, "_writer", None)
|
|
216
220
|
transport = getattr(writer, "transport", None)
|
|
217
221
|
raw_sock = transport.get_extra_info("socket") if transport else None
|
|
218
222
|
if raw_sock is None:
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
from sqlalchemy import create_engine, text, event
|
|
2
4
|
|
|
3
5
|
from sycommon.config.Config import SingletonMeta
|
|
@@ -106,17 +108,57 @@ class DatabaseConnector(metaclass=SingletonMeta):
|
|
|
106
108
|
SYLogger.info(f"Database URL: {safe_url}")
|
|
107
109
|
|
|
108
110
|
# 优化连接池配置
|
|
111
|
+
# 🔑 同步引擎与异步引擎(AsyncDatabaseConnector)同源风险:池中空闲连接被中间
|
|
112
|
+
# 网络设备静默掐死(不回 RST)时,pre_ping 的 SELECT 1 发进黑洞,会挂到内核
|
|
113
|
+
# TCP 重传放弃。分驱动两层防护(实测验证,见 test_db_keepalive.py):
|
|
114
|
+
# - C 扩展(CMySQLConnection,装了 _mysql_driver 时默认):不暴露 Python 层
|
|
115
|
+
# socket,keepalive 钩子拿不到 raw socket;用 read_timeout=60 兜底——黑洞
|
|
116
|
+
# 读挂死 60s 后抛错,pre_ping 捕获弃连重建(真实环境已验证 SELECT SLEEP
|
|
117
|
+
# 按 read_timeout 抛 OperationalError)。
|
|
118
|
+
# - pure 实现(MySQLConnection):钩子对 _socket.sock 补 keepalive 参数,
|
|
119
|
+
# 死连接 ~60s 内被内核暴露为错误(与异步侧 73f2156 同一套参数)。
|
|
109
120
|
self.engine = create_engine(
|
|
110
121
|
self.db_url,
|
|
111
|
-
connect_args=
|
|
122
|
+
connect_args={
|
|
123
|
+
**params,
|
|
124
|
+
"connect_timeout": 10, # TCP 建连超时(秒)
|
|
125
|
+
"read_timeout": 60, # 查询级读超时(秒):C 扩展拿不到 socket 时的黑洞兜底
|
|
126
|
+
"write_timeout": 60, # 写超时(秒):同上
|
|
127
|
+
},
|
|
112
128
|
pool_size=10, # 连接池大小
|
|
113
129
|
max_overflow=20, # 最大溢出连接数
|
|
114
130
|
pool_timeout=30, # 连接超时时间(秒)
|
|
115
|
-
pool_recycle=
|
|
131
|
+
pool_recycle=300, # 连接回收时间(秒);赶在防火墙空闲回收窗口(常见 5-15min)前换血
|
|
116
132
|
pool_pre_ping=True, # 每次获取连接前检查连接是否有效
|
|
117
133
|
echo=False, # 打印 SQL 语句
|
|
118
134
|
)
|
|
119
135
|
|
|
136
|
+
# 每个新连接补齐 TCP keepalive 参数(连接建立后才能拿到 raw socket)
|
|
137
|
+
@event.listens_for(self.engine, "connect")
|
|
138
|
+
def tune_keepalive(dbapi_connection, connection_record):
|
|
139
|
+
import socket as _socket
|
|
140
|
+
try:
|
|
141
|
+
# mysql-connector-python: raw socket 在 MySQLSocket 包装对象的 .sock 上
|
|
142
|
+
sock_wrapper = getattr(dbapi_connection, "_socket", None)
|
|
143
|
+
raw_sock = getattr(sock_wrapper, "sock", None)
|
|
144
|
+
if raw_sock is None:
|
|
145
|
+
return
|
|
146
|
+
raw_sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_KEEPALIVE, 1)
|
|
147
|
+
if sys.platform == "win32":
|
|
148
|
+
# Windows: SIO_KEEPALIVE_VALS = (on/off, idle ms, interval ms)
|
|
149
|
+
raw_sock.ioctl(_socket.SIO_KEEPALIVE_VALS, (1, 30000, 10000))
|
|
150
|
+
else:
|
|
151
|
+
# Linux: 空闲 30s 发首个探测,每 10s 一次,3 次无回应判死(约 60s 暴露)
|
|
152
|
+
if hasattr(_socket, "TCP_KEEPIDLE"):
|
|
153
|
+
raw_sock.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_KEEPIDLE, 30)
|
|
154
|
+
if hasattr(_socket, "TCP_KEEPINTVL"):
|
|
155
|
+
raw_sock.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_KEEPINTVL, 10)
|
|
156
|
+
if hasattr(_socket, "TCP_KEEPCNT"):
|
|
157
|
+
raw_sock.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_KEEPCNT, 3)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
# keepalive 调优失败不阻断连接创建(回退内核默认)
|
|
160
|
+
SYLogger.warning(f"TCP keepalive 调优失败(回退默认): {e}")
|
|
161
|
+
|
|
120
162
|
# 注册 SQL 日志拦截器
|
|
121
163
|
SQLTraceLogger.setup_sql_logging(self.engine)
|
|
122
164
|
|
|
@@ -3,6 +3,8 @@ Redis 异步服务
|
|
|
3
3
|
使用 redis-py 官方异步客户端
|
|
4
4
|
"""
|
|
5
5
|
import logging
|
|
6
|
+
import socket
|
|
7
|
+
import sys
|
|
6
8
|
from typing import Optional, Any, Union
|
|
7
9
|
|
|
8
10
|
from sycommon.config.Config import SingletonMeta
|
|
@@ -32,6 +34,18 @@ class RedisService(metaclass=SingletonMeta):
|
|
|
32
34
|
"""
|
|
33
35
|
cls.init(config)
|
|
34
36
|
|
|
37
|
+
# TCP keepalive 探测参数:空闲 30s 首探、每 10s 一次、3 次无回应判死。
|
|
38
|
+
# 与 MySQL 引擎同套参数(见 async_database_service.py tune_keepalive)。
|
|
39
|
+
# redis-py 只按平台常量透传 setsockopt;macOS 用 TCP_KEEPALIVE 表 idle。
|
|
40
|
+
# Windows 无 per-socket keepalive 常量(需 SIO_KEEPALIVE_VALS ioctl),
|
|
41
|
+
# 且其 TCP_KEEPALIVE 语义不同,透传会抛 WSAENOPROTOOPT —— 直接留空回退默认。
|
|
42
|
+
_KEEPALIVE_OPTIONS = {} if sys.platform == "win32" else {
|
|
43
|
+
getattr(socket, name, None): value
|
|
44
|
+
for name, value in (("TCP_KEEPIDLE", 30), ("TCP_KEEPALIVE", 30),
|
|
45
|
+
("TCP_KEEPINTVL", 10), ("TCP_KEEPCNT", 3))
|
|
46
|
+
if getattr(socket, name, None) is not None
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
@classmethod
|
|
36
50
|
def init(cls, config: Optional[dict] = None):
|
|
37
51
|
"""
|
|
@@ -104,6 +118,8 @@ class RedisService(metaclass=SingletonMeta):
|
|
|
104
118
|
db=cls._config.database,
|
|
105
119
|
max_connections=max_connections,
|
|
106
120
|
socket_timeout=cls._config.timeout,
|
|
121
|
+
socket_keepalive=True, # 开 TCP keepalive;探测参数见下方 _KEEPALIVE_OPTIONS
|
|
122
|
+
socket_keepalive_options=RedisService._KEEPALIVE_OPTIONS,
|
|
107
123
|
decode_responses=True # 自动解码为字符串
|
|
108
124
|
)
|
|
109
125
|
|
|
@@ -151,6 +167,8 @@ class RedisService(metaclass=SingletonMeta):
|
|
|
151
167
|
cls._client = sentinel.master_for(
|
|
152
168
|
sentinel_config.master,
|
|
153
169
|
socket_timeout=cls._config.timeout,
|
|
170
|
+
socket_keepalive=True, # 与单机模式一致,防空闲连接被静默掐死
|
|
171
|
+
socket_keepalive_options=RedisService._KEEPALIVE_OPTIONS,
|
|
154
172
|
password=cls._config.password,
|
|
155
173
|
db=cls._config.database,
|
|
156
174
|
decode_responses=True
|
|
@@ -3,10 +3,11 @@ Token 使用量记录专用数据库服务
|
|
|
3
3
|
独立于业务系统的数据库连接,使用 llm 配置中的数据库信息
|
|
4
4
|
"""
|
|
5
5
|
import logging
|
|
6
|
+
import sys
|
|
6
7
|
from contextlib import asynccontextmanager
|
|
7
8
|
from typing import Optional
|
|
8
9
|
|
|
9
|
-
from sqlalchemy import text
|
|
10
|
+
from sqlalchemy import event, text
|
|
10
11
|
from sqlalchemy.ext.asyncio import (
|
|
11
12
|
AsyncSession,
|
|
12
13
|
async_sessionmaker,
|
|
@@ -197,17 +198,50 @@ class TokenUsageDBConnector(metaclass=SingletonMeta):
|
|
|
197
198
|
|
|
198
199
|
# 创建异步引擎(Token 记录连接池较小,增加连接超时防止慢 MySQL 拖垮主服务)
|
|
199
200
|
# 4 workers × (pool_size=1 + max_overflow=2) = 12
|
|
201
|
+
# 🔑 TCP keepalive:与 AsyncDatabaseConnector(73f2156)同源风险——aiomysql 无查询
|
|
202
|
+
# 级读超时,池中死连接会让 Token 落库黑洞挂死。pool_recycle 同步降为 300,
|
|
203
|
+
# 赶在防火墙空闲回收窗口(常见 5-15min)前换血。
|
|
200
204
|
self.engine = create_async_engine(
|
|
201
205
|
self.db_url,
|
|
202
206
|
connect_args={**params, "connect_timeout": 3},
|
|
203
207
|
pool_size=1,
|
|
204
208
|
max_overflow=2,
|
|
205
209
|
pool_timeout=5,
|
|
206
|
-
pool_recycle=
|
|
210
|
+
pool_recycle=300,
|
|
207
211
|
pool_pre_ping=True,
|
|
208
212
|
echo=False,
|
|
209
213
|
)
|
|
210
214
|
|
|
215
|
+
# 每个新连接补齐 TCP keepalive 参数(连接建立后才能拿到 raw socket)
|
|
216
|
+
@event.listens_for(self.engine.sync_engine, "connect")
|
|
217
|
+
def tune_keepalive(dbapi_connection, connection_record):
|
|
218
|
+
import socket as _socket
|
|
219
|
+
try:
|
|
220
|
+
# SQLAlchemy 2.x 异步方言把 aiomysql 连接包在
|
|
221
|
+
# AsyncAdapt_aiomysql_connection._connection 里;老版本/裸 aiomysql
|
|
222
|
+
# 则直接挂 _writer。两级都探测。
|
|
223
|
+
inner = getattr(dbapi_connection, "_connection", dbapi_connection)
|
|
224
|
+
writer = getattr(inner, "_writer", None)
|
|
225
|
+
transport = getattr(writer, "transport", None)
|
|
226
|
+
raw_sock = transport.get_extra_info("socket") if transport else None
|
|
227
|
+
if raw_sock is None:
|
|
228
|
+
return
|
|
229
|
+
raw_sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_KEEPALIVE, 1)
|
|
230
|
+
if sys.platform == "win32":
|
|
231
|
+
# Windows: SIO_KEEPALIVE_VALS = (on/off, idle ms, interval ms)
|
|
232
|
+
raw_sock.ioctl(_socket.SIO_KEEPALIVE_VALS, (1, 30000, 10000))
|
|
233
|
+
else:
|
|
234
|
+
# Linux: 空闲 30s 发首个探测,每 10s 一次,3 次无回应判死(约 60s 暴露)
|
|
235
|
+
if hasattr(_socket, "TCP_KEEPIDLE"):
|
|
236
|
+
raw_sock.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_KEEPIDLE, 30)
|
|
237
|
+
if hasattr(_socket, "TCP_KEEPINTVL"):
|
|
238
|
+
raw_sock.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_KEEPINTVL, 10)
|
|
239
|
+
if hasattr(_socket, "TCP_KEEPCNT"):
|
|
240
|
+
raw_sock.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_KEEPCNT, 3)
|
|
241
|
+
except Exception as e:
|
|
242
|
+
# keepalive 调优失败不阻断连接创建(回退内核默认)
|
|
243
|
+
SYLogger.warning(f"TCP keepalive 调优失败(回退默认): {e}")
|
|
244
|
+
|
|
211
245
|
# 注册 SQL 日志拦截器
|
|
212
246
|
from sycommon.logging.async_sql_logger import AsyncSQLTraceLogger
|
|
213
247
|
AsyncSQLTraceLogger.setup_sql_logging(self.engine)
|
|
@@ -178,6 +178,9 @@ class HeartbeatProcessWorker:
|
|
|
178
178
|
username=self.nacos_config.username,
|
|
179
179
|
password=self.nacos_config.password
|
|
180
180
|
)
|
|
181
|
+
# SDK 默认单请求超时仅 3 秒,网络/DNS 抖动即抛
|
|
182
|
+
# "All server are not available",放宽到 10 秒
|
|
183
|
+
self.nacos_client.set_options(default_timeout=10)
|
|
181
184
|
logger.info("Nacos 客户端初始化成功")
|
|
182
185
|
return True
|
|
183
186
|
except Exception as e:
|
sycommon/middleware/traceid.py
CHANGED
|
@@ -68,7 +68,7 @@ def setup_trace_id_handler(app):
|
|
|
68
68
|
token = SYLogger.set_trace_id(trace_id)
|
|
69
69
|
# 🔒 白名单过滤:Authorization/Cookie/SYT_TOKEN 等凭证 header 不进
|
|
70
70
|
# 日志上下文,防止下游任何日志路径把凭证打到日志。
|
|
71
|
-
header_token = SYLogger.set_headers(
|
|
71
|
+
header_token = SYLogger.set_headers(request.headers.raw)
|
|
72
72
|
|
|
73
73
|
# 获取请求参数
|
|
74
74
|
query_params = dict(request.query_params)
|
sycommon/models/base_http.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
from typing import TypeVar, Any, Generic
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
2
4
|
from pydantic import BaseModel, Field
|
|
3
5
|
from fastapi.responses import JSONResponse
|
|
4
6
|
from fastapi import status
|
|
@@ -8,7 +10,11 @@ T = TypeVar('T')
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class BaseResponseModel(BaseModel, Generic[T]):
|
|
11
|
-
"""基础响应模型,支持自定义状态码
|
|
13
|
+
"""基础响应模型,支持自定义状态码
|
|
14
|
+
|
|
15
|
+
注意:code 默认 0 是旧版 0/1 业务码的兼容默认值,新代码请走
|
|
16
|
+
ok()/fail()(code=200/500)。默认值暂不改,避免破坏旧接口反序列化。
|
|
17
|
+
"""
|
|
12
18
|
code: int = Field(default=0, description="业务响应码,默认0成功,1失败,支持自定义")
|
|
13
19
|
message: str | None = Field(default=None, description="业务响应信息")
|
|
14
20
|
success: bool = Field(default=True, description="请求是否成功")
|
|
@@ -20,6 +26,23 @@ class BaseResponseModel(BaseModel, Generic[T]):
|
|
|
20
26
|
from_attributes = True
|
|
21
27
|
|
|
22
28
|
|
|
29
|
+
# =====================================================================
|
|
30
|
+
# 【Deprecated】旧版 0/1 业务码方式 —— 未来将移除,请勿在新代码中使用。
|
|
31
|
+
#
|
|
32
|
+
# 新代码请用下方的 ok() / fail()(HTTP 语义:成功 code=200 / 失败 code=500,
|
|
33
|
+
# HTTP status 与业务码一致,success 由 2xx 推导)。
|
|
34
|
+
# =====================================================================
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _deprecate_01_style(name: str) -> None:
|
|
38
|
+
warnings.warn(
|
|
39
|
+
f"sycommon.models.base_http.{name} 已废弃(0/1 业务码方式),"
|
|
40
|
+
f"请改用 ok()/fail()(HTTP 语义 code=200/500);未来版本将移除。",
|
|
41
|
+
DeprecationWarning,
|
|
42
|
+
stacklevel=3,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
23
46
|
def build_response_content(
|
|
24
47
|
data: T | Any = None,
|
|
25
48
|
code: int = 0,
|
|
@@ -59,7 +82,8 @@ def create_response(
|
|
|
59
82
|
message: str = None,
|
|
60
83
|
status_code: int = status.HTTP_200_OK
|
|
61
84
|
) -> JSONResponse:
|
|
62
|
-
"""
|
|
85
|
+
"""【Deprecated】创建完整响应,支持自定义业务状态码 —— 改用 http_response()"""
|
|
86
|
+
_deprecate_01_style("create_response")
|
|
63
87
|
content = build_response_content(data=data, code=code, message=message)
|
|
64
88
|
return JSONResponse(
|
|
65
89
|
content=content,
|
|
@@ -68,7 +92,8 @@ def create_response(
|
|
|
68
92
|
|
|
69
93
|
|
|
70
94
|
def success_response(data: T | Any = None, code: int = 0, message: str = None) -> JSONResponse:
|
|
71
|
-
"""
|
|
95
|
+
"""【Deprecated】快捷创建成功响应(code=0, success=True) —— 改用 ok()"""
|
|
96
|
+
_deprecate_01_style("success_response")
|
|
72
97
|
return create_response(data=data, code=code, message=message)
|
|
73
98
|
|
|
74
99
|
|
|
@@ -78,7 +103,8 @@ def error_response(
|
|
|
78
103
|
data: T | Any = None,
|
|
79
104
|
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
80
105
|
) -> JSONResponse:
|
|
81
|
-
"""
|
|
106
|
+
"""【Deprecated】快捷创建错误响应(code=1) —— 改用 fail()"""
|
|
107
|
+
_deprecate_01_style("error_response")
|
|
82
108
|
return create_response(
|
|
83
109
|
data=data,
|
|
84
110
|
code=code,
|
|
@@ -88,7 +114,8 @@ def error_response(
|
|
|
88
114
|
|
|
89
115
|
|
|
90
116
|
def success_content(data: T | Any = None) -> dict:
|
|
91
|
-
"""
|
|
117
|
+
"""【Deprecated】构建成功响应内容字典(code=0) —— 改用 ok()"""
|
|
118
|
+
_deprecate_01_style("success_content")
|
|
92
119
|
return build_response_content(data=data)
|
|
93
120
|
|
|
94
121
|
|
|
@@ -97,7 +124,8 @@ def error_content(
|
|
|
97
124
|
code: int = 1,
|
|
98
125
|
data: T | Any = None
|
|
99
126
|
) -> dict:
|
|
100
|
-
"""
|
|
127
|
+
"""【Deprecated】构建错误响应内容字典(code=1) —— 改用 fail()"""
|
|
128
|
+
_deprecate_01_style("error_content")
|
|
101
129
|
return build_response_content(data=data, code=code, message=message)
|
|
102
130
|
|
|
103
131
|
|
|
@@ -107,7 +135,7 @@ def error_content(
|
|
|
107
135
|
# 与上面 0/1 业务码的区别:code 直接用 HTTP 状态码语义——
|
|
108
136
|
# 成功 code=200 / 失败 code=500,HTTP status 同步对应(200 / 500),
|
|
109
137
|
# success 由 code 是否为 2xx 推导。不再用 0/1 代表成功失败。
|
|
110
|
-
#
|
|
138
|
+
# 旧函数已标记 Deprecated(调用即抛 DeprecationWarning),未来版本移除。
|
|
111
139
|
# =====================================================================
|
|
112
140
|
|
|
113
141
|
|
|
@@ -55,6 +55,9 @@ async def send_wechat_markdown_msg(
|
|
|
55
55
|
) -> Optional[dict]:
|
|
56
56
|
"""
|
|
57
57
|
异步发送企业微信Markdown格式的WebHook消息
|
|
58
|
+
|
|
59
|
+
失败(网络异常/非0 errcode)时等 2 秒重试一次,
|
|
60
|
+
避免网络瞬时抖动导致告警丢失(例如只收到"恢复"没收到"断开")。
|
|
58
61
|
"""
|
|
59
62
|
# 构造请求体(Markdown格式)
|
|
60
63
|
payload = {
|
|
@@ -64,26 +67,34 @@ async def send_wechat_markdown_msg(
|
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
max_attempts = 2
|
|
71
|
+
for attempt in range(1, max_attempts + 1):
|
|
72
|
+
try:
|
|
73
|
+
# 使用 json 参数自动处理序列化和 Content-Type
|
|
74
|
+
async with aiohttp.ClientSession() as session:
|
|
75
|
+
async with session.post(
|
|
76
|
+
url=webhook,
|
|
77
|
+
json=payload,
|
|
78
|
+
timeout=aiohttp.ClientTimeout(total=10)
|
|
79
|
+
) as response:
|
|
80
|
+
response_data = await response.json()
|
|
81
|
+
|
|
82
|
+
if response.status == 200 and response_data.get("errcode") == 0:
|
|
83
|
+
SYLogger.info(f"消息发送成功: {response_data}")
|
|
84
|
+
return response_data
|
|
85
|
+
else:
|
|
86
|
+
SYLogger.warning(
|
|
87
|
+
f"消息发送失败 - 状态码: {response.status}, 响应: {response_data}")
|
|
88
|
+
if attempt < max_attempts:
|
|
89
|
+
await asyncio.sleep(2)
|
|
90
|
+
continue
|
|
91
|
+
return None
|
|
92
|
+
except Exception as e:
|
|
93
|
+
SYLogger.error(f"发送企业微信消息异常: {str(e)}")
|
|
94
|
+
if attempt < max_attempts:
|
|
95
|
+
await asyncio.sleep(2)
|
|
96
|
+
continue
|
|
97
|
+
return None
|
|
87
98
|
|
|
88
99
|
|
|
89
100
|
async def send_webhook(error_info: dict = None, webhook: str = None):
|
|
@@ -88,6 +88,9 @@ class NacosClientBase:
|
|
|
88
88
|
server_addresses=register_ip,
|
|
89
89
|
namespace=namespace_id
|
|
90
90
|
)
|
|
91
|
+
# SDK 默认单请求超时仅 3 秒,网络/DNS 抖动即抛
|
|
92
|
+
# "All server are not available",放宽到 10 秒
|
|
93
|
+
self.nacos_client.set_options(default_timeout=10)
|
|
91
94
|
SYLogger.info("nacos:客户端初始化成功")
|
|
92
95
|
self._client_initialized = True
|
|
93
96
|
return True
|
|
@@ -120,6 +123,9 @@ class NacosClientBase:
|
|
|
120
123
|
server_addresses=register_ip,
|
|
121
124
|
namespace=namespace_id
|
|
122
125
|
)
|
|
126
|
+
# SDK 默认单请求超时仅 3 秒,网络/DNS 抖动即抛
|
|
127
|
+
# "All server are not available",放宽到 10 秒
|
|
128
|
+
self.nacos_client.set_options(default_timeout=10)
|
|
123
129
|
|
|
124
130
|
if self._verify_client_connection():
|
|
125
131
|
self._client_initialized = True
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
数据库连接 TCP keepalive 调优验证(不依赖真实数据库)。
|
|
3
|
+
|
|
4
|
+
覆盖三个建连点:
|
|
5
|
+
- 同步 DatabaseConnector(mysqlconnector, raw socket 在 _socket.sock)
|
|
6
|
+
- 异步 AsyncDatabaseConnector(aiomysql, raw socket 在 _writer.transport)
|
|
7
|
+
- 异步 TokenUsageDBConnector(aiomysql, 同上)
|
|
8
|
+
|
|
9
|
+
验证方式:构造连接器拿到 engine 上注册的 "connect" 事件监听器,
|
|
10
|
+
用假 dbapi 连接触发,断言对 raw socket 调了 SO_KEEPALIVE 与
|
|
11
|
+
KEEPIDLE/KEEPINTVL/KEEPCNT 三个参数(即 73f2156 事故修复的参数集)。
|
|
12
|
+
"""
|
|
13
|
+
import socket
|
|
14
|
+
import sys
|
|
15
|
+
import unittest
|
|
16
|
+
from unittest.mock import MagicMock, patch
|
|
17
|
+
|
|
18
|
+
from sycommon.config.DatabaseConfig import DatabaseConfig, HikariConfig
|
|
19
|
+
|
|
20
|
+
DB_CONFIG = DatabaseConfig(
|
|
21
|
+
url="mysql://user@127.0.0.1:3306/testdb",
|
|
22
|
+
username="user",
|
|
23
|
+
password="secret",
|
|
24
|
+
driver_class_name="com.mysql.cj.jdbc.Driver",
|
|
25
|
+
time_between_eviction_runs_millis=30000,
|
|
26
|
+
min_evictable_idle_time_millis=60000,
|
|
27
|
+
validation_query="SELECT 1",
|
|
28
|
+
test_while_idle=True,
|
|
29
|
+
test_on_borrow=True,
|
|
30
|
+
test_on_return=False,
|
|
31
|
+
hikari=HikariConfig(minimum_idle=5, maximum_pool_size=10, idle_timeout=600000),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _make_async_fake_conn():
|
|
36
|
+
"""模拟 aiomysql 连接(SQLAlchemy 2.x 包装在 AsyncAdapt._connection 里):
|
|
37
|
+
raw socket 挂在内层 _writer.transport 上。"""
|
|
38
|
+
raw_sock = MagicMock(name="raw_socket")
|
|
39
|
+
writer = MagicMock()
|
|
40
|
+
writer.transport.get_extra_info.return_value = raw_sock
|
|
41
|
+
inner = MagicMock()
|
|
42
|
+
inner._writer = writer
|
|
43
|
+
conn = MagicMock(spec=["_connection", "cursor", "character_set_name",
|
|
44
|
+
"rollback", "commit", "autocommit"])
|
|
45
|
+
conn._connection = inner
|
|
46
|
+
conn.cursor.return_value = _make_smart_cursor()
|
|
47
|
+
conn.character_set_name.return_value = "utf8mb4"
|
|
48
|
+
conn.autocommit = False
|
|
49
|
+
return conn, raw_sock
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _make_smart_cursor():
|
|
53
|
+
"""按 SQL 语句返回可解析结果的假 cursor(方言初始化 + 时区钩子都会走它)。"""
|
|
54
|
+
cursor = MagicMock()
|
|
55
|
+
|
|
56
|
+
def execute(sql, *a, **kw):
|
|
57
|
+
cursor._last_sql = sql
|
|
58
|
+
|
|
59
|
+
def fetchone():
|
|
60
|
+
sql = getattr(cursor, "_last_sql", "")
|
|
61
|
+
if "VERSION" in sql:
|
|
62
|
+
return ("8.0.30",)
|
|
63
|
+
if "transaction_isolation" in sql or "tx_isolation" in sql:
|
|
64
|
+
return ("READ-COMMITTED",)
|
|
65
|
+
if "time_zone" in sql:
|
|
66
|
+
return ("+08:00",)
|
|
67
|
+
if "sql_mode" in sql:
|
|
68
|
+
return ("",)
|
|
69
|
+
if "lower_case_table_names" in sql:
|
|
70
|
+
return (0,)
|
|
71
|
+
if "collation" in sql.lower():
|
|
72
|
+
return ("utf8mb4_general_ci",)
|
|
73
|
+
return ("",)
|
|
74
|
+
|
|
75
|
+
cursor.execute.side_effect = execute
|
|
76
|
+
cursor.fetchone.side_effect = fetchone
|
|
77
|
+
return cursor
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _make_sync_fake_conn():
|
|
81
|
+
"""模拟 mysql-connector 连接:raw socket 在 _socket.sock 上。"""
|
|
82
|
+
raw_sock = MagicMock(name="raw_socket")
|
|
83
|
+
conn = MagicMock()
|
|
84
|
+
conn._socket = MagicMock(sock=raw_sock)
|
|
85
|
+
conn.cursor.return_value = _make_smart_cursor()
|
|
86
|
+
conn.get_server_version.return_value = (8, 0, 30)
|
|
87
|
+
conn.autocommit = False
|
|
88
|
+
return conn, raw_sock
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class KeepaliveHookTest(unittest.TestCase):
|
|
92
|
+
"""三个连接器的 keepalive 钩子统一断言。"""
|
|
93
|
+
|
|
94
|
+
def _trigger_hooks(self, engine, fake_conn):
|
|
95
|
+
"""触发 engine 上全部 connect 监听器(public dispatch 路径,与真实建连一致)。"""
|
|
96
|
+
pool = engine.pool if hasattr(engine, "pool") else engine.sync_engine.pool
|
|
97
|
+
pool.dispatch.connect(fake_conn, MagicMock())
|
|
98
|
+
return [fake_conn]
|
|
99
|
+
|
|
100
|
+
def _assert_keepalive_opts(self, raw_sock):
|
|
101
|
+
"""断言与 73f2156 相同的参数集被设置到 raw socket。"""
|
|
102
|
+
calls = raw_sock.setsockopt.call_args_list
|
|
103
|
+
sol_opts = [c.args[1] for c in calls if c.args[0] == socket.SOL_SOCKET]
|
|
104
|
+
self.assertIn(socket.SO_KEEPALIVE, sol_opts, "SO_KEEPALIVE 未开启")
|
|
105
|
+
if sys.platform != "win32":
|
|
106
|
+
calls_ipproto = {c.args[1]: c.args[2] for c in calls
|
|
107
|
+
if c.args[0] == socket.IPPROTO_TCP}
|
|
108
|
+
expected = {
|
|
109
|
+
getattr(socket, "TCP_KEEPIDLE", None): 30,
|
|
110
|
+
getattr(socket, "TCP_KEEPINTVL", None): 10,
|
|
111
|
+
getattr(socket, "TCP_KEEPCNT", None): 3,
|
|
112
|
+
}
|
|
113
|
+
for opt, val in expected.items():
|
|
114
|
+
if opt is not None:
|
|
115
|
+
self.assertEqual(calls_ipproto.get(opt), val,
|
|
116
|
+
f"TCP 选项 {opt} 未按预期设置为 {val}")
|
|
117
|
+
|
|
118
|
+
def test_sync_connector_keepalive(self):
|
|
119
|
+
"""同步 DatabaseConnector:钩子从 _socket.sock 取 raw socket 并调参。"""
|
|
120
|
+
from sycommon.database.database_service import DatabaseConnector
|
|
121
|
+
with patch.object(DatabaseConnector, "test_connection", return_value=True):
|
|
122
|
+
connector = DatabaseConnector(DB_CONFIG)
|
|
123
|
+
fake_conn, raw_sock = _make_sync_fake_conn()
|
|
124
|
+
fired = self._trigger_hooks(connector.engine, fake_conn)
|
|
125
|
+
self.assertTrue(fired, "connect 监听器未被触发")
|
|
126
|
+
self._assert_keepalive_opts(raw_sock)
|
|
127
|
+
# 同步引擎建连参数带上超时(connect_args 在 pool creator 调 dialect.connect
|
|
128
|
+
# 时才合并,故在此处捕获断言)
|
|
129
|
+
with patch.object(connector.engine.dialect, "connect",
|
|
130
|
+
return_value=MagicMock()) as m:
|
|
131
|
+
connector.engine.pool._invoke_creator(MagicMock())
|
|
132
|
+
self.assertEqual(m.call_args.kwargs.get("connect_timeout"), 10)
|
|
133
|
+
|
|
134
|
+
def test_async_connector_keepalive(self):
|
|
135
|
+
"""异步 AsyncDatabaseConnector:钩子从 _writer.transport 取 raw socket。"""
|
|
136
|
+
import asyncio
|
|
137
|
+
from sycommon.database.async_database_service import AsyncDatabaseConnector
|
|
138
|
+
|
|
139
|
+
async def build():
|
|
140
|
+
return AsyncDatabaseConnector(DB_CONFIG)
|
|
141
|
+
|
|
142
|
+
connector = asyncio.new_event_loop().run_until_complete(build())
|
|
143
|
+
fake_conn, raw_sock = _make_async_fake_conn()
|
|
144
|
+
fired = self._trigger_hooks(connector.engine, fake_conn)
|
|
145
|
+
self.assertTrue(fired, "connect 监听器未被触发")
|
|
146
|
+
self._assert_keepalive_opts(raw_sock)
|
|
147
|
+
with patch.object(connector.engine.sync_engine.dialect, "connect",
|
|
148
|
+
return_value=MagicMock()) as m:
|
|
149
|
+
connector.engine.sync_engine.pool._invoke_creator(MagicMock())
|
|
150
|
+
self.assertEqual(m.call_args.kwargs.get("connect_timeout"), 10)
|
|
151
|
+
|
|
152
|
+
def test_token_usage_connector_keepalive(self):
|
|
153
|
+
"""TokenUsageDBConnector:与异步主链路同一套 keepalive 参数。"""
|
|
154
|
+
import asyncio
|
|
155
|
+
from sycommon.database.token_usage_db_service import TokenUsageDBConnector
|
|
156
|
+
|
|
157
|
+
async def build():
|
|
158
|
+
return TokenUsageDBConnector(DB_CONFIG)
|
|
159
|
+
|
|
160
|
+
connector = asyncio.new_event_loop().run_until_complete(build())
|
|
161
|
+
fake_conn, raw_sock = _make_async_fake_conn()
|
|
162
|
+
fired = self._trigger_hooks(connector.engine, fake_conn)
|
|
163
|
+
self.assertTrue(fired, "connect 监听器未被触发")
|
|
164
|
+
self._assert_keepalive_opts(raw_sock)
|
|
165
|
+
|
|
166
|
+
def test_keepalive_failure_does_not_block(self):
|
|
167
|
+
"""setsockopt 抛异常不阻断建连(钩子内部吞掉)。"""
|
|
168
|
+
from sycommon.database.database_service import DatabaseConnector
|
|
169
|
+
with patch.object(DatabaseConnector, "test_connection", return_value=True):
|
|
170
|
+
connector = DatabaseConnector(DB_CONFIG)
|
|
171
|
+
fake_conn, raw_sock = _make_sync_fake_conn()
|
|
172
|
+
raw_sock.setsockopt.side_effect = OSError("perm")
|
|
173
|
+
# 不应抛异常
|
|
174
|
+
self._trigger_hooks(connector.engine, fake_conn)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
unittest.main()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""验证本次依赖升级 + 补丁适配的正确性(纯离线,不依赖 RabbitMQ/真实 LLM)。
|
|
2
2
|
|
|
3
3
|
覆盖本次改动:
|
|
4
|
-
A. 依赖版本落地:langchain 1.3.
|
|
4
|
+
A. 依赖版本落地:langchain 1.3.18 / langchain_openai 1.6.0 / langchain_core 1.6.1。
|
|
5
5
|
B. summarization_utils.py 的 ContextOverflow 补丁:在当前版本上成功替换
|
|
6
6
|
_handle_openai_bad_request / _handle_openai_api_error,且:
|
|
7
7
|
- MiniMax/vLLM 句式 "maximum context length is ..." 仍被正确转成
|
|
@@ -28,12 +28,12 @@ def test_installed_versions():
|
|
|
28
28
|
import langchain_openai
|
|
29
29
|
import langchain_core
|
|
30
30
|
|
|
31
|
-
assert langchain.__version__ == "1.3.
|
|
32
|
-
f"langchain 应为 1.3.
|
|
33
|
-
assert langchain_openai.__version__ == "1.
|
|
34
|
-
f"langchain_openai 应为 1.
|
|
35
|
-
assert langchain_core.__version__ == "1.
|
|
36
|
-
f"langchain_core 应为 1.
|
|
31
|
+
assert langchain.__version__ == "1.3.18", (
|
|
32
|
+
f"langchain 应为 1.3.18, 实际 {langchain.__version__}")
|
|
33
|
+
assert langchain_openai.__version__ == "1.6.0", (
|
|
34
|
+
f"langchain_openai 应为 1.6.0, 实际 {langchain_openai.__version__}")
|
|
35
|
+
assert langchain_core.__version__ == "1.6.1", (
|
|
36
|
+
f"langchain_core 应为 1.6.1, 实际 {langchain_core.__version__}")
|
|
37
37
|
print(f" [OK] langchain={langchain.__version__} "
|
|
38
38
|
f"langchain_openai={langchain_openai.__version__} "
|
|
39
39
|
f"langchain_core={langchain_core.__version__}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10a4
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -13,36 +13,36 @@ Requires-Dist: deepagents==0.7.6
|
|
|
13
13
|
Requires-Dist: elasticsearch==9.5.0
|
|
14
14
|
Requires-Dist: fastapi==0.141.1
|
|
15
15
|
Requires-Dist: jinja2==3.1.6
|
|
16
|
-
Requires-Dist: kafka-python==3.0.
|
|
17
|
-
Requires-Dist: langchain==1.3.
|
|
18
|
-
Requires-Dist: langchain-core==1.
|
|
19
|
-
Requires-Dist: langchain-openai==1.
|
|
20
|
-
Requires-Dist: langfuse==4.
|
|
16
|
+
Requires-Dist: kafka-python==3.0.11
|
|
17
|
+
Requires-Dist: langchain==1.3.18
|
|
18
|
+
Requires-Dist: langchain-core==1.6.1
|
|
19
|
+
Requires-Dist: langchain-openai==1.6.0
|
|
20
|
+
Requires-Dist: langfuse==4.15.1
|
|
21
21
|
Requires-Dist: langgraph==1.2.11
|
|
22
22
|
Requires-Dist: langgraph-checkpoint-postgres==3.1.2
|
|
23
|
-
Requires-Dist: langgraph-checkpoint-redis==0.5.
|
|
23
|
+
Requires-Dist: langgraph-checkpoint-redis==0.5.2
|
|
24
24
|
Requires-Dist: ldap3==2.9.1
|
|
25
25
|
Requires-Dist: loguru==0.7.3
|
|
26
26
|
Requires-Dist: mysql-connector-python==26.7.0
|
|
27
27
|
Requires-Dist: nacos-sdk-python<3.0,==2.0.11
|
|
28
28
|
Requires-Dist: psutil==7.2.2
|
|
29
29
|
Requires-Dist: pyxxl==0.4.8
|
|
30
|
-
Requires-Dist: pydantic==2.13.
|
|
31
|
-
Requires-Dist: python-dotenv==1.2.
|
|
30
|
+
Requires-Dist: pydantic==2.13.5
|
|
31
|
+
Requires-Dist: python-dotenv==1.2.3
|
|
32
32
|
Requires-Dist: python-multipart==0.0.32
|
|
33
33
|
Requires-Dist: pyyaml==6.0.3
|
|
34
34
|
Requires-Dist: redis==7.4.0
|
|
35
|
-
Requires-Dist: sentry-sdk[fastapi]==2.68.
|
|
35
|
+
Requires-Dist: sentry-sdk[fastapi]==2.68.1
|
|
36
36
|
Requires-Dist: sqlalchemy[asyncio]==2.0.52
|
|
37
37
|
Requires-Dist: starlette[full]==1.6.0
|
|
38
|
-
Requires-Dist: tiktoken==0.
|
|
39
|
-
Requires-Dist: uvicorn==0.52.
|
|
38
|
+
Requires-Dist: tiktoken==0.14.0
|
|
39
|
+
Requires-Dist: uvicorn==0.52.4
|
|
40
40
|
Requires-Dist: wecom-aibot-python-sdk==1.0.2
|
|
41
41
|
Requires-Dist: twine==7.0.0
|
|
42
42
|
Requires-Dist: minio==7.2.20
|
|
43
43
|
Requires-Dist: langchain-mcp-adapters==0.3.2
|
|
44
44
|
Requires-Dist: fastapi-mcp==0.4.0
|
|
45
|
-
Requires-Dist: psycopg[binary,pool]==3.3.
|
|
45
|
+
Requires-Dist: psycopg[binary,pool]==3.3.5
|
|
46
46
|
Requires-Dist: agent-client-protocol==0.11.0
|
|
47
47
|
Requires-Dist: langchain-acp==1.7.0
|
|
48
48
|
Requires-Dist: pypinyin==0.55.0
|
|
@@ -137,8 +137,8 @@ sycommon/services.py,sha256=BalCSLqx0aictqEsyUOUXimjqdMGbZgZyd0MqViaBI0,27315
|
|
|
137
137
|
sycommon/agent/__init__.py,sha256=mxceAeUifQ-DKvWp7ZEJIFlmOCb5wpYHPGQw3rwEN8I,4378
|
|
138
138
|
sycommon/agent/agent_manager.py,sha256=UhhaekEumT7g4v_Z1UB4jTp13X0n8M8erYaQdkGGWkA,13620
|
|
139
139
|
sycommon/agent/chat_events.py,sha256=t7qWa6OrIWLfqtd1AnqaVP67QWkn1JxpCBTZYQpoLuM,14226
|
|
140
|
-
sycommon/agent/deep_agent.py,sha256=
|
|
141
|
-
sycommon/agent/multi_agent_team.py,sha256=
|
|
140
|
+
sycommon/agent/deep_agent.py,sha256=gBQ2rzQIS53PZhaU50zIsJ3cWMEg_IaBbH_IzycmHGU,87895
|
|
141
|
+
sycommon/agent/multi_agent_team.py,sha256=GRXjjrPblsMnLjY2aB5CeFJ6-F7wvPhUc_JxNjfBXes,34789
|
|
142
142
|
sycommon/agent/reasoning_content_patch.py,sha256=HZ_If0qVDHlz_b1mDT3tuTqbExhzRVhP4VgsoDt0JLc,3945
|
|
143
143
|
sycommon/agent/resume_compactor.py,sha256=CJp3AUqeStr8U2JeWP31DyulomXZdrkLL4kVtLxnwn8,20849
|
|
144
144
|
sycommon/agent/skills_retry_patch.py,sha256=LwlNIQdl0Hz9tywT5foPipULReMlWJyRK5nGibgp948,4049
|
|
@@ -154,7 +154,7 @@ sycommon/agent/mcp/models.py,sha256=zda4ho-UTYOJ5oPAZPZ-vxhfDRLghUCsMroHnA2A2QQ,
|
|
|
154
154
|
sycommon/agent/mcp/tool_loader.py,sha256=pwhZoSoZuQVYo6BgfdMoRHlGnRPDRxvOvxEgvUM5q7Q,17818
|
|
155
155
|
sycommon/agent/middleware/model_request_prep.py,sha256=W0dTgdFo6RI0Tg9jdN7jSB9QOroQR5XHIrm2hw5lslk,7618
|
|
156
156
|
sycommon/agent/middleware/sandbox_path_guard.py,sha256=K1SnxA8pDxkCSucOkranwUvC9_8rd3l_-TIR8gMMTK8,8102
|
|
157
|
-
sycommon/agent/middleware/sensitive_guard.py,sha256=
|
|
157
|
+
sycommon/agent/middleware/sensitive_guard.py,sha256=TN1SEFzUborxx7l24-XvR6TZY0s5G61kTbJnyaB11tE,32350
|
|
158
158
|
sycommon/agent/middleware/skill_write_guard.py,sha256=ZLW-OIG56z5LvLRMaMnryhPz_O5PbrdTLuHTfC3PveA,3101
|
|
159
159
|
sycommon/agent/sandbox/__init__.py,sha256=jR7LlkD4J4Y6QYyRXQClkwmqDBCCPmycV_hQV9p9YHw,4621
|
|
160
160
|
sycommon/agent/sandbox/consistent_hash.py,sha256=8Jgk-W4NAD2-u5_vKRVlPmql_e0Vy4aNORiXOuzvIrs,7277
|
|
@@ -187,14 +187,14 @@ sycommon/config/XxlJobConfig.py,sha256=VSG6dn9ysfUVunOs7PqugyZUGJWmX_cEePz2ZCfqH
|
|
|
187
187
|
sycommon/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
188
188
|
sycommon/config/config_change_notifier.py,sha256=7qQe7vdBS1zohEfVq0d5IaDqYsLdgd2ckF4mKKgh0gE,6848
|
|
189
189
|
sycommon/database/async_base_db_service.py,sha256=8mPZ2yI1hIYvuHYxcpeU0gus_1WGjrpRqv2bm9TIBCk,6186
|
|
190
|
-
sycommon/database/async_database_service.py,sha256=
|
|
190
|
+
sycommon/database/async_database_service.py,sha256=Ghop6On0H2H1yZBhP3xt8EKvYjAZ1kjyjDpDP9dhE_g,12985
|
|
191
191
|
sycommon/database/base_db_service.py,sha256=urdAibaCUt3hxwwKzEMBceWkIwsw3ja_WDJ1QKc8nLU,4116
|
|
192
|
-
sycommon/database/database_service.py,sha256=
|
|
192
|
+
sycommon/database/database_service.py,sha256=mspi98qQnzl7gvaEjGM40lvM_LfjGdQZR1jp5RaFDkc,8974
|
|
193
193
|
sycommon/database/elasticsearch_service.py,sha256=BJnBcoeSDtK1a1IcCyLbzQaOMb5hCfIh0wgBRQ9XmpU,4257
|
|
194
194
|
sycommon/database/pg_checkpoint_service.py,sha256=lR73iZTDJDZc7tfx4_D6vjdlJAUTDe49zAOa3QIkBsg,7342
|
|
195
|
-
sycommon/database/redis_service.py,sha256=
|
|
195
|
+
sycommon/database/redis_service.py,sha256=Gz9ovGIeHZdcvY7_DTG_pU1t1OqfV29cS9CvN6A0q5E,22598
|
|
196
196
|
sycommon/database/resilient_pg_saver.py,sha256=S5hOoF7gGTa0b4-P3dM_iLy1GFesG_jEnIxv-WRwd3s,7148
|
|
197
|
-
sycommon/database/token_usage_db_service.py,sha256=
|
|
197
|
+
sycommon/database/token_usage_db_service.py,sha256=iYK51ZtGNpBA4nj2IhflltbftE5Fsc3C0UksSJr-phY,10152
|
|
198
198
|
sycommon/health/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
199
199
|
sycommon/health/health_check.py,sha256=EhfbhspRpQiKJaxdtE-PzpKQO_ucaFKtQxIm16F5Mpk,391
|
|
200
200
|
sycommon/health/metrics.py,sha256=fHqO73JuhoZkNPR-xIlxieXiTCvttq-kG-tvxag1s1s,268
|
|
@@ -202,7 +202,7 @@ sycommon/health/ping.py,sha256=FTlnIKk5y1mPfS1ZGOeT5IM_2udF5aqVLubEtuBp18M,250
|
|
|
202
202
|
sycommon/heartbeat_process/__init__.py,sha256=8HPwckgg7UW6dhf_h71BidiZy6Uj5Q2la8OnmqVcPSU,1899
|
|
203
203
|
sycommon/heartbeat_process/heartbeat_config.py,sha256=qbxxsYYhYNS5jEQ644aYmJB1wJCnhwFn04m92EQ0iSg,5486
|
|
204
204
|
sycommon/heartbeat_process/heartbeat_process_manager.py,sha256=24qUKs8qegdWHqcoxL0gTaypjNHoSCFDwlbzi5SgkYw,9252
|
|
205
|
-
sycommon/heartbeat_process/heartbeat_process_worker.py,sha256=
|
|
205
|
+
sycommon/heartbeat_process/heartbeat_process_worker.py,sha256=18XtfMTYrbOeC9EsZF5oKTUb2vNIoV8JzqiGqemwiv4,17064
|
|
206
206
|
sycommon/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
207
207
|
sycommon/llm/embedding.py,sha256=n2PtKlfJ4E1-V8DIHTilR-y9y1TYB5iaATlWOKjid28,19878
|
|
208
208
|
sycommon/llm/get_llm.py,sha256=DK-kZSz6NtQ8lpbEdleLyYXm7qnAJwAItDKUqF6Xxkw,10079
|
|
@@ -243,9 +243,9 @@ sycommon/middleware/sandbox_gateway.py,sha256=ZyXV9iwvaLNNTk0oM-RdjGRSgkBPaPp67A
|
|
|
243
243
|
sycommon/middleware/timeout.py,sha256=KlxOPa8xl2dg6yuRi_EzkVJG8bX4stb5ueYxctzzGM8,1433
|
|
244
244
|
sycommon/middleware/token_tracking.py,sha256=rEbgV1bgWMdzAERx4aq5XAvOIT6jTY_tK1P0xHJnL3o,6609
|
|
245
245
|
sycommon/middleware/tool_result_truncation.py,sha256=xtwBf8q11Z8EPzmo3Ju4OfUbULOklSsWo_oystxCW0o,13640
|
|
246
|
-
sycommon/middleware/traceid.py,sha256=
|
|
246
|
+
sycommon/middleware/traceid.py,sha256=NZ2zLtqnd3MfNSGMal2pfnKlc57t-CLIi5j1L1NAaFU,19865
|
|
247
247
|
sycommon/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
248
|
-
sycommon/models/base_http.py,sha256=
|
|
248
|
+
sycommon/models/base_http.py,sha256=pLa6IBWZtVy92bKiZ8_Sdo5jUjlojAqhJ1TH4C-nxvQ,6042
|
|
249
249
|
sycommon/models/log.py,sha256=rZpj6VkDRxK3B6H7XSeWdYZshU8F0Sks8bq1p6pPlDw,500
|
|
250
250
|
sycommon/models/mcp_server_config.py,sha256=cm_oDEDXVJR4nCKgWPli5zFI0DUy6xtvRs-___EWAW8,1768
|
|
251
251
|
sycommon/models/mqlistener_config.py,sha256=ZFGg_vc9HcIV22L5Jgrynkjj5B6Ci8ETQNUbNUiXrVw,1830
|
|
@@ -257,7 +257,7 @@ sycommon/models/token_usage.py,sha256=w3Aash7jUVSUIgKxBUzI6SqJ9VV5Pk7rLBbMUlLv6n
|
|
|
257
257
|
sycommon/models/token_usage_mysql.py,sha256=eWnCvI2FDwyTA_fr4wWQtAmUnFJgLwrt0WXMXHLAESQ,2633
|
|
258
258
|
sycommon/models/xxljob_handler_config.py,sha256=XiSv9AlJ8UsLRai4hvTMeiSZ6VsO5Ijx8HcDwLxJL64,376
|
|
259
259
|
sycommon/notice/__init__.py,sha256=cVYbKlLLMFHckyDpx21CTZCMTufqZk_uHuEIxUNxwx4,104
|
|
260
|
-
sycommon/notice/uvicorn_monitor.py,sha256=
|
|
260
|
+
sycommon/notice/uvicorn_monitor.py,sha256=68VC2d6JvlVsH-Ev87JS-TIbCBGhIjneg3Uxx9LMkOg,12308
|
|
261
261
|
sycommon/notice/wecom_message.py,sha256=_oA6hg0Ask7NkFbvUpL78WOmyUb6uW7KcY71GhNKiJY,12363
|
|
262
262
|
sycommon/rabbitmq/process_pool_consumer.py,sha256=EXdNypgathjTfPqkccoAW0HiVB9HuZLGhPbWUcYh0_Q,29750
|
|
263
263
|
sycommon/rabbitmq/rabbit_management_client.py,sha256=cf7GSbvdzOA6vFV4jC3IL6wh8yu8IEDgTPHUqRV2_uE,4021
|
|
@@ -279,7 +279,7 @@ sycommon/synacos/example.py,sha256=FOnBkvodR8WF_jf-RovM3ngVmvZQX6wKwMLscUTGn2M,8
|
|
|
279
279
|
sycommon/synacos/example2.py,sha256=yYuQscfHUIl1HLZ8kSRBuZpHUcNWZMi5H3Mb-LjYnvk,8136
|
|
280
280
|
sycommon/synacos/feign.py,sha256=RU6p2gRP3LZoHYBBEUUY9z5KKzGiUmwj4qdo7aF9UNA,12610
|
|
281
281
|
sycommon/synacos/feign_client.py,sha256=i6O20JWl1g2fjD-et7olFaO-0z2yEbKT8-pKluAHCgs,19917
|
|
282
|
-
sycommon/synacos/nacos_client_base.py,sha256=
|
|
282
|
+
sycommon/synacos/nacos_client_base.py,sha256=dBKtGG1g-in8cAILHUVxPmfZZ3fNEuYMDFAHBgTYfJM,7337
|
|
283
283
|
sycommon/synacos/nacos_config_manager.py,sha256=LdmMwyQcc09K0fNWCuSqobyV-wdSW_qVZXb-TEASgxY,8330
|
|
284
284
|
sycommon/synacos/nacos_heartbeat_manager.py,sha256=LfimUKpG4KaqsVQl150sg3MLK8psanuUwQ07tjL3uBE,10963
|
|
285
285
|
sycommon/synacos/nacos_service.py,sha256=nmL007c2SBMHaVJXlzBHV2_R8whfl_X_rmgCpjRDqZw,12319
|
|
@@ -295,6 +295,7 @@ sycommon/tests/test_consumer_loss_rootcause.py,sha256=rP_XdbGbzOM9RB36vk0gU4kqip
|
|
|
295
295
|
sycommon/tests/test_consumer_recovery.py,sha256=pMMtOwHIijNTZxg3IVBYSGFYgezMX0PUeylks8xzMM8,10364
|
|
296
296
|
sycommon/tests/test_consumer_recovery_fix.py,sha256=q2OW0-KEsTqHpelNIDr15G8p-ZOC49rXGxq2wqBmFZM,12857
|
|
297
297
|
sycommon/tests/test_consumer_tags_sync.py,sha256=E9BZs9WGUIE5ygHsGzLlaohG2qlAVnkpOcjaYW9WHkg,5702
|
|
298
|
+
sycommon/tests/test_db_keepalive.py,sha256=xp2MSwMpVFjq4gdT0UCFptUKau95xiwJHUXzXGs3YBM,7576
|
|
298
299
|
sycommon/tests/test_deep_agent.py,sha256=YDbDdMbTjUjLjgGjT1kPwyeMRQ4fqjliJd71dJ0rOPA,5918
|
|
299
300
|
sycommon/tests/test_dynamic_max_tokens.py,sha256=mk2QXZIxpyClEBvS9g88NXr9xmXoNdVrIjFAsN5t6CQ,17934
|
|
300
301
|
sycommon/tests/test_email.py,sha256=-oPtYVGQzJ3Cv-op3ZNRMeyYOF-UNidGJC35CHRgjGQ,5442
|
|
@@ -322,7 +323,7 @@ sycommon/tests/test_summarization_real.py,sha256=iTwwA_xQd5Zgkn849lu2M0zIHPnMp-3
|
|
|
322
323
|
sycommon/tests/test_summary_leak_e2e.py,sha256=vE5r9QCcZRmUy3YQASwdwCBsVrRcl8vU95sLUyB6u9s,34739
|
|
323
324
|
sycommon/tests/test_upgrade_aio_pika.py,sha256=HIvy0Gs-aNNGX6Q3Y2fwSq2nDODbsWt7kl7-bk3gTk0,6115
|
|
324
325
|
sycommon/tests/test_upgrade_langgraph_stream.py,sha256=1FEVuv8FPNeLOB6yht6NoQifHoiCtkrtBI4pB_PQYTE,5443
|
|
325
|
-
sycommon/tests/test_upgrade_verification.py,sha256=
|
|
326
|
+
sycommon/tests/test_upgrade_verification.py,sha256=HS0KQ1q-Djolf1Km2xMFNKtoUHKLNLjacTHv_Wh5O8Y,7589
|
|
326
327
|
sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
327
328
|
sycommon/tools/async_utils.py,sha256=e2Bp8v0LDrnViBIZ3zcCWtm_01w67vG34VhnAyGu9ZM,1015
|
|
328
329
|
sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
|
|
@@ -335,8 +336,8 @@ sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
|
335
336
|
sycommon/tools/user_id.py,sha256=o-zsubGSR5rSSZf8Pk8B7AhE21ahDTTIW5JZmJO6Flw,4761
|
|
336
337
|
sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
|
|
337
338
|
sycommon/xxljob/xxljob_service.py,sha256=1yifwIBNGsCIxLnQjHKiBlbsigc_zvPH-dMTZcNxe-Q,7649
|
|
338
|
-
sycommon_python_lib-0.2.
|
|
339
|
-
sycommon_python_lib-0.2.
|
|
340
|
-
sycommon_python_lib-0.2.
|
|
341
|
-
sycommon_python_lib-0.2.
|
|
342
|
-
sycommon_python_lib-0.2.
|
|
339
|
+
sycommon_python_lib-0.2.10a4.dist-info/METADATA,sha256=LEDBGdzj5bGZDBhJBAJ0w7v0IeSMsw9MQJsoxrXYCKM,8033
|
|
340
|
+
sycommon_python_lib-0.2.10a4.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
341
|
+
sycommon_python_lib-0.2.10a4.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
|
|
342
|
+
sycommon_python_lib-0.2.10a4.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
|
|
343
|
+
sycommon_python_lib-0.2.10a4.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{sycommon_python_lib-0.2.10a2.dist-info → sycommon_python_lib-0.2.10a4.dist-info}/top_level.txt
RENAMED
|
File without changes
|