sycommon-python-lib 0.2.8a0__py3-none-any.whl → 0.2.8a1__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.
@@ -160,6 +160,10 @@ class AgentConfig(BaseModel):
160
160
  # (scoped 由服务端路由硬编码,user 由 MinIO 按 user 灌入)。
161
161
  system_skills_allow: Optional[set[str]] = None
162
162
  memory_dir: Optional[str] = None
163
+ # 额外 middleware(应用层注入,如技能使用上报、自定义拦截):
164
+ # 数字员工等项目自定义 AgentMiddleware 实例列表,create_deep_agent 会
165
+ # 追加到内置 middleware_list 之后执行。类型用 list 而非具体类避免循环依赖。
166
+ extra_middleware: list = []
163
167
 
164
168
  @property
165
169
  def skills_dir(self) -> Optional[str]:
@@ -1118,6 +1122,9 @@ async def create_deep_agent(
1118
1122
  SkillWriteGuardMiddleware(), # 系统 /skills/system 只读守卫(先于白名单)
1119
1123
  SandboxPathGuardMiddleware(user_id), # 宿主绝对路径规范化/拦截(防越权读他人沙箱文件)
1120
1124
  ]
1125
+ # 应用层注入的额外 middleware(如技能使用上报),排在内置之后
1126
+ if config.extra_middleware:
1127
+ middleware_list.extend(config.extra_middleware)
1121
1128
 
1122
1129
  # RubricMiddleware:复用 FilesystemMiddleware 的工具(底层走 HTTPSandboxBackend HTTP 远程调用)
1123
1130
  if config.rubric_max_iterations > 0 and sandbox_backend:
@@ -142,6 +142,8 @@ class TeamConfig(BaseModel):
142
142
 
143
143
  sandbox_service_name: str = "shengye-platform-sandbox"
144
144
  sandbox_timeout: int = 300
145
+ # 额外 middleware(应用层注入,如技能使用上报)
146
+ extra_middleware: list = []
145
147
  # system 技能:本地仓库目录 → 同步到沙箱 /skills/system(只读)
146
148
  system_skills_dir: Optional[str] = None
147
149
  # user 技能:标记启用沙箱 /skills/user(可写,内容由 MinIO 灌入)
@@ -685,6 +687,8 @@ async def create_multi_agent_team(
685
687
  SkillWriteGuardMiddleware(),
686
688
  SandboxPathGuardMiddleware(user_id),
687
689
  ]
690
+ if config.extra_middleware:
691
+ middleware.extend(config.extra_middleware)
688
692
  shared = config.shared_tools or [get_current_date]
689
693
 
690
694
  def _build_subagent(member: TeamMember) -> CompiledSubAgent:
sycommon/config/Config.py CHANGED
@@ -114,6 +114,30 @@ class Config(metaclass=SingletonMeta):
114
114
  return es
115
115
  raise ValueError(f"No configuration found for elasticsearch: {name}")
116
116
 
117
+ # 命名空间 → 默认租户 ID:prod/wsuat1 共享同一租户,其余环境另一租户
118
+ _PROD_NAMESPACES = ("prod", "wsuat1")
119
+ _DEFAULT_TENANT_PROD = "T000002"
120
+ _DEFAULT_TENANT_OTHER = "T000003"
121
+
122
+ def get_namespace_id(self, default=''):
123
+ """获取当前环境标识(Nacos namespaceId,如 'prod'/'wsuat1'/'dev')。
124
+
125
+ Args:
126
+ default: 配置缺失时的默认值,按调用场景传入:
127
+ 告警展示用 '未知环境',功能判断用 'dev'/'prod',缓存哨兵用 None。
128
+ """
129
+ return self.config.get('Nacos', {}).get('namespaceId', default)
130
+
131
+ def get_tenant_id(self):
132
+ """根据当前环境推导系统默认租户 ID。
133
+
134
+ prod / wsuat1 → "T000002",其余环境 → "T000003"。
135
+ 仅作为系统内部消息(如 RabbitMQ SYSTEM 消息)的兜底默认值;
136
+ 若调用方有请求上下文中的真实租户(x-tenant-header),应优先使用它。
137
+ """
138
+ ns = self.get_namespace_id()
139
+ return self._DEFAULT_TENANT_PROD if ns in self._PROD_NAMESPACES else self._DEFAULT_TENANT_OTHER
140
+
117
141
  def _process_config(self):
118
142
  # 清空列表,防止 set_attr 多次调用时重复累积
119
143
  self.llm_configs.clear()
@@ -29,12 +29,47 @@ def _is_retryable_tx_error(exc: Exception) -> bool:
29
29
 
30
30
 
31
31
  class AsyncBaseDBService(metaclass=SingletonMeta):
32
- """数据库操作基础服务类,封装异步会话管理功能"""
32
+ """数据库操作基础服务类,封装异步会话管理功能
33
+
34
+ engine 不在 __init__ 时绑定,而是每次 session 时按当前 event loop 动态
35
+ 获取(AsyncDatabaseService.engine() 按 loop 隔离)。这样子类即使作为
36
+ 模块级单例在 import 时实例化(此时 DB 未初始化、loop 未确定),也不会
37
+ 把 engine 绑死在错误的时机/loop 上;后台线程在独立 loop 调用时同样能
38
+ 拿到本 loop 的连接池。
39
+ """
33
40
 
