sycommon-python-lib 0.2.2a9__py3-none-any.whl → 0.2.2a11__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.
nexus/__init__.py CHANGED
@@ -1,83 +1,21 @@
1
- import aiohttp
2
- import asyncio
3
- import random
4
- import argparse
1
+ import subprocess
5
2
  import sys
6
3
  import os
7
4
  import glob
5
+ import argparse
8
6
 
9
7
 
10
8
  NEXUS_URL = "http://192.168.2.174:8081"
11
- USERNAME = "T3N1bGNvZGUueGlhbw=="
12
- PASSWORD = "T3N1bGNvZGUueGlhb0AxMjM="
9
+ USERNAME = "Osulcode.xiao"
10
+ PASSWORD = "Osulcode.xiao@123"
13
11
  REPOSITORY = "shengye"
14
12
 
15
13
 
16
- async def upload(file_path: str, nexus_url: str = NEXUS_URL,
17
- username: str = USERNAME, password: str = PASSWORD,
18
- repository: str = REPOSITORY) -> bool:
19
- if not os.path.exists(file_path):
20
- print(f"File not found: {file_path}")
21
- return False
22
-
23
- jar = aiohttp.CookieJar(unsafe=True)
24
- async with aiohttp.ClientSession(cookie_jar=jar) as session:
25
- async with session.get(f"{nexus_url}/") as resp:
26
- resp.raise_for_status()
27
-
28
- async with session.post(
29
- f"{nexus_url}/service/rapture/session",
30
- data={"username": username, "password": password},
31
- ) as resp:
32
- resp.raise_for_status()
33
-
34
- csrf_token = str(random.random())
35
- session.cookie_jar.update_cookies(
36
- {"NX-ANTI-CSRF-TOKEN": csrf_token},
37
- response_url=aiohttp.client.URL(nexus_url),
38
- )
39
-
40
- filename = os.path.basename(file_path)
41
- data = aiohttp.FormData()
42
- data.add_field("NX-ANTI-CSRF-TOKEN", csrf_token)
43
- data.add_field(
44
- "asset0", open(file_path, "rb"),
45
- filename=filename, content_type="application/octet-stream",
46
- )
47
-
48
- async with session.post(
49
- f"{nexus_url}/service/rest/internal/ui/upload/{repository}",
50
- data=data,
51
- ) as resp:
52
- status = resp.status
53
- text = await resp.text()
54
-
55
- if status in (200, 204):
56
- print(f"Upload successful: {filename} -> {repository}")
57
- return True
58
- else:
59
- print(f"Upload failed ({status}): {text}")
60
- return False
61
-
62
-
63
14
  def main():
64
- parser = argparse.ArgumentParser(
65
- prog="nexus-upload",
66
- description="上传 whl 包到 Nexus 仓库",
67
- )
68
- parser.add_argument(
69
- "file", nargs="?", default=None,
70
- help="whl 文件路径 (默认: dist/*.whl 中最新的)",
71
- )
72
- parser.add_argument(
73
- "--url", default=NEXUS_URL,
74
- help=f"Nexus 地址 (默认: {NEXUS_URL})",
75
- )
76
- parser.add_argument(
77
- "-r", "--repository", default=REPOSITORY,
78
- help=f"仓库名称 (默认: {REPOSITORY})",
79
- )
80
-
15
+ parser = argparse.ArgumentParser(prog="sypublish", description="上传 whl 到 Nexus")
16
+ parser.add_argument("file", nargs="?", help="whl 文件路径 (默认: dist/*.whl 中最新的)")
17
+ parser.add_argument("--url", default=NEXUS_URL, help=f"Nexus 地址 (默认: {NEXUS_URL})")
18
+ parser.add_argument("-r", "--repository", default=REPOSITORY, help=f"仓库名称 (默认: {REPOSITORY})")
81
19
  args = parser.parse_args()
82
20
 
83
21
  if args.file:
@@ -89,8 +27,13 @@ def main():
89
27
  sys.exit(1)
90
28
  file_path = dist_files[-1]
91
29
 
92
- ok = asyncio.run(upload(file_path, nexus_url=args.url, repository=args.repository))
93
- sys.exit(0 if ok else 1)
30
+ if not os.path.exists(file_path):
31
+ print(f"File not found: {file_path}")
32
+ sys.exit(1)
33
+
34
+ repo_url = f"{args.url}/repository/{args.repository}/"
35
+ cmd = ["twine", "upload", "--repository-url", repo_url, file_path, "-u", USERNAME, "-p", PASSWORD]
36
+ sys.exit(subprocess.call(cmd))
94
37
 
