sycommon-python-lib 0.2.7a11__py3-none-any.whl → 0.2.7a12__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.
@@ -1346,46 +1346,67 @@ async def _sync_skills_to_sandbox(
1346
1346
  版本比较只看沙箱 sandbox_dest/{name}/SKILL.md,无需 MinIO 锚点:
1347
1347
  - 沙箱有该技能且 version 一致 → 跳过
1348
1348
  - 沙箱没有 / 版本不一致 → 从本地 skills/ 全量上传到沙箱
1349
+
1350
+ 性能: 比对与上传均绕过 backend 的懒同步(_skip_lazy_sync=True),
1351
+ 避免「比对 read SKILL.md 前先整目录上传」「删除前先上传再删」的往返浪费。
1352
+ 多技能的 SKILL.md 比对用 gather 并发; batch_upload 服务端无条件覆盖,
1353
+ 无需先删(删除仅在版本不一致时做,用 adelete 走 /delete 而非 /execute)。
1349
1354
  """
1350
1355
  if not os.path.isdir(skills_dir):
1351
1356
  return
1352
1357
 
1353
1358
  try:
1354
- # 获取沙箱 sandbox_dest 下已有的目录列表
1355
- existing_dirs = set()
1356
- tree_result = await backend.atree(sandbox_dest, max_depth=1)
1357
- if tree_result and tree_result.tree and tree_result.tree.children:
1358
- for child in tree_result.tree.children:
1359
- if child.is_dir:
1360
- existing_dirs.add(child.name)
1361
-
1362
- skill_count = 0
1363
- skip_count = 0
1359
+ # 收集本地技能名 + 本地版本(纯本地读,快)
1360
+ local_skills = [] # [(skill_name, local_version, src_path)]
1364
1361
  for skill_name in sorted(os.listdir(skills_dir)):
1365
1362
  src = os.path.join(skills_dir, skill_name)
1366
1363
  if not os.path.isdir(src):
1367
1364
  continue
1368
-
1369
- # 读取本地版本
1370
- local_skill_md = os.path.join(src, "SKILL.md")
1371
1365
  local_version = ""
1366
+ local_skill_md = os.path.join(src, "SKILL.md")
1372
1367
  if os.path.isfile(local_skill_md):
1373
1368
  try:
1374
1369
  with open(local_skill_md, "r", encoding="utf-8") as f:
1375
1370
  local_version = _parse_skill_version(f.read())
1376
1371
  except Exception:
1377
1372
  pass
1373
+ local_skills.append((skill_name, local_version, src))
1378
1374
 
1379
- # 比较版本:只从沙箱 sandbox_dest/{name}/SKILL.md 读取
1380
- remote_version = ""
1381
- if skill_name in existing_dirs:
1382
- try:
1383
- remote_content = await backend.aread(
1384
- f"{sandbox_dest}/{skill_name}/SKILL.md")
1385
- if remote_content:
1386
- remote_version = _parse_skill_version(remote_content)
1387
- except Exception:
1388
- pass
1375
+ # 获取沙箱 sandbox_dest 下已有的目录列表(一次 atree)
1376
+ existing_dirs = set()
1377
+ tree_result = await backend.atree(sandbox_dest, max_depth=1)
1378
+ if tree_result and tree_result.tree and tree_result.tree.children:
1379
+ for child in tree_result.tree.children:
1380
+ if child.is_dir:
1381
+ existing_dirs.add(child.name)
1382
+
1383
+ # 并发读取所有已存在技能的沙箱 SKILL.md 比对版本(跳过懒同步)
1384
+ async def _read_remote_version(skill_name: str) -> str:
1385
+ if skill_name not in existing_dirs:
1386
+ return ""
1387
+ try:
1388
+ remote_content = await backend.aread(
1389
+ f"{sandbox_dest}/{skill_name}/SKILL.md",
1390
+ _skip_lazy_sync=True)
1391
+ if remote_content and remote_content.file_data:
1392
+ # FileData 是 TypedDict(dict), 用键访问而非属性
1393
+ return _parse_skill_version(
1394
+ remote_content.file_data.get("content", "") or "")
1395
+ except Exception:
1396
+ pass
1397
+ return ""
1398
+
1399
+ remote_versions = await asyncio.gather(
1400
+ *[_read_remote_version(name) for name, _, _ in local_skills]
1401
+ )
1402
+ remote_version_map = {
1403
+ name: rv for (name, _, _), rv in zip(local_skills, remote_versions)
1404
+ }
1405
+
1406
+ skill_count = 0
1407
+ skip_count = 0
1408
+ for skill_name, local_version, src in local_skills:
1409
+ remote_version = remote_version_map.get(skill_name, "")
1389
1410
 
1390
1411
  if remote_version and remote_version == local_version:
1391
1412
  SYLogger.debug(
@@ -1394,10 +1415,13 @@ async def _sync_skills_to_sandbox(
1394
1415
  skip_count += 1
1395
1416
  continue
1396
1417
 
1397
- # 版本不一致或无版本,删除旧目录后全量覆盖
1418
+ # 版本不一致或无版本,删除旧目录(走 /delete,跳过懒同步,毫秒级)
1398
1419
  if skill_name in existing_dirs:
1399
1420
  try:
1400
- await backend.aexecute(f"rm -rf {sandbox_dest}/{skill_name}")
1421
+ await backend.adelete(
1422
+ f"{sandbox_dest}/{skill_name}",
1423
+ recursive=True,
1424
+ _skip_lazy_sync=True)
1401
1425
  SYLogger.info(
1402
1426
  f"[DeepAgent] 技能版本不一致,覆盖: {skill_name} "
1403
1427
  f"({remote_version or '无版本'} → {local_version})")
@@ -1405,7 +1429,7 @@ async def _sync_skills_to_sandbox(
1405
1429
  SYLogger.warning(
1406
1430
  f"[DeepAgent] 删除旧版技能失败: {skill_name}, {e}")
1407
1431
 
1408
- # 全量上传整个技能目录到沙箱
1432
+ # 全量上传整个技能目录到沙箱(batch_upload 服务端无条件覆盖)
1409
1433
  await backend.async_sync_dirs([(src, f"{sandbox_dest}/{skill_name}")])
1410
1434
  backend._synced_skills.add(skill_name)
1411
1435
  skill_count += 1
@@ -1417,7 +1441,8 @@ async def _sync_skills_to_sandbox(
1417
1441
  # sandbox_dest 是虚拟绝对路径(/skills/system),沙箱 shell 不重映射裸绝对路径,
1418
1442
  # 须拼 $_SANDBOX_WORKSPACE 指向真实 workspace 才能命中。否则 chmod 静默失败。
1419
1443
  await backend.aexecute(
1420
- f'chmod -R 555 "$_SANDBOX_WORKSPACE{sandbox_dest}"')
1444
+ f'chmod -R 555 "$_SANDBOX_WORKSPACE{sandbox_dest}"',
1445
+ _skip_lazy_sync=True)
1421
1446
  SYLogger.info(f"[DeepAgent] 已设只读: chmod -R 555 {sandbox_dest}")
1422
1447
  except Exception as e:
1423
1448
  SYLogger.warning(f"[DeepAgent] chmod 只读失败(忽略): {e}")
@@ -333,11 +333,17 @@ class FileOperationsMixin:
333
333
  limit: int = 2000,
334
334
  *,
335
335
  timeout: int = None,
336
+ _skip_lazy_sync: bool = False,
336
337
  ) -> ReadResult:
337
- """异步读取文件内容"""
338
+ """异步读取文件内容
339
+
340
+ _skip_lazy_sync=True 时跳过懒同步(供内部技能版本比对用,
341
+ 避免 read SKILL.md 触发整目录上传)。
342
+ """
338
343
  try:
339
344
  # 按需懒同步:检测是否读取 skill 资源
340
- await self._lazy_sync_skill_from_path(file_path)
345
+ if not _skip_lazy_sync:
346
+ await self._lazy_sync_skill_from_path(file_path)
341
347
 
342
348
  await self._ensure_synced_async()
343
349
  SYLogger.info(
@@ -738,10 +744,16 @@ class FileOperationsMixin:
738
744
  recursive: bool = False,
739
745
  *,
740
746
  timeout: int = None,
747
+ _skip_lazy_sync: bool = False,
741
748
  ) -> DeleteResult:
742
- """异步删除文件或文件夹"""
749
+ """异步删除文件或文件夹
750
+
751
+ _skip_lazy_sync=True 时跳过懒同步(供内部技能覆盖删除用,
752
+ 避免「删之前先把要删的技能上传一遍」的浪费)。
753
+ """
743
754
  try:
744
- await self._lazy_sync_skill_from_path(path)
755
+ if not _skip_lazy_sync:
756
+ await self._lazy_sync_skill_from_path(path)
745
757
  await self._ensure_synced_async()
746
758
  SYLogger.info(f"[Sandbox] 异步删除: {path} (recursive={recursive})")
747
759
  result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/delete", {
@@ -813,17 +813,21 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
813
813
  truncated=False
814
814
  )
815
815
 
816
- async def aexecute(self, command: str, *, timeout: int = None) -> ExecuteResponse:
816
+ async def aexecute(self, command: str, *, timeout: int = None, _skip_lazy_sync: bool = False) -> ExecuteResponse:
817
817
  """异步执行 shell 命令(服务端负责路径隔离和目录初始化)
818
818
 
819
819
  如果命令引用了 /skills/<name>/ 下的资源,
820
820
  会自动按需同步该 skill 的整个目录到沙箱。
821
+
822
+ _skip_lazy_sync=True 时跳过懒同步(仅供内部技能同步/删除使用,
823
+ 避免删前先传、比对前先传的往返浪费)。
821
824
  """
822
825
  command = self._inject_env(command)
823
826
  SYLogger.info(f"[Sandbox] aexecute 开始: command={command}")
824
827
 
825
828
  # 按需懒同步:检测命令中是否引用了 skill 资源
826
- await self._lazy_sync_skill_from_path(command)
829
+ if not _skip_lazy_sync:
830
+ await self._lazy_sync_skill_from_path(command)
827
831
 
828
832
  await self._ensure_synced_async()
829
833
  SYLogger.info(f"[Sandbox] _ensure_synced_async 完成,开始执行命令: {command}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.7a11
3
+ Version: 0.2.7a12
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -11,14 +11,14 @@ Requires-Dist: anyio==4.14.1
11
11
  Requires-Dist: decorator==5.3.1
12
12
  Requires-Dist: deepagents==0.6.12
13
13
  Requires-Dist: elasticsearch==9.4.1
14
- Requires-Dist: fastapi==0.138.1
14
+ Requires-Dist: fastapi==0.139.0
15
15
  Requires-Dist: jinja2==3.1.6
16
- Requires-Dist: kafka-python==3.0.6
16
+ Requires-Dist: kafka-python==3.0.7
17
17
  Requires-Dist: langchain==1.3.11
18
18
  Requires-Dist: langchain-core==1.4.8
19
19
  Requires-Dist: langchain-openai==1.3.3
20
- Requires-Dist: langfuse==4.12.0
21
- Requires-Dist: langgraph==1.2.6
20
+ Requires-Dist: langfuse==4.13.0
21
+ Requires-Dist: langgraph==1.2.7
22
22
  Requires-Dist: langgraph-checkpoint-postgres==3.1.0
23
23
  Requires-Dist: langgraph-checkpoint-redis==0.5.0
24
24
  Requires-Dist: ldap3==2.9.1
@@ -32,7 +32,7 @@ Requires-Dist: python-dotenv==1.2.2
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.63.0
35
+ Requires-Dist: sentry-sdk[fastapi]==2.64.0
36
36
  Requires-Dist: sqlalchemy[asyncio]==2.0.51
37
37
  Requires-Dist: starlette[full]==1.3.1
38
38
  Requires-Dist: tiktoken==0.13.0
@@ -136,7 +136,7 @@ sycommon/services.py,sha256=CwC_gESafo05Qn41KLQgtulx6PfX7s2FkxlBI-hXve4,24861
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=F3kn4dsu4aWOGtAGmsbxUWsrzUkkT6Uv7-LLrs1Ve5M,70420
139
+ sycommon/agent/deep_agent.py,sha256=MtMTTqRTUQ_HPpniGfDkL0t781b_ATxV-btLG5iwSt4,71834
140
140
  sycommon/agent/multi_agent_team.py,sha256=IbjwhUMDsW4HMyWr53leWBLeaALO-8iNdxcKL6irfz0,28672
141
141
  sycommon/agent/summarization_utils.py,sha256=bqZRFqSekNGlv2mYQPaT_s2QNA5VDTKddi2Va_SkHWc,18376
142
142
  sycommon/agent/acp/__init__.py,sha256=vQGMQYAA5-ZaejYmDZ4r4Fklqs17qH3cDXQ5pZlwj-0,1844
@@ -153,8 +153,8 @@ sycommon/agent/middleware/skill_wl_check.py,sha256=rCJ9F6aWPh8tVQBIPmcq2lKDsfwiJ
153
153
  sycommon/agent/middleware/skill_write_guard.py,sha256=N7EsHMc87eo5QFLjTxbIcn0kurM1LD3vlfeHSR3jVg4,2557
154
154
  sycommon/agent/sandbox/__init__.py,sha256=jR7LlkD4J4Y6QYyRXQClkwmqDBCCPmycV_hQV9p9YHw,4621
155
155
  sycommon/agent/sandbox/consistent_hash.py,sha256=8Jgk-W4NAD2-u5_vKRVlPmql_e0Vy4aNORiXOuzvIrs,7277
156
- sycommon/agent/sandbox/file_ops.py,sha256=7A-T5dV2o5OzPT2kaNrlOTAqmyQ8HGiHV7ifqqMivZA,32988
157
- sycommon/agent/sandbox/http_sandbox_backend.py,sha256=59FvNhU9WdevXx_X14uiN_REWnEtQk01OXT0UQlgoTk,68303
156
+ sycommon/agent/sandbox/file_ops.py,sha256=knjRqtj_0lGALNXxocg4cCClNuEgd0dtCBtCJsfjbik,33469
157
+ sycommon/agent/sandbox/http_sandbox_backend.py,sha256=ZEAyxEeLR9aJg3REmoeupdsYb5RLarbt7NYoZqPIXxQ,68528
158
158
  sycommon/agent/sandbox/minio_sync.py,sha256=HRtw3SwOlgy0G-AsXhWr_1Ob7s-n4Z-9bjpFRfKecTA,23438
159
159
  sycommon/agent/sandbox/sandbox_pool.py,sha256=eMn8sLakCWf90l6ni2-333QM8oBdX1CflV-WzneFp_k,9133
160
160
  sycommon/agent/sandbox/sandbox_recovery.py,sha256=X-eDODx1tmGMh_iTngV6e1ppfDBHpTdkPreJusN5MHY,7358
@@ -306,8 +306,8 @@ sycommon/tools/syemail.py,sha256=BDFhgf7WDOQeTcjxJEQdu0dQhnHFPO_p3eI0-Ni3LhQ,561
306
306
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
307
307
  sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
308
308
  sycommon/xxljob/xxljob_service.py,sha256=1yifwIBNGsCIxLnQjHKiBlbsigc_zvPH-dMTZcNxe-Q,7649
309
- sycommon_python_lib-0.2.7a11.dist-info/METADATA,sha256=uNouGqbjoHhu62nFa67_11WrNGxBCdNgppLjQduJA1s,7962
310
- sycommon_python_lib-0.2.7a11.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
311
- sycommon_python_lib-0.2.7a11.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
312
- sycommon_python_lib-0.2.7a11.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
313
- sycommon_python_lib-0.2.7a11.dist-info/RECORD,,
309
+ sycommon_python_lib-0.2.7a12.dist-info/METADATA,sha256=emXawvSwauqGcr4C8LfZaoj4SvUBY5PrpJ-iS6a03dc,7962
310
+ sycommon_python_lib-0.2.7a12.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
311
+ sycommon_python_lib-0.2.7a12.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
312
+ sycommon_python_lib-0.2.7a12.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
313
+ sycommon_python_lib-0.2.7a12.dist-info/RECORD,,