sycommon-python-lib 0.2.1a38__tar.gz → 0.2.1a40__tar.gz

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.
Files changed (144) hide show
  1. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/PKG-INFO +2 -1
  2. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/pyproject.toml +2 -1
  3. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/sandbox/http_sandbox_backend.py +23 -21
  4. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/sandbox.py +142 -58
  5. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/sandbox.py +1 -0
  6. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/sso_user.py +6 -1
  7. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +1 -1
  8. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon_python_lib.egg-info/PKG-INFO +2 -1
  9. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon_python_lib.egg-info/requires.txt +1 -0
  10. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/README.md +0 -0
  11. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/setup.cfg +0 -0
  12. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/__init__.py +0 -0
  13. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/cli.py +0 -0
  14. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/core/__init__.py +0 -0
  15. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/core/console.py +0 -0
  16. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/core/models.py +0 -0
  17. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/core/project.py +0 -0
  18. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/core/utils.py +0 -0
  19. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/templates/__init__.py +0 -0
  20. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/templates/agent/__init__.py +0 -0
  21. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/templates/base/__init__.py +0 -0
  22. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/command/templates/web/__init__.py +0 -0
  23. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/__init__.py +0 -0
  24. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/__init__.py +0 -0
  25. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/agent_manager.py +0 -0
  26. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/chat_events.py +0 -0
  27. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/deep_agent.py +0 -0
  28. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/multi_agent_team.py +0 -0
  29. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/sandbox/__init__.py +0 -0
  30. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/sandbox/file_ops.py +0 -0
  31. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
  32. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/sandbox/sandbox_recovery.py +0 -0
  33. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/agent/sandbox/session.py +0 -0
  34. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/auth/__init__.py +0 -0
  35. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/auth/ldap_service.py +0 -0
  36. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/Config.py +0 -0
  37. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/DatabaseConfig.py +0 -0
  38. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/ElasticsearchConfig.py +0 -0
  39. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/EmbeddingConfig.py +0 -0
  40. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/LLMConfig.py +0 -0
  41. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/LangfuseConfig.py +0 -0
  42. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/MQConfig.py +0 -0
  43. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/RedisConfig.py +0 -0
  44. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/RerankerConfig.py +0 -0
  45. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/SentryConfig.py +0 -0
  46. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/config/__init__.py +0 -0
  47. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/async_base_db_service.py +0 -0
  48. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/async_database_service.py +0 -0
  49. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/base_db_service.py +0 -0
  50. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/database_service.py +0 -0
  51. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/elasticsearch_service.py +0 -0
  52. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/redis_service.py +0 -0
  53. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/database/token_usage_db_service.py +0 -0
  54. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/health/__init__.py +0 -0
  55. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/health/health_check.py +0 -0
  56. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/health/metrics.py +0 -0
  57. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/health/ping.py +0 -0
  58. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/heartbeat_process/__init__.py +0 -0
  59. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
  60. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
  61. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
  62. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/__init__.py +0 -0
  63. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/embedding.py +0 -0
  64. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/get_llm.py +0 -0
  65. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/llm_logger.py +0 -0
  66. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/llm_tokens.py +0 -0
  67. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
  68. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
  69. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/output_fixing_runnable.py +0 -0
  70. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/struct_token.py +0 -0
  71. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/sy_langfuse.py +0 -0
  72. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/token_usage_es_service.py +0 -0
  73. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/token_usage_mysql_service.py +0 -0
  74. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/llm/usage_token.py +0 -0
  75. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/__init__.py +0 -0
  76. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/async_sql_logger.py +0 -0
  77. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/kafka_log.py +0 -0
  78. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/logger_levels.py +0 -0
  79. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/logger_wrapper.py +0 -0
  80. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/process_logger.py +0 -0
  81. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/logging/sql_logger.py +0 -0
  82. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/__init__.py +0 -0
  83. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/background_execution.py +0 -0
  84. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/context.py +0 -0
  85. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/cors.py +0 -0
  86. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/docs.py +0 -0
  87. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/exception.py +0 -0
  88. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/middleware.py +0 -0
  89. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/monitor_memory.py +0 -0
  90. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/mq.py +0 -0
  91. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/timeout.py +0 -0
  92. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/middleware/traceid.py +0 -0
  93. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/__init__.py +0 -0
  94. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/base_http.py +0 -0
  95. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/log.py +0 -0
  96. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/mqlistener_config.py +0 -0
  97. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/mqmsg_model.py +0 -0
  98. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/mqsend_config.py +0 -0
  99. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/token_usage.py +0 -0
  100. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/models/token_usage_mysql.py +0 -0
  101. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/notice/__init__.py +0 -0
  102. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/notice/uvicorn_monitor.py +0 -0
  103. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
  104. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
  105. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
  106. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
  107. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
  108. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
  109. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
  110. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
  111. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/sentry/__init__.py +0 -0
  112. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/sentry/sy_sentry.py +0 -0
  113. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/services.py +0 -0
  114. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/sse/__init__.py +0 -0
  115. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/sse/event.py +0 -0
  116. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/sse/sse.py +0 -0
  117. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/__init__.py +0 -0
  118. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/example.py +0 -0
  119. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/example2.py +0 -0
  120. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/feign.py +0 -0
  121. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/feign_client.py +0 -0
  122. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/nacos_client_base.py +0 -0
  123. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/nacos_config_manager.py +0 -0
  124. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
  125. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/nacos_service.py +0 -0
  126. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
  127. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/nacos_service_registration.py +0 -0
  128. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/synacos/param.py +0 -0
  129. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tests/deep_agent_server.py +0 -0
  130. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tests/test_deep_agent.py +0 -0
  131. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tests/test_email.py +0 -0
  132. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tests/test_mq.py +0 -0
  133. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/__init__.py +0 -0
  134. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/async_utils.py +0 -0
  135. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/docs.py +0 -0
  136. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/env.py +0 -0
  137. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/merge_headers.py +0 -0
  138. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/snowflake.py +0 -0
  139. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/syemail.py +0 -0
  140. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon/tools/timing.py +0 -0
  141. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
  142. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  143. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  144. {sycommon_python_lib-0.2.1a38 → sycommon_python_lib-0.2.1a40}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.1a38