95
38
 
96
39
  if __name__ == "__main__":
sycommon/services.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import logging
3
+ import os
3
4
  import yaml
4
5
  from contextlib import asynccontextmanager
5
6
  from dotenv import load_dotenv
@@ -22,10 +23,6 @@ from sycommon.database.redis_service import RedisService
22
23
  from sycommon.database.database_service import DatabaseService
23
24
  from sycommon.xxljob.xxljob_service import XxlJobService
24
25
 
25
- # 延迟导入的类型(避免循环导入)
26
- AgentConfig = Any # type: ignore[misc]
27
- TeamConfig = Any # type: ignore[misc]
28
-
29
26
  # 类型别名,支持更灵活的参数
30
27
  # - bool: True 启用默认配置,False 不启用
31
28
  # - dict: 使用字典配置
@@ -182,6 +179,25 @@ class Services(metaclass=SingletonMeta):
182
179
  "h11_max_incomplete_event_size": cls._config.get('H11MaxIncompleteEventSize', 1024 * 1024 * 10)
183
180
  }
184
181
 
182
+ workers = cls._config.get('Workers', 1)
183
+
184
+ # Worker 进程检测逻辑:
185
+ # 当 workers > 1 时,uvicorn 通过 spawn 创建子进程。子进程会重新 import app 模块,
186
+ # 导致 plugins() 在每个子进程中再次执行。需要确保 Nacos 注册等只执行一次。
187
+ #
188
+ # 检测方式:
189
+ # 1. 第一次调用 plugins() 的是主进程(设置了 _SY_MAIN_PID),主进程正常注册 Nacos
190
+ # 2. 子进程通过 spawn 创建后,_SY_MAIN_PID 环境变量已设置,且 PID 不同 → 判定为 worker
191
+ # 3. 主进程在 plugins() 返回后、uvicorn.run() 前设置 _SY_WORKER=1,
192
+ # 让子进程 import 时能直接通过环境变量判断
193
+ _is_worker = os.environ.get("_SY_WORKER") == "1"
194
+ app.state._is_worker = _is_worker
195
+
196
+ if workers > 1 and not _is_worker:
197
+ # 主进程:设置环境变量,子进程 spawn 后会继承
198
+ os.environ["_SY_WORKER"] = "1"
199
+ logging.info(f"多 Worker 模式 (workers={workers}),主进程将注册 Nacos,子进程跳过")
200
+
185
201
  # 保存可插拔服务开关状态到类变量(实例创建时会被拷贝到实例变量)
186
202
  Services._enable_es = elasticsearch_service is not None
187
203
  Services._enable_redis = redis_service is not None
@@ -197,8 +213,10 @@ class Services(metaclass=SingletonMeta):
197
213
  if multi_agent_service:
198
214
  cls._config['MultiAgentService'] = True
199
215
 
200
- if nacos_service:
216
+ if nacos_service and not _is_worker:
201
217
  nacos_service(cls._config)
218
+ elif _is_worker:
219
+ logging.info("Worker 进程,跳过 Nacos 注册")
202
220
 
203
221
  if middleware:
204
222
  middleware(app, cls._config)
@@ -226,6 +244,9 @@ class Services(metaclass=SingletonMeta):
226
244
  # 获取 Services 实例
227
245
  instance = cls(config, app_instance)
228
246
 
247
+ # 传递 worker 标记到 app.state,供用户 lifespan 使用
248
+ app_instance.state._is_worker = _is_worker
249
+
229
250
  # 沙箱清理任务引用,在 finally 中用于取消
230
251
  sandbox_cleanup_task = None
231
252
 
@@ -292,7 +313,11 @@ class Services(metaclass=SingletonMeta):
292
313
  logging.info("沙箱后台清理任务已启动")
293
314
 
294
315
  # 5. 执行用户定义的生命周期
295
- if cls._user_lifespan:
316
+ # Worker 进程跳过用户 lifespan(如企微机器人等不支持多实例的服务)
317
+ if _is_worker:
318
+ logging.info("Worker 进程,跳过用户生命周期")
319
+ yield
320
+ elif cls._user_lifespan:
296
321
  async with cls._user_lifespan(app_instance):
297
322
  yield
298
323
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.2a9
3
+ Version: 0.2.2a11
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -37,6 +37,7 @@ Requires-Dist: starlette[full]>=1.0.0
37
37
  Requires-Dist: tiktoken>=0.12.0
38
38
  Requires-Dist: uvicorn>=0.46.0