34
41
  def __init__(self):
35
- self.engine = AsyncDatabaseService.engine()
36
- self.Session = async_sessionmaker(
37
- bind=self.engine,
42
+ # 不缓存 engine/Session,延迟到每次使用时按当前 loop 创建,
43
+ # _session_factory()。保留 engine 只读属性兼容旧子类(见下)。
44
+ pass
45
+
46
+ @property
47
+ def engine(self):
48
+ """当前 loop 的 engine(兼容旧子类直接访问 self.engine 的写法)
49
+
50
+ 注意:仅在调试/日志场景使用;正式会话请走 session()/execute(),
51
+ 不要把返回的 engine 跨 loop 缓存复用。
52
+ """
53
+ return AsyncDatabaseService.engine()
54
+
55
+ @property
56
+ def Session(self) -> async_sessionmaker:
57
+ """当前 loop 的会话工厂(兼容旧子类直接访问 self.Session 的写法)
58
+
59
+ 用法与旧版一致:`async with self.Session() as session: ...`
60
+ 但每次访问都按当前 loop 重新构建,不要将其返回值跨 loop 缓存。
61
+ """
62
+ return self._session_factory()
63
+
64
+ def _session_factory(self) -> async_sessionmaker:
65
+ """按当前 loop 创建会话工厂(engine 由 AsyncDatabaseService 按 loop 提供)"""
66
+ engine = AsyncDatabaseService.engine()
67
+ if engine is None:
68
+ raise RuntimeError(
69
+ "数据库未初始化:AsyncDatabaseService.engine() 返回 None,"
70
+ "请先通过 Services.plugins(database_service=...) 完成初始化")
71
+ return async_sessionmaker(
72
+ bind=engine,
38
73
  class_=AsyncSession,
39
74
  expire_on_commit=False
40
75
  )
@@ -45,7 +80,7 @@ class AsyncBaseDBService(metaclass=SingletonMeta):
45
80
  异步数据库会话上下文管理器
46
81
  自动处理会话的创建、提交、回滚和关闭
47
82
  """
48
- async with self.Session() as session:
83
+ async with self._session_factory()() as session:
49
84
  try:
50
85
  yield session
51
86
  await session.commit()
@@ -81,7 +116,7 @@ class AsyncBaseDBService(metaclass=SingletonMeta):
81
116
  last_exc = None
82
117
 
83
118
  while True:
84
- async with self.Session() as session:
119
+ async with self._session_factory()() as session:
85
120
  try:
86
121
  result = await fn(session)
87
122
  await session.commit()
@@ -1,5 +1,6 @@
1
1
  from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
2
2
  from sqlalchemy import text, event
3
+ import asyncio
3
4
 
4
5
  from sycommon.config.Config import SingletonMeta
5
6
  from sycommon.config.DatabaseConfig import DatabaseConfig, convert_dict_keys
@@ -9,7 +10,19 @@ from sycommon.synacos.nacos_service import NacosService
9
10
 
10
11
 
11
12
  class AsyncDatabaseService(metaclass=SingletonMeta):
12
- _engine = None
13
+ # event loop 隔离的 engine 池:{loop: AsyncEngine}
14
+ #
15
+ # 为什么不是全局单 engine:aiomysql 连接的 socket stream 在首次读写时
16
+ # 绑定到当时的 event loop,跨 loop 复用会抛
17
+ # "got Future attached to a different loop"。
18
+ # 业务服务的后台线程(如定时刷盘)通常跑在独立 loop,必须各用各的连接。
19
+ #
20
+ # 兼容:engine() 保持无参,自动按"当前 running loop"返回对应 engine;
21
+ # 主 loop(setup_database 所在)返回初始化的那个,其他 loop 首次访问时
22
+ # 按同一份配置懒创建。单 loop 项目行为与之前完全一致。
23
+ _engines: dict = {}
24
+ _main_loop = None # setup_database 时的 loop
25
+ _db_config = None # DatabaseConfig,供其他 loop 懒建 engine
13
26
  # setup 时记录入参,供 reload() 重建使用
14
27
  _setup_args = None
15
28
 
@@ -24,8 +37,15 @@ class AsyncDatabaseService(metaclass=SingletonMeta):
24
37
  # 初始化 DatabaseConnector (传入配置)
25
38
  connector = AsyncDatabaseConnector(db_config)
26
39
 
27
- # 赋值 engine
28
- AsyncDatabaseService._engine = connector.engine
40
+ # 记录主 loop 与其 engine
41
+ try:
42
+ AsyncDatabaseService._main_loop = asyncio.get_running_loop()
43
+ except RuntimeError:
44
+ AsyncDatabaseService._main_loop = None
45
+ AsyncDatabaseService._db_config = db_config
46
+ AsyncDatabaseService._engines = {
47
+ AsyncDatabaseService._main_loop: connector.engine
48
+ }
29
49
 
30
50
  # 执行异步测试连接
31
51
  if not await connector.test_connection():
@@ -39,43 +59,90 @@ class AsyncDatabaseService(metaclass=SingletonMeta):
39
59
 
40
60
  @staticmethod
41
61
  def engine():
42
- return AsyncDatabaseService._engine
62
+ """返回当前 running loop 对应的 engine。
63
+
64
+ 主 loop 返回初始化的共享 engine;其他 loop(后台线程)首次调用时
65
+ 按同一份数据库配置懒创建独立 engine(连接归该 loop 专用)。
66
+ 无 running loop(纯同步上下文)时回退主 engine。
67
+ """
68
+ try:
69
+ loop = asyncio.get_running_loop()
70
+ except RuntimeError:
71
+ # 同步上下文(如 ORM 事件回调、脚本),回退主 engine
72
+ return AsyncDatabaseService._engines.get(
73
+ AsyncDatabaseService._main_loop)
74
+
75
+ if loop in AsyncDatabaseService._engines:
76
+ return AsyncDatabaseService._engines[loop]
77
+
78
+ # 其他 loop 首次访问:懒建专属 engine
79
+ if AsyncDatabaseService._db_config is None:
80
+ return None
81
+ engine = AsyncDatabaseService._build_engine()
82
+ AsyncDatabaseService._engines[loop] = engine
83
+ return engine
84
+
85
+ @staticmethod
86
+ def _build_engine():
87
+ """按当前 running loop 创建 engine(内部使用)"""
88
+ connector = AsyncDatabaseConnector(AsyncDatabaseService._db_config)
89
+ SYLogger.info(
90
+ f"AsyncDatabaseService 为非主 loop 创建独立 engine "
91
+ f"(共 {len(AsyncDatabaseService._engines) + 1} 个)")
92
+ return connector.engine
43
93
 
44
94
  @staticmethod
45
95
  async def dispose() -> None:
46
- """释放异步引擎连接池资源"""
47
- if AsyncDatabaseService._engine is not None:
96
+ """释放所有 engine 的连接池资源(在各 loop 内安全地尽力释放)"""
97
+ engines = list(AsyncDatabaseService._engines.values())
98
+ AsyncDatabaseService._engines = {}
99
+ AsyncDatabaseService._main_loop = None
100
+ for engine in engines:
101
+ if engine is None:
102
+ continue
48
103
  try:
49
- await AsyncDatabaseService._engine.dispose()
104
+ # dispose 是协程但内部只做连接关闭,跨 loop 调用对
105
+ # 空闲连接安全;忙碌连接的 loop 此时应已停止
106
+ await engine.dispose()
50
107
  except Exception as e:
51
108
  SYLogger.error(f"AsyncDatabase engine dispose error: {e}")
52
- finally:
53
- AsyncDatabaseService._engine = None
54
109
 
55
110
  @classmethod
56
111
  async def reload(cls) -> None:
57
112
  """配置热更新:dispose 旧引擎并按最新配置重建,失败回退。"""
58
113
  if not cls._setup_args:
59
114
  return
60
- old_engine = cls._engine
61
- # 清掉 AsyncDatabaseConnector 单例缓存,使其按新配置重建
62
- from sycommon.config.Config import SingletonMeta
63
- SingletonMeta._instances.pop(AsyncDatabaseConnector, None)
115
+ old_engines = dict(cls._engines)
116
+ old_main_loop = cls._main_loop
64
117
  try:
65
118
  await cls.setup_database(*cls._setup_args)
66
119
  except Exception as e:
67
120
  SYLogger.error(f"AsyncDatabaseService reload 重建失败: {e}", exc_info=True)
68
- cls._engine = old_engine
121
+ cls._engines = old_engines
122
+ cls._main_loop = old_main_loop
69
123
  return
70
- # 新 engine 建好,释放旧的
71
- if old_engine is not None:
124
+ # 新 engine 建好,释放旧的(含其他 loop 的,其连接已失效,
125
+ # 下次访问会按新 _db_config 自动重建)
126
+ for loop, old_engine in old_engines.items():
127
+ if old_engine is None:
128
+ continue
72
129
  try:
73
130
  await old_engine.dispose()
74
131
  except Exception as e:
75
132
  SYLogger.error(f"AsyncDatabaseService reload 释放旧 engine 失败: {e}")
76
133
 
77
134
 
78
- class AsyncDatabaseConnector(metaclass=SingletonMeta):
135
+ class AsyncDatabaseConnector:
136
+ """数据库连接器(每次实例化都新建 engine)
137
+
138
+ 不能用 SingletonMeta:AsyncDatabaseService 按 event loop 隔离 engine,
139
+ 需要为每个 loop 各建一个连接池;单例会让所有 loop 拿到同一个 engine,
140
+ 跨 loop 使用必抛 "attached to a different loop"。
141
+
142
+ 注意:reload() 原本靠 `SingletonMeta._instances.pop(AsyncDatabaseConnector)`
143
+ 清缓存强制重建——去掉单例后每次实例化本身就是全新 engine,无需再清。
144
+ """
145
+
79
146
  def __init__(self, db_config: DatabaseConfig):
80
147
  # 从 DatabaseConfig 中提取数据库连接信息
81
148
  self.db_user = db_config.username
@@ -48,7 +48,7 @@ class LangfuseInitializer(metaclass=SingletonMeta):
48
48
 
49
49
  server_name = config_dict.get('Name', '')
50
50
  langfuse_configs = config_dict.get('LangfuseConfig', [])
51
- environment = config_dict.get('Nacos', {}).get('namespaceId', '')
51
+ environment = Config().get_namespace_id('')
52
52
 
53
53
  # 3. 查找匹配的配置项
54
54
  target_config = next(
@@ -134,7 +134,7 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
134
134
  f"TokenUsageMySQLService 加载 service_name: {cls._service_name}")
135
135
 
136
136
  if cls._system_env is None:
137
- env = config.get('Nacos', {}).get('namespaceId', None)
137
+ env = Config().get_namespace_id(None)
138
138
  if env:
139
139
  cls._system_env = env
140
140
  logging.info(
@@ -236,7 +236,7 @@ class KafkaSink:
236
236
  "tenantId": "",
237
237
  "userId": "",
238
238
  "customerId": "",
239
- "env": Config().config.get('Nacos', {}).get('namespaceId', ''),
239
+ "env": Config().get_namespace_id(''),
240
240
  "priReqSource": "",
241
241
  "reqSource": "",
242
242
  "serviceId": self.service_id,
@@ -99,3 +99,55 @@ def error_content(
99
99
  ) -> dict:
100
100
  """构建错误响应内容字典,支持自定义错误码"""
101
101
  return build_response_content(data=data, code=code, message=message)
102
+
103
+
104
+ # =====================================================================
105
+ # HTTP 语义响应(新方式,推荐使用)
106
+ #
107
+ # 与上面 0/1 业务码的区别:code 直接用 HTTP 状态码语义——
108
+ # 成功 code=200 / 失败 code=500,HTTP status 同步对应(200 / 500),
109
+ # success 由 code 是否为 2xx 推导。不再用 0/1 代表成功失败。
110
+ # 旧函数保留兼容,新项目请用 ok() / fail()。
111
+ # =====================================================================
112
+
113
+
114
+ def http_response(
115
+ data: T | Any = None,
116
+ code: int = status.HTTP_200_OK,
117
+ message: str = None,
118
+ ) -> JSONResponse:
119
+ """按 HTTP 状态码语义构建响应,code 即业务码也是 HTTP status。
120
+
121
+ success 自动按 code 是否为 2xx 推导。
122
+ """
123
+ content = BaseResponseModel(
124
+ code=code,
125
+ message=message,
126
+ success=200 <= code < 300,
127
+ data=data,
128
+ )
129
+ if isinstance(content.data, BaseModel):
130
+ body = {
131
+ "code": content.code,
132
+ "message": content.message,
133
+ "success": content.success,
134
+ "data": content.data.model_dump(),
135
+ }
136
+ else:
137
+ body = content.model_dump()
138
+ return JSONResponse(content=body, status_code=code)
139
+
140
+
141
+ def ok(data: T | Any = None, message: str = None) -> JSONResponse:
142
+ """成功响应:code=200, success=true, HTTP 200"""
143
+ return http_response(data=data, code=status.HTTP_200_OK, message=message)
144
+
145
+
146
+ def fail(
147
+ message: str = None,
148
+ data: T | Any = None,
149
+ code: int = status.HTTP_500_INTERNAL_SERVER_ERROR,
150
+ ) -> JSONResponse:
151
+ """失败响应:默认 code=500, success=false, HTTP 500(可用 code 覆盖)"""
152
+ return http_response(data=data, code=code, message=message)
153
+
@@ -94,7 +94,7 @@ async def send_webhook(error_info: dict = None, webhook: str = None):
94
94
  try:
95
95
  config = Config().config
96
96
  service_name = config.get('Name', "未知服务")
97
- env = config.get('Nacos', {}).get('namespaceId', '未知环境')
97
+ env = Config().get_namespace_id('未知环境')
98
98
  webHook = get_webhook()
99
99
  except Exception as e:
100
100
  service_name = "未知服务"
@@ -138,7 +138,7 @@ def run(*args, webhook: str = None, **kwargs):
138
138
  """
139
139
  # 判断环境
140
140
  try:
141
- env = Config().config.get('Nacos', {}).get('namespaceId', 'dev')
141
+ env = Config().get_namespace_id('dev')
142
142
  except:
143
143
  env = 'dev'
144
144
 
@@ -249,7 +249,7 @@ async def send_mq_disconnect_alert(
249
249
  try:
250
250
  config = Config().config
251
251
  service_name = config.get('Name', "未知服务")
252
- env = config.get('Nacos', {}).get('namespaceId', '未知环境')
252
+ env = Config().get_namespace_id('未知环境')
253
253
  except Exception:
254
254
  service_name = "未知服务"
255
255
  env = "未知环境"
@@ -309,7 +309,7 @@ async def send_nacos_disconnect_alert(
309
309
  try:
310
310
  config = Config().config
311
311
  display_service_name = config.get('Name', "未知服务")
312
- env = config.get('Nacos', {}).get('namespaceId', '未知环境')
312
+ env = Config().get_namespace_id('未知环境')
313
313
  except Exception:
314
314
  display_service_name = "未知服务"
315
315
  env = "未知环境"
@@ -198,8 +198,7 @@ class RabbitMQProducerManager(RabbitMQClientManager):
198
198
  )
199
199
 
200
200
  # 构建消息头
201
- namespace_id = Config().config.get('Nacos', {}).get('namespaceId', '')
202
- tenant_id = "T000002" if namespace_id == "prod" or namespace_id == "wsuat1" else "T000003"
201
+ tenant_id = Config().get_tenant_id()
203
202
  mq_header = {
204
203
  "context": SsoUser(
205
204
  tenant_id=tenant_id,
@@ -9,7 +9,7 @@ from sentry_sdk.integrations.fastapi import FastApiIntegration
9
9
  def sy_sentry_init():
10
10
  config = Config().config
11
11
  server_name = config.get('Name', '')
12
- environment = config.get('Nacos', {}).get('namespaceId', '')
12
+ environment = Config().get_namespace_id('')
13
13
  sentry_configs = config.get('SentryConfig', [])
14
14
  target_config = next(
15
15
  (item for item in sentry_configs if item.get('name') == server_name), None)
@@ -0,0 +1,72 @@
1
+ """Config.get_namespace_id / get_tenant_id 统一环境/租户解析 单测。
2
+
3
+ 锁定:
4
+ - namespaceId 原样返回;配置缺失时按调用方传入的 default 返回。
5
+ - 租户映射: prod / wsuat1 → T000002; dev / '' / None / 其它 → T000003。
6
+
7
+ 通过 monkeypatch Config 单例的 config 覆盖分支,无需真实 app.yaml。
8
+ """
9
+ import pytest
10
+
11
+ from sycommon.config.Config import Config
12
+
13
+
14
+ def _set_config(monkeypatch, nacos_block):
15
+ """把 Config 单例替换为只含 Nacos 段的实例。
16
+
17
+ 跳过 __init__(它要求 app.yaml 存在),直接构造实例并注入单例缓存,
18
+ 使 Config() 返回该实例。
19
+
20
+ Args:
21
+ nacos_block: Nacos 段内容。传 None 表示整个 Nacos key 都没有,
22
+ 传 {} 表示有 key 但无 namespaceId,
23
+ 传 {"namespaceId": "prod"} 表示正常配置。
24
+ """
25
+ cfg = {} if nacos_block is None else {"Nacos": nacos_block}
26
+ inst = Config.__new__(Config)
27
+ inst.config = cfg
28
+ monkeypatch.setitem(Config._instances, Config, inst)
29
+
30
+
31
+ @pytest.mark.parametrize(
32
+ "nacos_block, expected",
33
+ [
34
+ ({"namespaceId": "prod"}, "prod"),
35
+ ({"namespaceId": "wsuat1"}, "wsuat1"),
36
+ ({"namespaceId": "dev"}, "dev"),
37
+ ({}, ""),
38
+ (None, ""),
39
+ ],
40
+ )
41
+ def test_get_namespace_id_returns_configured_value(monkeypatch, nacos_block, expected):
42
+ _set_config(monkeypatch, nacos_block)
43
+ assert Config().get_namespace_id() == expected
44
+
45
+
46
+ @pytest.mark.parametrize("default", ["未知环境", "dev", None])
47
+ def test_get_namespace_id_returns_default_when_missing(monkeypatch, default):
48
+ # Nacos 段缺失 —— 命中 default
49
+ _set_config(monkeypatch, None)
50
+ assert Config().get_namespace_id(default) == default
51
+ # Nacos 段在但无 namespaceId —— 同样命中 default
52
+ _set_config(monkeypatch, {})
53
+ assert Config().get_namespace_id(default) == default
54
+
55
+
56
+ @pytest.mark.parametrize("ns", ["prod", "wsuat1"])
57
+ def test_get_tenant_id_prod_namespaces(monkeypatch, ns):
58
+ _set_config(monkeypatch, {"namespaceId": ns})
59
+ assert Config().get_tenant_id() == Config._DEFAULT_TENANT_PROD == "T000002"
60
+
61
+
62
+ @pytest.mark.parametrize("nacos_block", [{"namespaceId": "dev"}, {}, None])
63
+ def test_get_tenant_id_other_namespaces(monkeypatch, nacos_block):
64
+ _set_config(monkeypatch, nacos_block)
65
+ assert Config().get_tenant_id() == Config._DEFAULT_TENANT_OTHER == "T000003"
66
+
67
+
68
+ def test_tenant_mapping_constants_locked():
69
+ """锁定映射表,防止后续误改租户 ID 或判定环境集合。"""
70
+ assert Config._PROD_NAMESPACES == ("prod", "wsuat1")
71
+ assert Config._DEFAULT_TENANT_PROD == "T000002"
72
+ assert Config._DEFAULT_TENANT_OTHER == "T000003"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.8a0
3
+ Version: 0.2.8a1
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -136,8 +136,8 @@ sycommon/services.py,sha256=iC73h5d5bpNqW2qjRUGODZFhUhcwvKVnKUFLKJlBSq0,25322
136
136
  sycommon/agent/__init__.py,sha256=mxceAeUifQ-DKvWp7ZEJIFlmOCb5wpYHPGQw3rwEN8I,4378
137
137
  sycommon/agent/agent_manager.py,sha256=UhhaekEumT7g4v_Z1UB4jTp13X0n8M8erYaQdkGGWkA,13620
138
138
  sycommon/agent/chat_events.py,sha256=t7qWa6OrIWLfqtd1AnqaVP67QWkn1JxpCBTZYQpoLuM,14226
139
- sycommon/agent/deep_agent.py,sha256=Igw9etibbwE8JfvPU5LYd2fxlVfSqVGAj1obORCoXhI,77264
140
- sycommon/agent/multi_agent_team.py,sha256=P8vNPegpvzVtoEnQZyjpFbt2d1emqt0EQqIq8zlo8LA,33619
139
+ sycommon/agent/deep_agent.py,sha256=_pWnZ_qUsQrE3GE6Ck1eYW3WmPtHSxZ39hzdOf6Y7FA,77759
140
+ sycommon/agent/multi_agent_team.py,sha256=imovcL-yqfXnT6jsTcaDZJbUHW8vUWyKAGELV47SntM,33803
141
141
  sycommon/agent/reasoning_content_patch.py,sha256=HZ_If0qVDHlz_b1mDT3tuTqbExhzRVhP4VgsoDt0JLc,3945
142
142
  sycommon/agent/resume_compactor.py,sha256=5ZdjKWSAH8P3eyVlmu_MBos4o-2aYDY5I46kf_01eHc,9049
143
143
  sycommon/agent/summarization_utils.py,sha256=Yiu_zYn6cXUOq2NOsUh7tljqvmcOw6WAUKREONP2pLM,18923
@@ -171,7 +171,7 @@ sycommon/auth/oa_crypto.py,sha256=xpY1R1Bj3KLENXB0TuThB6Eku1E9PYjcoSpOdgDmCgc,18
171
171
  sycommon/auth/oa_service.py,sha256=kLepV9zgqpZoaB73DRPpMA5tJJQjoaDtQPdzBcGXeak,6235
172
172
  sycommon/auth/wecom_ldap_service.py,sha256=7yw4gvoaaT2G1SuL_jC6kTORgMuT4GVYbusFbPuOb6g,43173
173
173
  sycommon/config/ACPAgentConfig.py,sha256=paAa0c0gy1RnDDum-Q6m7QLTe7lhz2dsFt4r72QtmWE,3202
174
- sycommon/config/Config.py,sha256=zbddvm2_y_l3k2zMeAZ0BTEST6Or_kUD7y21RnEy2Kc,7929
174
+ sycommon/config/Config.py,sha256=m58rd3WIyb7fCexR7pelFUcmnnoL0wa9DVwIXtAbf_c,9068
175
175
  sycommon/config/DatabaseConfig.py,sha256=ILiUuYT9_xJZE2W-RYuC3JCt_YLKc1sbH13-MHIOPhg,804
176
176
  sycommon/config/ElasticsearchConfig.py,sha256=fO9ZPMgJxSg1-UyDJ90wO6UvYy-jscwPJsSkXgx9qTU,2308
177
177
  sycommon/config/EmbeddingConfig.py,sha256=gPKwiDYbeu1GpdIZXMmgqM7JqBIzCXi0yYuGRLZooMI,362
@@ -186,8 +186,8 @@ sycommon/config/SkillApiWhitelistConfig.py,sha256=-fimbAofnYSZRhksmvg_rrZOJPc76w
186
186
  sycommon/config/XxlJobConfig.py,sha256=VSG6dn9ysfUVunOs7PqugyZUGJWmX_cEePz2ZCfqHtU,392
187
187
  sycommon/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
188
188
  sycommon/config/config_change_notifier.py,sha256=7qQe7vdBS1zohEfVq0d5IaDqYsLdgd2ckF4mKKgh0gE,6848
189
- sycommon/database/async_base_db_service.py,sha256=yo3hhz4UlKtYBY3DoyJjqXfDJPNfhNdGivvzPkW2S_I,4469
190
- sycommon/database/async_database_service.py,sha256=fL3_W-cBwyg0-TlwKEMqcX5dHWnriSSticztWLHC1zw,6587
189
+ sycommon/database/async_base_db_service.py,sha256=8mPZ2yI1hIYvuHYxcpeU0gus_1WGjrpRqv2bm9TIBCk,6186
190
+ sycommon/database/async_database_service.py,sha256=fLc0dyLCA51kbjFH4dE4oab44fLnn8Ec26EH7TsY-So,9888
191
191
  sycommon/database/base_db_service.py,sha256=urdAibaCUt3hxwwKzEMBceWkIwsw3ja_WDJ1QKc8nLU,4116
192
192
  sycommon/database/database_service.py,sha256=svkg6MBxcar6nWSepBv40mSfpZi5tVecloTDu1O1Tns,6099
193
193
  sycommon/database/elasticsearch_service.py,sha256=BJnBcoeSDtK1a1IcCyLbzQaOMb5hCfIh0wgBRQ9XmpU,4257
@@ -212,16 +212,16 @@ sycommon/llm/llm_with_token_tracking.py,sha256=VJYHQSSuqvAbJ7qFs7Zml_thFDZoqL2rX
212
212
  sycommon/llm/native_with_fallback_runnable.py,sha256=8g2q8cRqJv2miY7GrZz_f-zNmHxdb_5WSujD3frqObQ,8035
213
213
  sycommon/llm/output_fixing_runnable.py,sha256=wGHBKRpUjDz4ONJulTvG5UzJxZr1iK3ULkosaKqjF0w,8113
214
214
  sycommon/llm/struct_token.py,sha256=WYNa7o2cC_3X6dJw3ZN1Zgn83HmhdD8GCUlSiEV7qus,22353
215
- sycommon/llm/sy_langfuse.py,sha256=UOQhZ5P3tBgPyrCkxqEFiCMnIm-B3UOeC2djfGUbxWI,6117
215
+ sycommon/llm/sy_langfuse.py,sha256=Y0XzFOeoJDOCft9fPqqCVaXugM39SImdXga937nVhmo,6095
216
216
  sycommon/llm/token_usage_es_service.py,sha256=-RBCwZ8jS436n8DBUBm3Lf5O6TsQMaQgIj5oYBLO4Ss,19909
217
- sycommon/llm/token_usage_mysql_service.py,sha256=1WhdpZ8M4v4UNP5W0ui3A-Cx5yfZiX73l77FTJvL_d8,32644
217
+ sycommon/llm/token_usage_mysql_service.py,sha256=PfGaQQRY7bl7UZbxnMOjtx4sN-8V2e9kN2PU0H8GIao,32627
218
218
  sycommon/llm/token_utils.py,sha256=C_9_rhsOx8AhY16p00R9aMYnGEhtbR_2acXm1u5ZeTE,6647
219
219
  sycommon/llm/usage_token.py,sha256=uFskOhl6IMeJtysJ6tMAmBb2taVLm5lRWjqcrbnkx54,8736
220
220
  sycommon/llm/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
221
221
  sycommon/llm/tiktoken_cache/fb374d419588a4632f3f557e76b4b70aebbca790,sha256=RGqVOMtsNI41FhINfAiwn1fDZJXirP_-WaW_iwz7Gi0,3613922
222
222
  sycommon/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
223
223
  sycommon/logging/async_sql_logger.py,sha256=TIUAqK8_F9H7Graw00hNjEI3DJ1ZRo5fRUcnqaMZlbU,2187
224
- sycommon/logging/kafka_log.py,sha256=taQE4U1F9rOiOq6SAHzUJyZS10lFX_QQY4ekfASBwQk,31050
224
+ sycommon/logging/kafka_log.py,sha256=M-VxIilYw_89gtIfn3xlavQT6kwnlEsBGxK8ylS_UXw,31024
225
225
  sycommon/logging/log_masker.py,sha256=GYf4NA1YXF070r0MUkj2oh4bygaZo0hEnDih734a-eE,15923
226
226
  sycommon/logging/logger_levels.py,sha256=xJBzPV2H7GpEQggOIjyckEsA4yf6s4NpI9ckgO3xWKA,1154
227
227
  sycommon/logging/logger_wrapper.py,sha256=TtzmGw_K5NjBGBq9Gjqtnh834izAv8TQwsOeL2fIbAI,481
@@ -243,7 +243,7 @@ sycommon/middleware/token_tracking.py,sha256=rEbgV1bgWMdzAERx4aq5XAvOIT6jTY_tK1P
243
243
  sycommon/middleware/tool_result_truncation.py,sha256=dTh2VHyNVGvl7CyjsZ4SqAxaabR5hnL-oB0bf2-S9Pk,12534
244
244
  sycommon/middleware/traceid.py,sha256=U124PDNS4xhPVXcfpfTJn7dTEMsE0V4UT5umrxmjD4Y,16891
245
245
  sycommon/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
246
- sycommon/models/base_http.py,sha256=EICAAibx3xhjBsLqm35Mi3DCqxp0FME4rD_3iQVjT_E,3051
246
+ sycommon/models/base_http.py,sha256=efxtsxmQmInlsRVqQU9P17MSVXx3TsNmgimMlz0hNV0,4788
247
247
  sycommon/models/log.py,sha256=rZpj6VkDRxK3B6H7XSeWdYZshU8F0Sks8bq1p6pPlDw,500
248
248
  sycommon/models/mcp_server_config.py,sha256=cm_oDEDXVJR4nCKgWPli5zFI0DUy6xtvRs-___EWAW8,1768
249
249
  sycommon/models/mqlistener_config.py,sha256=ZFGg_vc9HcIV22L5Jgrynkjj5B6Ci8ETQNUbNUiXrVw,1830
@@ -255,7 +255,7 @@ sycommon/models/token_usage.py,sha256=w3Aash7jUVSUIgKxBUzI6SqJ9VV5Pk7rLBbMUlLv6n
255
255
  sycommon/models/token_usage_mysql.py,sha256=eWnCvI2FDwyTA_fr4wWQtAmUnFJgLwrt0WXMXHLAESQ,2633
256
256
  sycommon/models/xxljob_handler_config.py,sha256=XiSv9AlJ8UsLRai4hvTMeiSZ6VsO5Ijx8HcDwLxJL64,376
257
257
  sycommon/notice/__init__.py,sha256=cVYbKlLLMFHckyDpx21CTZCMTufqZk_uHuEIxUNxwx4,104
258
- sycommon/notice/uvicorn_monitor.py,sha256=O4R25z032dAlvlbU0WLo0LJqoL1Z52fMFgFdb1e3Cls,11833
258
+ sycommon/notice/uvicorn_monitor.py,sha256=_SlLB91N7g-Jt_xP_AX1IWFgA7_1lCqsQFwm_Y6QjFk,11756
259
259
  sycommon/notice/wecom_message.py,sha256=_oA6hg0Ask7NkFbvUpL78WOmyUb6uW7KcY71GhNKiJY,12363
260
260
  sycommon/rabbitmq/process_pool_consumer.py,sha256=EXdNypgathjTfPqkccoAW0HiVB9HuZLGhPbWUcYh0_Q,29750
261
261
  sycommon/rabbitmq/rabbitmq_client.py,sha256=TmIOtfb_Wx-swX2s93GgAjR7mzs9KeXy5kA_8UgE1vI,28168
@@ -265,9 +265,9 @@ sycommon/rabbitmq/rabbitmq_service_client_manager.py,sha256=UZgpD6n0mdo5u3gaGW-j
265
265
  sycommon/rabbitmq/rabbitmq_service_connection_monitor.py,sha256=jPr9OWAq_nRGWAt7-q3qdOdhtT3EC6MShhNu9fiEpqk,8701
266
266
  sycommon/rabbitmq/rabbitmq_service_consumer_manager.py,sha256=GeiS1GEtny5MxopIdKOCfeRB1AzkHZKPXKq9_jwg1KI,10274
267
267
  sycommon/rabbitmq/rabbitmq_service_core.py,sha256=WeYtMAxGXpCJoVIVMRNrWSBTMnTDQCLVXiOn03iGAjg,7488
268
- sycommon/rabbitmq/rabbitmq_service_producer_manager.py,sha256=bqp3XiJli9bmWWAw419yAZKbJLs_xivK2EaLc1J4TsA,10032
268
+ sycommon/rabbitmq/rabbitmq_service_producer_manager.py,sha256=cersEoHvl4__YqORYnqTckRl2ZlKV1j_zGamPKqsp-A,9895
269
269
  sycommon/sentry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
270
- sycommon/sentry/sy_sentry.py,sha256=e5Fbt9Gi2gIb048z0nuKbuhp3uCAEqYH2xXbF6qrZq4,1471
270
+ sycommon/sentry/sy_sentry.py,sha256=Os-BISmJvjaSarHxqWH_8u7KBk-k2XMVZfoj5UUzqso,1454
271
271
  sycommon/sse/__init__.py,sha256=u-F-bzxb6g57JhkUwWxrnQYNSmZS30BYA0Tkrq2EKVg,194
272
272
  sycommon/sse/event.py,sha256=k_rBJy23R7crtzQeetT0Q73D8o5-5p-eESGSs_BPOj0,2797
273
273
  sycommon/sse/sse.py,sha256=OQ3ElV8WCi-AD3-e0nbiUF28Syf6GRpGztneWTn77EM,10356
@@ -294,6 +294,7 @@ sycommon/tests/test_deep_agent.py,sha256=34gP2KL8SSmtqqhaA9OV97OAl_I0T4TfEutZrR_
294
294
  sycommon/tests/test_dynamic_max_tokens.py,sha256=mk2QXZIxpyClEBvS9g88NXr9xmXoNdVrIjFAsN5t6CQ,17934
295
295
  sycommon/tests/test_email.py,sha256=-oPtYVGQzJ3Cv-op3ZNRMeyYOF-UNidGJC35CHRgjGQ,5442
296
296
  sycommon/tests/test_encoding_cache.py,sha256=EqeN2x9RuyS5Xg8DhAfnzI-ezzaIaitSCZtXnfvALgA,4794
297
+ sycommon/tests/test_env_tenant_helper.py,sha256=WCaajpVmCPPpV_Y2rvD5bTVG_ZDiEHnb4G8gGGSe0JA,2694
297
298
  sycommon/tests/test_exp_concurrent_migrate.py,sha256=2FY60udoClkrZPyDjTp-26KvMUbiKiWwT2502tb3YjE,5678
298
299
  sycommon/tests/test_exp_delete_queue_with_consumers.py,sha256=L9RWLOzUtTEEpzad-VGkOkPCbCVmR3Znl6WCezxfSMY,5695
299
300
  sycommon/tests/test_fault_injection_real.py,sha256=vqTmQXeay5VXq2DqXUKs1KdN-EbpWj9zoxhgpeCEHms,18986
@@ -326,8 +327,8 @@ sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
326
327
  sycommon/tools/user_id.py,sha256=o-zsubGSR5rSSZf8Pk8B7AhE21ahDTTIW5JZmJO6Flw,4761
327
328
  sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
328
329
  sycommon/xxljob/xxljob_service.py,sha256=1yifwIBNGsCIxLnQjHKiBlbsigc_zvPH-dMTZcNxe-Q,7649
329
- sycommon_python_lib-0.2.8a0.dist-info/METADATA,sha256=1M3hc17dUZxai8jTTPmuAAWUFgUGg4ULeYkuC7z72fk,7996
330
- sycommon_python_lib-0.2.8a0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
331
- sycommon_python_lib-0.2.8a0.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
332
- sycommon_python_lib-0.2.8a0.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
333
- sycommon_python_lib-0.2.8a0.dist-info/RECORD,,
330
+ sycommon_python_lib-0.2.8a1.dist-info/METADATA,sha256=lhsnYQpDEPhhOBsWNk16ltiUVesU8sIrj1n2SvgSMWw,7996
331
+ sycommon_python_lib-0.2.8a1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
332
+ sycommon_python_lib-0.2.8a1.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
333
+ sycommon_python_lib-0.2.8a1.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
334
+ sycommon_python_lib-0.2.8a1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (83.0.0)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5