3
+ Version: 0.2.1a40
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -19,6 +19,7 @@ Requires-Dist: langchain-core>=1.2.20
19
19
  Requires-Dist: langchain-openai>=1.1.11
20
20
  Requires-Dist: langfuse>=4.0.1
21
21
  Requires-Dist: langgraph>=1.1.3
22
+ Requires-Dist: langgraph-checkpoint-redis>=0.4.0
22
23
  Requires-Dist: ldap3>=2.9.1
23
24
  Requires-Dist: loguru>=0.7.3
24
25
  Requires-Dist: mysql-connector-python>=9.6.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sycommon-python-lib"
3
- version = "0.2.1a38"
3
+ version = "0.2.1a40"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -20,6 +20,7 @@ dependencies = [
20
20
  "langchain-openai>=1.1.11",
21
21
  "langfuse>=4.0.1",
22
22
  "langgraph>=1.1.3",
23
+ "langgraph-checkpoint-redis>=0.4.0",
23
24
  "ldap3>=2.9.1",
24
25
  "loguru>=0.7.3",
25
26
  "mysql-connector-python>=9.6.0",
@@ -558,32 +558,34 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
558
558
 
559
559
  SYLogger.info(f"[Sandbox] 准备上传 {len(files_to_upload)} 个文件到沙箱")
560
560
 
561
- success_count = 0
562
- failed_count = 0
563
- errors = []
561
+ semaphore = asyncio.Semaphore(10)
564
562
 
565
- for idx, (sandbox_path, local_file) in enumerate(files_to_upload):
566
- try:
563
+ async def _upload_one(idx, sandbox_path, local_file):
564
+ async with semaphore:
567
565
  if idx % 10 == 0:
568
566
  SYLogger.info(f"[Sandbox] 上传进度: {idx}/{len(files_to_upload)}")
567
+ try:
568
+ content = await asyncio.to_thread(lambda f=local_file: open(f, "rb").read())
569
+ result = await self._post_async(f"{SANDBOX_API_PREFIX}/upload", {
570
+ "path": sandbox_path,
571
+ "content": base64.b64encode(content).decode(),
572
+ "user_id": self._user_id
573
+ })
574
+ if result.get("error"):
575
+ return {"ok": False, "path": sandbox_path, "error": result["error"]}
576
+ return {"ok": True}
577
+ except Exception as e:
578
+ return {"ok": False, "path": sandbox_path, "error": str(e)}
569
579
 
570
- content = await asyncio.to_thread(lambda f=local_file: open(f, "rb").read())
571
-
572
- result = await self._post_async(f"{SANDBOX_API_PREFIX}/upload", {
573
- "path": sandbox_path,
574
- "content": base64.b64encode(content).decode(),
575
- "user_id": self._user_id
576
- })
577
-
578
- if result.get("error"):
579
- failed_count += 1
580
- errors.append({"path": sandbox_path, "error": result["error"]})
581
- else:
582
- success_count += 1
580
+ upload_tasks = [
581
+ _upload_one(idx, sandbox_path, local_file)
582
+ for idx, (sandbox_path, local_file) in enumerate(files_to_upload)
583
+ ]
584
+ upload_results = await asyncio.gather(*upload_tasks)
583
585
 
584
- except Exception as e:
585
- failed_count += 1
586
- errors.append({"path": sandbox_path, "error": str(e)})
586
+ success_count = sum(1 for r in upload_results if r["ok"])
587
+ failed_count = sum(1 for r in upload_results if not r["ok"])
588
+ errors = [{"path": r["path"], "error": r["error"]} for r in upload_results if not r["ok"]]
587
589
 
588
590
  SYLogger.info(f"[Sandbox] _upload_directory_async 完成: 成功={success_count}, 失败={failed_count}")
589
591
  return {"success": success_count, "failed": failed_count, "errors": errors}
@@ -21,13 +21,14 @@
21
21
  """
22
22
 
23
23
  import os
24
- import subprocess
24
+ import asyncio
25
25
  import base64
26
26
  import shutil
27
27
  import tempfile
28
28
  import platform
29
29
  import uuid
30
30
  import threading
31
+ import shlex
31
32
  from datetime import datetime
32
33
  from fastapi import FastAPI, APIRouter, Request, Header
33
34
  from typing import Dict
@@ -55,6 +56,26 @@ from sycommon.models.sandbox import (
55
56
  _background_processes: Dict[str, dict] = {}
56
57
  _process_lock = threading.Lock()
57
58
 
59
+ # 进程清理配置
60
+ PROCESS_CLEANUP_TTL = 300 # 已完成进程保留时间(秒)
61
+ PROCESS_CLEANUP_INTERVAL = 60 # 清理检查间隔(秒)
62
+ _cleanup_task = None
63
+
64
+
65
+ async def _cleanup_finished_processes():
66
+ """异步清理已完成的过期进程"""
67
+ while True:
68
+ await asyncio.sleep(PROCESS_CLEANUP_INTERVAL)
69
+ now = datetime.now().timestamp()
70
+ with _process_lock:
71
+ expired = [
72
+ k for k, v in _background_processes.items()
73
+ if v.get("completed") and now - v.get("completed_at", 0) > PROCESS_CLEANUP_TTL
74
+ ]
75
+ for k in expired:
76
+ _background_processes.pop(k, None)
77
+ SYLogger.debug(f"[Sandbox Server] 清理过期进程: {k}")
78
+
58
79
 
59
80
  # ============== 常量配置 ==============
60
81
 
@@ -70,7 +91,6 @@ if platform.system() == "Linux":
70
91
  os.makedirs(_default_workspace, exist_ok=True)
71
92
  except PermissionError:
72
93
  # 降级到临时目录
73
- import tempfile
74
94
  _default_workspace = os.path.join(tempfile.gettempdir(), "sycommon_sandbox")
75
95
  else:
76
96
  # macOS / Windows 使用临时目录
@@ -150,6 +170,13 @@ def setup_sandbox_handler(app: FastAPI, config: dict = None):
150
170
 
151
171
  initialized = False
152
172
 
173
+ # 启动后台清理任务(在 FastAPI startup event 中启动)
174
+ @app.on_event("startup")
175
+ async def start_cleanup_task():
176
+ global _cleanup_task
177
+ if _cleanup_task is None:
178
+ _cleanup_task = asyncio.create_task(_cleanup_finished_processes())
179
+
153
180
  @app.middleware("http")
154
181
  async def sandbox_middleware(request: Request, call_next):
155
182
  nonlocal initialized
@@ -189,10 +216,12 @@ def setup_sandbox_handler(app: FastAPI, config: dict = None):
189
216
  # 创建 shell 初始化脚本,设置路径映射
190
217
  # 让绝对路径自动映射到 workspace 下
191
218
  # 使用 heredoc 避免命令中的特殊字符(如正则表达式)被 shell 错误解析
219
+ # 对 workspace 路径进行 shell 转义,防止路径中包含特殊字符(如单引号)导致语法错误
220
+ safe_workspace = shlex.quote(workspace)
192
221
  init_script = f'''
193
222
  # 沙箱环境初始化
194
- export SANDBOX_ROOT="{workspace}"
195
- export _SANDBOX_WORKSPACE="{workspace}"
223
+ export SANDBOX_ROOT={safe_workspace}
224
+ export _SANDBOX_WORKSPACE={safe_workspace}
196
225
 
197
226
  # 路径映射函数:将绝对路径映射到沙箱内(如果尚未在沙箱内)
198
227
  sandbox_path() {{
@@ -267,23 +296,29 @@ COMMAND_EOF
267
296
  env["SANDBOX_ROOT"] = workspace
268
297
 
269
298
  try:
270
- # 使用 Popen 以便超时时能正确终止进程
271
- process = subprocess.Popen(
299
+ # 使用 asyncio.create_subprocess_shell 异步执行命令,避免阻塞事件循环
300
+ process = await asyncio.create_subprocess_shell(
272
301
  full_command,
273
- shell=True,
274
- stdout=subprocess.PIPE,
275
- stderr=subprocess.PIPE,
276
- text=True,
302
+ stdout=asyncio.subprocess.PIPE,
303
+ stderr=asyncio.subprocess.PIPE,
277
304
  cwd=workspace,
278
305
  env=env
279
306
  )
280
307
 
281
308
  try:
282
- stdout, stderr = process.communicate(timeout=req.timeout)
283
- except subprocess.TimeoutExpired:
309
+ stdout, stderr = await asyncio.wait_for(
310
+ process.communicate(),
311
+ timeout=req.timeout
312
+ )
313
+ # decode bytes to string
314
+ stdout = stdout.decode('utf-8') if stdout else ''
315
+ stderr = stderr.decode('utf-8') if stderr else ''
316
+ except asyncio.TimeoutError:
284
317
  # 超时时终止进程
285
318
  process.kill()
286
- stdout, stderr = process.communicate()
319
+ stdout, stderr = await process.communicate()
320
+ stdout = stdout.decode('utf-8') if stdout else ''
321
+ stderr = stderr.decode('utf-8') if stderr else ''
287
322
  SYLogger.error(f"[Sandbox Server] 执行超时: {req.timeout}s")
288
323
  return ExecuteResponse(
289
324
  output=f"Error: Command timed out after {req.timeout} seconds.\nPartial output:\n{stdout}",
@@ -334,7 +369,7 @@ COMMAND_EOF
334
369
  if not req.command:
335
370
  raise ValueError("Command must be a non-empty string.")
336
371
 
337
- process_id = str(uuid.uuid4())[:8]
372
+ process_id = str(uuid.uuid4()).replace('-', '')[:16]
338
373
  SYLogger.info(f"[Sandbox Server] 后台执行命令: {req.command} (user={req.user_id}, process_id={process_id})")
339
374
 
340
375
  # 创建常用目录
@@ -342,10 +377,12 @@ COMMAND_EOF
342
377
  os.makedirs(os.path.join(workspace, subdir), exist_ok=True)
343
378
 
344
379
  # 使用与 execute 相同的 shell 初始化脚本
380
+ # 对 workspace 路径进行 shell 转义
381
+ safe_workspace = shlex.quote(workspace)
345
382
  init_script = f'''
346
383
  # 沙箱环境初始化
347
- export SANDBOX_ROOT="{workspace}"
348
- export _SANDBOX_WORKSPACE="{workspace}"
384
+ export SANDBOX_ROOT={safe_workspace}
385
+ export _SANDBOX_WORKSPACE={safe_workspace}
349
386
 
350
387
  sandbox_path() {{
351
388
  local path="$1"
@@ -406,12 +443,11 @@ COMMAND_EOF
406
443
  env["SANDBOX_ROOT"] = workspace
407
444
 
408
445
  try:
409
- process = subprocess.Popen(
446
+ # 使用 asyncio.create_subprocess_shell 异步执行命令
447
+ process = await asyncio.create_subprocess_shell(
410
448
  full_command,
411
- shell=True,
412
- stdout=subprocess.PIPE,
413
- stderr=subprocess.PIPE,
414
- text=True,
449
+ stdout=asyncio.subprocess.PIPE,
450
+ stderr=asyncio.subprocess.PIPE,
415
451
  cwd=workspace,
416
452
  env=env
417
453
  )
@@ -429,17 +465,25 @@ COMMAND_EOF
429
465
  "killed": False
430
466
  }
431
467
 
432
- # 启动后台线程监控进程
433
- def monitor_process():
468
+ # 启动后台异步任务监控进程
469
+ async def monitor_process():
434
470
  try:
435
- stdout, stderr = process.communicate(timeout=req.timeout)
436
- except subprocess.TimeoutExpired:
471
+ stdout, stderr = await asyncio.wait_for(
472
+ process.communicate(),
473
+ timeout=req.timeout
474
+ )
475
+ stdout = stdout.decode('utf-8') if stdout else ''
476
+ stderr = stderr.decode('utf-8') if stderr else ''
477
+ except asyncio.TimeoutError:
437
478
  process.kill()
438
- stdout, stderr = process.communicate()
479
+ stdout, stderr = await process.communicate()
480
+ stdout = stdout.decode('utf-8') if stdout else ''
481
+ stderr = stderr.decode('utf-8') if stderr else ''
439
482
  with _process_lock:
440
483
  if process_id in _background_processes:
441
484
  _background_processes[process_id]["killed"] = True
442
485
  _background_processes[process_id]["output_buffer"] = f"Error: Command timed out after {req.timeout} seconds.\nPartial output:\n{stdout}"
486
+ return
443
487
  except Exception as e:
444
488
  with _process_lock:
445
489
  if process_id in _background_processes:
@@ -467,9 +511,10 @@ COMMAND_EOF
467
511
  _background_processes[process_id]["output_buffer"] = output
468
512
  _background_processes[process_id]["exit_code"] = exit_code
469
513
  _background_processes[process_id]["completed"] = True
514
+ _background_processes[process_id]["completed_at"] = datetime.now().timestamp()
470
515
 
471
- monitor_thread = threading.Thread(target=monitor_process, daemon=True)
472
- monitor_thread.start()
516
+ # 创建后台异步任务
517
+ asyncio.create_task(monitor_process())
473
518
 
474
519
  return ExecuteBackgroundResponse(process_id=process_id, status="started")
475
520
 
@@ -498,13 +543,13 @@ COMMAND_EOF
498
543
 
499
544
  process = proc_info["process"]
500
545
 
501
- # 判断进程状态
546
+ # 判断进程状态(asyncio subprocess 使用 returncode 而非 poll)
502
547
  if proc_info["killed"]:
503
548
  status = "timeout"
504
549
  elif proc_info["completed"]:
505
550
  status = "completed"
506
- elif process.poll() is not None:
507
- # 进程已结束但还没被监控线程处理(极少情况)
551
+ elif process.returncode is not None:
552
+ # 进程已结束但还没被监控任务处理(极少情况)
508
553
  status = "completed"
509
554
  else:
510
555
  status = "running"
@@ -548,8 +593,7 @@ COMMAND_EOF
548
593
  process = proc_info["process"]
549
594
  try:
550
595
  process.kill()
551
- # 等待进程结束
552
- process.wait(timeout=5)
596
+ # asyncio subprocess 没有 wait(timeout),直接检查 returncode
553
597
  except Exception as e:
554
598
  SYLogger.warning(f"[Sandbox Server] 终止进程时出错: {e}")
555
599
 
@@ -633,23 +677,23 @@ COMMAND_EOF
633
677
  SYLogger.info(f"[Sandbox Server] 读取文件: {file_path} (offset={req.offset}, limit={req.limit})")
634
678
 
635
679
  try:
680
+ output = []
681
+ lines_read = 0
636
682
  with open(file_path, 'r') as f:
637
- lines = f.readlines()
638
-
639
- if not lines:
683
+ for i, line in enumerate(f):
684
+ if i < req.offset:
685
+ continue
686
+ if lines_read >= req.limit:
687
+ break
688
+ line_num = i + 1
689
+ output.append(f"{line_num:6d}\t{line.rstrip()}")
690
+ lines_read += 1
691
+
692
+ if not output:
640
693
  return ReadResponse(content="System reminder: File exists but has empty contents", error=None)
641
694
 
642
- start = req.offset
643
- end = req.offset + req.limit
644
- selected = lines[start:end]
645
-
646
- output = []
647
- for i, line in enumerate(selected):
648
- line_num = req.offset + i + 1
649
- output.append(f"{line_num:6d}\t{line.rstrip()}")
650
-
651
695
  content = "\n".join(output)
652
- SYLogger.info(f"[Sandbox Server] 读取完成: {len(selected)} 行")
696
+ SYLogger.info(f"[Sandbox Server] 读取完成: {lines_read} 行")
653
697
  return ReadResponse(content=content, error=None)
654
698
 
655
699
  except Exception as e:
@@ -819,8 +863,6 @@ COMMAND_EOF
819
863
  @sandbox_router.post("/grep", response_model=list[GrepMatch])
820
864
  async def grep_search(req: GrepRequest):
821
865
  """grep 搜索内容"""
822
- import shlex
823
-
824
866
  workspace = get_user_workspace(req.user_id)
825
867
  try:
826
868
  search_path = resolve_sandbox_path(req.path or "/", workspace)
@@ -831,24 +873,47 @@ COMMAND_EOF
831
873
  quoted_path = shlex.quote(search_path)
832
874
  grep_opts = "-rHnF"
833
875
 
834
- glob_pattern = f"--include='{req.glob}'" if req.glob else ""
876
+ # 安全处理 glob_pattern,使用 shlex.quote 防止命令注入
877
+ glob_pattern = f"--include={shlex.quote(req.glob)}" if req.glob else ""
835
878
  pattern_escaped = shlex.quote(req.pattern)
836
879
 
837
880
  cmd = f"grep {grep_opts} {glob_pattern} -e {pattern_escaped} {quoted_path} 2>/dev/null || true"
838
881
 
839
- result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
882
+ # 使用 asyncio.create_subprocess_shell 异步执行
883
+ proc = await asyncio.create_subprocess_shell(
884
+ cmd,
885
+ stdout=asyncio.subprocess.PIPE,
886
+ stderr=asyncio.subprocess.PIPE
887
+ )
888
+ stdout, _ = await proc.communicate()
889
+ result_text = stdout.decode('utf-8') if stdout else ''
840
890
 
841
891
  matches = []
842
- for line in result.stdout.strip().split("\n"):
892
+ for line in result_text.strip().split("\n"):
843
893
  if not line:
844
894
  continue
845
- parts = line.split(":", 2)
846
- if len(parts) >= 3:
847
- matches.append(GrepMatch(
848
- path=parts[0],
849
- line=int(parts[1]),
850
- text=parts[2]
851
- ))
895
+ # 使用更稳健的解析方式:找到第一个冒号(行号前)和第二个冒号(内容前)
896
+ # 先获取行号和内容部分(路径:path:line:text)
897
+ # 格式: path:line:text(text 可能包含冒号)
898
+ first_colon = line.find(":")
899
+ if first_colon == -1:
900
+ continue
901
+ second_colon = line.find(":", first_colon + 1)
902
+ if second_colon == -1:
903
+ continue
904
+
905
+ path_part = line[:first_colon]
906
+ try:
907
+ line_num = int(line[first_colon + 1:second_colon])
908
+ except ValueError:
909
+ continue
910
+ text_part = line[second_colon + 1:]
911
+
912
+ matches.append(GrepMatch(
913
+ path=path_part,
914
+ line=line_num,
915
+ text=text_part
916
+ ))
852
917
 
853
918
  return matches
854
919
 
@@ -867,7 +932,12 @@ COMMAND_EOF
867
932
  if not os.path.isdir(target_path):
868
933
  return LsResponse(error="not_a_directory")
869
934
 
935
+ # 递归遍历深度和文件数量限制
936
+ MAX_WALK_DEPTH = 10
937
+ MAX_FILES = 10000
938
+
870
939
  files = []
940
+ file_count = 0
871
941
  try:
872
942
  for item in os.listdir(target_path):
873
943
  item_path = os.path.join(target_path, item)
@@ -882,11 +952,24 @@ COMMAND_EOF
882
952
  except Exception:
883
953
  pass
884
954
  files.append(info)
955
+ file_count += 1
956
+
957
+ if file_count >= MAX_FILES:
958
+ return LsResponse(files=files, warning=f"Results truncated at {MAX_FILES} files")
885
959
 
886
960
  # 递归获取子目录文件
887
961
  if info.is_dir:
888
962
  for root, dirs, filenames in os.walk(item_path):
963
+ # 限制深度
964
+ depth = root[len(item_path):].count(os.sep)
965
+ if depth >= MAX_WALK_DEPTH:
966
+ dirs.clear() # 不再递归更深层
967
+ continue
968
+
889
969
  for filename in filenames:
970
+ if file_count >= MAX_FILES:
971
+ return LsResponse(files=files, warning=f"Results truncated at {MAX_FILES} files")
972
+
890
973
  filepath = os.path.join(root, filename)
891
974
  file_info = FileInfo(path=filepath)
892
975
  try:
@@ -898,6 +981,7 @@ COMMAND_EOF
898
981
  except Exception:
899
982
  pass
900
983
  files.append(file_info)
984
+ file_count += 1
901
985
  except Exception as e:
902
986
  return LsResponse(error=str(e))
903
987
 
@@ -70,6 +70,7 @@ class LsResponse(BaseModel):
70
70
  """列出目录响应"""
71
71
  files: list[FileInfo] = []
72
72
  error: Optional[str] = None
73
+ warning: Optional[str] = None # 用于截断警告
73
74
 
74
75
 
75
76
  class GlobRequest(BaseModel):
@@ -1,11 +1,16 @@
1
1
  from pydantic import BaseModel, ConfigDict
2
+ from pydantic.alias_generators import to_camel
2
3
  from typing import List, Optional
3
4
  from datetime import datetime
4
5
 
5
6
 
6
7
  class SsoUser(BaseModel):
7
8
  """SSO用户模型,对应Java的SsoUser类"""
8
- model_config = ConfigDict(protected_namespaces=())
9
+ model_config = ConfigDict(
10
+ protected_namespaces=(),
11
+ alias_generator=to_camel,
12
+ populate_by_name=True,
13
+ )
9
14
  tenant_id: Optional[str] = None
10
15
  customer_id: Optional[str] = None
11
16
  customer_name: Optional[str] = None
@@ -209,7 +209,7 @@ class RabbitMQProducerManager(RabbitMQClientManager):
209
209
  request_path="/",
210
210
  req_type="SYSTEM",
211
211
  trace_id=logger.get_trace_id(),
212
- ).model_dump_json(),
212
+ ).model_dump_json(by_alias=True),
213
213
  "trace-id": logger.get_trace_id(),
214
214
  "createTime": str(int(time.time() * 1000)),
215
215
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.1a38
3
+ Version: 0.2.1a40
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -19,6 +19,7 @@ Requires-Dist: langchain-core>=1.2.20
19
19
  Requires-Dist: langchain-openai>=1.1.11
20
20
  Requires-Dist: langfuse>=4.0.1
21
21
  Requires-Dist: langgraph>=1.1.3
22
+ Requires-Dist: langgraph-checkpoint-redis>=0.4.0
22
23
  Requires-Dist: ldap3>=2.9.1
23
24
  Requires-Dist: loguru>=0.7.3
24
25
  Requires-Dist: mysql-connector-python>=9.6.0
@@ -13,6 +13,7 @@ langchain-core>=1.2.20
13
13
  langchain-openai>=1.1.11
14
14
  langfuse>=4.0.1
15
15
  langgraph>=1.1.3
16
+ langgraph-checkpoint-redis>=0.4.0
16
17
  ldap3>=2.9.1
17
18
  loguru>=0.7.3
18
19
  mysql-connector-python>=9.6.0