39
39
  Requires-Dist: wecom-aibot-python-sdk>=1.0.2
40
+ Requires-Dist: twine>=6.2.0
40
41
 
41
42
  # sycommon-python-lib
42
43
 
@@ -58,7 +58,7 @@ command/templates/web/client/__init__.py.tpl,sha256=m9P-4q99LnDPLX5npZCmHL4LClPo
58
58
  command/templates/web/model/__init__.py.tpl,sha256=So9EvAU5fSd3Y6uMNwdVJ37kP9xXUiux6m8SQE03HyE,21
59
59
  command/templates/web/tools/__init__.py.tpl,sha256=A_YW5Gb8wmc9FwtRi7lkiCz8otVgwQgSqdtzkNrmkHs,27
60
60
  command/templates/web/tools/mq.py.tpl,sha256=FQpV_dXpDzC-b7kWtSLylhqBCLgUN6GyRhrD9l7CQqw,653
61
- nexus/__init__.py,sha256=sJu1BSSovRMcJ5eBxLFMLAkYajWNSXAeev5gg0yJ80M,2826
61
+ nexus/__init__.py,sha256=MDZdgwMEFUSPvQ2ovibj7GE-4X4JX6ihNAB6GwVPa9g,1225
62
62
  sycli/__init__.py,sha256=mKtD5rrPbg4cF_FrYku5KJrcovLjVdJHvOPW8dit3zY,81
63
63
  sycli/__main__.py,sha256=sYL4dyeRvAN2ef71s2_K8plLCzlaVcuYo7_y6VGz9Tg,115
64
64
  sycli/cli.py,sha256=Uk0y6I8IK1PlPkwW6rqC93yOkgaCZnj7f39C-sv18d0,21642
@@ -125,7 +125,7 @@ sycli/rl/strategy_generator.py,sha256=RzYkaj4jQ_5aNOuj1WA-SXIHfO5h1nTSSHmLZpO-gi
125
125
  sycli/rl/strategy_prompts.py,sha256=dwx291OnyywP6z7uKmoDPZNCC4MNrIyDANdAEO0evJs,2873
126
126
  sycli/skills/__init__.py,sha256=0o9HuaDSvN-z0JZwlDSwU7WxtchQn7f0OtluIMgtC0Q,212
127
127
  sycommon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
128
- sycommon/services.py,sha256=AL3T_Ph6XsFMPE_b37EPammsBbPbmU8cWob7AwVQo4w,20679
128
+ sycommon/services.py,sha256=kcFcIul_p1vNQrh7bgoPDoj3hkB9k2N_zboTaHUIM4o,22155
129
129
  sycommon/agent/__init__.py,sha256=PXxUiDwdzxv3WQfD8R5MKXtV7qZPvbRc0wPxnS-ZsNQ,3752
130
130
  sycommon/agent/agent_manager.py,sha256=UhhaekEumT7g4v_Z1UB4jTp13X0n8M8erYaQdkGGWkA,13620
131
131
  sycommon/agent/chat_events.py,sha256=bWAMWYIZ2L_yqUcn5jq9ius_lQxLHEv4zQLEqX6UaeM,13190
@@ -253,8 +253,8 @@ sycommon/tools/syemail.py,sha256=BDFhgf7WDOQeTcjxJEQdu0dQhnHFPO_p3eI0-Ni3LhQ,561
253
253
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
254
254
  sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
255
255
  sycommon/xxljob/xxljob_service.py,sha256=JIEJaGXhqrTLcyxlyynSrsHg9bBnDNzX-D4qIWLRPUE,6815
256
- sycommon_python_lib-0.2.2a9.dist-info/METADATA,sha256=t7Lst_8MzU1X8cPWqqNcgqeNcIddyG8YEiIxUj7b5Ic,7683
257
- sycommon_python_lib-0.2.2a9.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
258
- sycommon_python_lib-0.2.2a9.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
259
- sycommon_python_lib-0.2.2a9.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
260
- sycommon_python_lib-0.2.2a9.dist-info/RECORD,,
256
+ sycommon_python_lib-0.2.2a11.dist-info/METADATA,sha256=1AKy0x_wUMGHsdNjT532gwHvrRZTpumQZDEc4ry-v_4,7712
257
+ sycommon_python_lib-0.2.2a11.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
258
+ sycommon_python_lib-0.2.2a11.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
259
+ sycommon_python_lib-0.2.2a11.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
260
+ sycommon_python_lib-0.2.2a11.dist-info/RECORD,,