sycommon-python-lib 0.2.1a36__tar.gz → 0.2.1a37__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.1a36 → sycommon_python_lib-0.2.1a37}/PKG-INFO +1 -1
  2. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/pyproject.toml +1 -1
  3. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/sandbox/file_ops.py +156 -13
  4. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/sandbox/http_sandbox_backend.py +288 -53
  5. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
  6. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/README.md +0 -0
  7. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/setup.cfg +0 -0
  8. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/__init__.py +0 -0
  9. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/cli.py +0 -0
  10. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/core/__init__.py +0 -0
  11. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/core/console.py +0 -0
  12. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/core/models.py +0 -0
  13. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/core/project.py +0 -0
  14. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/core/utils.py +0 -0
  15. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/templates/__init__.py +0 -0
  16. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/templates/agent/__init__.py +0 -0
  17. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/templates/base/__init__.py +0 -0
  18. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/command/templates/web/__init__.py +0 -0
  19. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/__init__.py +0 -0
  20. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/__init__.py +0 -0
  21. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/agent_manager.py +0 -0
  22. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/chat_events.py +0 -0
  23. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/deep_agent.py +0 -0
  24. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/multi_agent_team.py +0 -0
  25. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/sandbox/__init__.py +0 -0
  26. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
  27. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/sandbox/sandbox_recovery.py +0 -0
  28. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/agent/sandbox/session.py +0 -0
  29. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/auth/__init__.py +0 -0
  30. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/auth/ldap_service.py +0 -0
  31. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/Config.py +0 -0
  32. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/DatabaseConfig.py +0 -0
  33. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/ElasticsearchConfig.py +0 -0
  34. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/EmbeddingConfig.py +0 -0
  35. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/LLMConfig.py +0 -0
  36. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/LangfuseConfig.py +0 -0
  37. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/MQConfig.py +0 -0
  38. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/RedisConfig.py +0 -0
  39. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/RerankerConfig.py +0 -0
  40. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/SentryConfig.py +0 -0
  41. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/config/__init__.py +0 -0
  42. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/async_base_db_service.py +0 -0
  43. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/async_database_service.py +0 -0
  44. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/base_db_service.py +0 -0
  45. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/database_service.py +0 -0
  46. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/elasticsearch_service.py +0 -0
  47. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/redis_service.py +0 -0
  48. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/database/token_usage_db_service.py +0 -0
  49. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/health/__init__.py +0 -0
  50. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/health/health_check.py +0 -0
  51. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/health/metrics.py +0 -0
  52. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/health/ping.py +0 -0
  53. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/heartbeat_process/__init__.py +0 -0
  54. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
  55. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
  56. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
  57. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/__init__.py +0 -0
  58. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/embedding.py +0 -0
  59. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/get_llm.py +0 -0
  60. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/llm_logger.py +0 -0
  61. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/llm_tokens.py +0 -0
  62. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
  63. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
  64. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/output_fixing_runnable.py +0 -0
  65. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/struct_token.py +0 -0
  66. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/sy_langfuse.py +0 -0
  67. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/token_usage_es_service.py +0 -0
  68. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/token_usage_mysql_service.py +0 -0
  69. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/llm/usage_token.py +0 -0
  70. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/__init__.py +0 -0
  71. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/async_sql_logger.py +0 -0
  72. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/kafka_log.py +0 -0
  73. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/logger_levels.py +0 -0
  74. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/logger_wrapper.py +0 -0
  75. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/process_logger.py +0 -0
  76. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/logging/sql_logger.py +0 -0
  77. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/__init__.py +0 -0
  78. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/background_execution.py +0 -0
  79. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/context.py +0 -0
  80. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/cors.py +0 -0
  81. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/docs.py +0 -0
  82. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/exception.py +0 -0
  83. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/middleware.py +0 -0
  84. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/monitor_memory.py +0 -0
  85. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/mq.py +0 -0
  86. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/sandbox.py +0 -0
  87. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/timeout.py +0 -0
  88. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/middleware/traceid.py +0 -0
  89. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/__init__.py +0 -0
  90. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/base_http.py +0 -0
  91. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/log.py +0 -0
  92. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/mqlistener_config.py +0 -0
  93. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/mqmsg_model.py +0 -0
  94. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/mqsend_config.py +0 -0
  95. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/sandbox.py +0 -0
  96. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/sso_user.py +0 -0
  97. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/token_usage.py +0 -0
  98. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/models/token_usage_mysql.py +0 -0
  99. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/notice/__init__.py +0 -0
  100. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/notice/uvicorn_monitor.py +0 -0
  101. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
  102. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
  103. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
  104. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
  105. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
  106. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
  107. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
  108. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
  109. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
  110. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/sentry/__init__.py +0 -0
  111. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/sentry/sy_sentry.py +0 -0
  112. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/services.py +0 -0
  113. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/sse/__init__.py +0 -0
  114. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/sse/event.py +0 -0
  115. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/sse/sse.py +0 -0
  116. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/__init__.py +0 -0
  117. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/example.py +0 -0
  118. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/example2.py +0 -0
  119. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/feign.py +0 -0
  120. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/feign_client.py +0 -0
  121. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/nacos_client_base.py +0 -0
  122. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/nacos_config_manager.py +0 -0
  123. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
  124. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/nacos_service.py +0 -0
  125. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
  126. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/nacos_service_registration.py +0 -0
  127. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/synacos/param.py +0 -0
  128. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tests/deep_agent_server.py +0 -0
  129. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tests/test_deep_agent.py +0 -0
  130. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tests/test_email.py +0 -0
  131. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tests/test_mq.py +0 -0
  132. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/__init__.py +0 -0
  133. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/async_utils.py +0 -0
  134. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/docs.py +0 -0
  135. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/env.py +0 -0
  136. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/merge_headers.py +0 -0
  137. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/snowflake.py +0 -0
  138. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/syemail.py +0 -0
  139. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon/tools/timing.py +0 -0
  140. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
  141. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  142. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  143. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
  144. {sycommon_python_lib-0.2.1a36 → sycommon_python_lib-0.2.1a37}/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.1a36
3
+ Version: 0.2.1a37
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sycommon-python-lib"
3
- version = "0.2.1a36"
3
+ version = "0.2.1a37"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -33,7 +33,7 @@ class FileOperationsMixin:
33
33
  """文件操作 Mixin
34
34
 
35
35
  适配 deepagents 0.5.0a2 协议
36
- 同步方法为主实现,异步方法通过 asyncio.to_thread 调用
36
+ 同步方法为主实现,异步方法使用 aiohttp 实现
37
37
  """
38
38
 
39
39
  _timeout: int # 类型提示,子类需要提供此属性
@@ -46,6 +46,10 @@ class FileOperationsMixin:
46
46
  """发送同步 POST 请求"""
47
47
  ...
48
48
 
49
+ async def _post_async_with_failover(self: "HTTPSandboxBackend", endpoint: str, data: dict, timeout: int = None) -> dict:
50
+ """发送异步 POST 请求(带故障转移)"""
51
+ ...
52
+
49
53
  def _ensure_synced_sync(self: "HTTPSandboxBackend"):
50
54
  """确保目录已同步(同步版本)"""
51
55
  if not self._auto_sync:
@@ -72,6 +76,32 @@ class FileOperationsMixin:
72
76
  self._workspace_verified = True
73
77
  SYLogger.info(f"[Sandbox] 同步完成")
74
78
 
79
+ async def _ensure_synced_async(self: "HTTPSandboxBackend"):
80
+ """确保目录已同步(异步版本)"""
81
+ if not self._auto_sync:
82
+ return
83
+
84
+ if self._synced and self._workspace_verified:
85
+ return
86
+
87
+ SYLogger.info(f"[Sandbox] 开始检查工作空间同步状态(异步)...")
88
+
89
+ try:
90
+ workspace_exists = await self._check_workspace_exists_async()
91
+ except Exception as e:
92
+ SYLogger.warning(f"[Sandbox] 检查工作空间异常: {e},跳过检查")
93
+ workspace_exists = True
94
+
95
+ if not workspace_exists:
96
+ SYLogger.info(f"[Sandbox] 工作空间不存在或已被清空,重新同步...")
97
+ self._synced = False
98
+
99
+ if not self._synced:
100
+ SYLogger.info(f"[Sandbox] 开始同步目录(异步)...")
101
+ await self._sync_async()
102
+ self._workspace_verified = True
103
+ SYLogger.info(f"[Sandbox] 同步完成")
104
+
75
105
  def _check_workspace_exists_sync(self: "HTTPSandboxBackend") -> bool:
76
106
  """检查沙箱工作空间是否存在(同步版本)"""
77
107
  try:
@@ -84,10 +114,26 @@ class FileOperationsMixin:
84
114
  SYLogger.warning(f"[Sandbox] 检查工作空间失败: {e}")
85
115
  return False
86
116
 
117
+ async def _check_workspace_exists_async(self: "HTTPSandboxBackend") -> bool:
118
+ """检查沙箱工作空间是否存在(异步版本)"""
119
+ try:
120
+ await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/ls", {
121
+ "path": "/",
122
+ "user_id": self.user_id
123
+ })
124
+ return True
125
+ except Exception as e:
126
+ SYLogger.warning(f"[Sandbox] 检查工作空间失败: {e}")
127
+ return False
128
+
87
129
  def _sync_sync(self: "HTTPSandboxBackend"):
88
130
  """同步目录(同步版本,由子类实现)"""
89
131
  ...
90
132
 
133
+ async def _sync_async(self: "HTTPSandboxBackend") -> dict:
134
+ """同步目录(异步版本,由子类实现)"""
135
+ ...
136
+
91
137
  # ============== 目录操作 ==============
92
138
 
93
139
  def ls(self: "HTTPSandboxBackend", path: str) -> LsResult:
@@ -107,8 +153,20 @@ class FileOperationsMixin:
107
153
  return LsResult(error=str(e))
108
154
 
109
155
  async def als(self: "HTTPSandboxBackend", path: str) -> LsResult:
110
- """异步列出目录内容"""
111
- return await asyncio.to_thread(self.ls, path)
156
+ """异步列出目录内容(真正的异步实现)"""
157
+ try:
158
+ await self._ensure_synced_async()
159
+ SYLogger.info(f"[Sandbox] 异步列出目录: {path}")
160
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/ls", {
161
+ "path": path,
162
+ "user_id": self.user_id
163
+ })
164
+ entries = [FileInfo(**item) for item in result]
165
+ SYLogger.info(f"[Sandbox] 目录内容: {len(entries)} 项")
166
+ return LsResult(entries=entries)
167
+ except Exception as e:
168
+ SYLogger.error(f"[Sandbox] 异步列出目录失败: {e}")
169
+ return LsResult(error=str(e))
112
170
 
113
171
  # ============== 文件读写 ==============
114
172
 
@@ -153,8 +211,33 @@ class FileOperationsMixin:
153
211
  offset: int = 0,
154
212
  limit: int = 2000,
155
213
  ) -> ReadResult:
156
- """异步读取文件内容"""
157
- return await asyncio.to_thread(self.read, file_path, offset, limit)
214
+ """异步读取文件内容(真正的异步实现)"""
215
+ try:
216
+ await self._ensure_synced_async()
217
+ SYLogger.info(f"[Sandbox] 异步读取文件: {file_path} (offset={offset}, limit={limit})")
218
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/read", {
219
+ "file_path": file_path,
220
+ "user_id": self.user_id,
221
+ "offset": offset,
222
+ "limit": limit
223
+ })
224
+ if result.get("error"):
225
+ SYLogger.error(f"[Sandbox] 异步读取文件失败: {result['error']}")
226
+ return ReadResult(error=result["error"])
227
+ content = result.get("content", "")
228
+ SYLogger.info(f"[Sandbox] 异步读取完成: {len(content)} 字符")
229
+ from datetime import datetime
230
+ now = datetime.now().isoformat()
231
+ file_data = FileData(
232
+ content=content,
233
+ encoding="utf-8",
234
+ created_at=result.get("created_at", now),
235
+ modified_at=result.get("modified_at", now),
236
+ )
237
+ return ReadResult(file_data=file_data)
238
+ except Exception as e:
239
+ SYLogger.error(f"[Sandbox] 异步读取文件异常: {e}")
240
+ return ReadResult(error=str(e))
158
241
 
159
242
  def write(
160
243
  self: "HTTPSandboxBackend",
@@ -188,8 +271,27 @@ class FileOperationsMixin:
188
271
  file_path: str,
189
272
  content: str,
190
273
  ) -> WriteResult:
191
- """异步写入文件"""
192
- return await asyncio.to_thread(self.write, file_path, content)
274
+ """异步写入文件(真正的异步实现)"""
275
+ try:
276
+ await self._ensure_synced_async()
277
+ SYLogger.info(f"[Sandbox] 异步写入文件: {file_path} ({len(content)} 字符)")
278
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/write", {
279
+ "file_path": file_path,
280
+ "content": base64.b64encode(content.encode()).decode(),
281
+ "user_id": self.user_id
282
+ })
283
+ write_result = WriteResult(
284
+ error=result.get("error"),
285
+ path=result.get("path")
286
+ )
287
+ if write_result.error:
288
+ SYLogger.error(f"[Sandbox] 异步写入失败: {write_result.error}")
289
+ else:
290
+ SYLogger.info(f"[Sandbox] 异步写入成功: {write_result.path}")
291
+ return write_result
292
+ except Exception as e:
293
+ SYLogger.error(f"[Sandbox] 异步写入文件异常: {e}")
294
+ return WriteResult(error=str(e))
193
295
 
194
296
  def edit(
195
297
  self: "HTTPSandboxBackend",
@@ -224,8 +326,24 @@ class FileOperationsMixin:
224
326
  new_string: str,
225
327
  replace_all: bool = False,
226
328
  ) -> EditResult:
227
- """异步编辑文件"""
228
- return await asyncio.to_thread(self.edit, file_path, old_string, new_string, replace_all)
329
+ """异步编辑文件(真正的异步实现)"""
330
+ try:
331
+ await self._ensure_synced_async()
332
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/edit", {
333
+ "file_path": file_path,
334
+ "old_string": base64.b64encode(old_string.encode()).decode(),
335
+ "new_string": base64.b64encode(new_string.encode()).decode(),
336
+ "user_id": self.user_id,
337
+ "replace_all": replace_all
338
+ })
339
+ return EditResult(
340
+ error=result.get("error"),
341
+ path=result.get("path"),
342
+ occurrences=result.get("occurrences")
343
+ )
344
+ except Exception as e:
345
+ SYLogger.error(f"[Sandbox] 异步编辑文件异常: {e}")
346
+ return EditResult(error=str(e))
229
347
 
230
348
  # ============== 搜索 ==============
231
349
 
@@ -245,8 +363,19 @@ class FileOperationsMixin:
245
363
  return GlobResult(error=str(e))
246
364
 
247
365
  async def aglob(self: "HTTPSandboxBackend", pattern: str, path: str = "/") -> GlobResult:
248
- """异步 glob 搜索文件"""
249
- return await asyncio.to_thread(self.glob, pattern, path)
366
+ """异步 glob 搜索文件(真正的异步实现)"""
367
+ try:
368
+ await self._ensure_synced_async()
369
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/glob", {
370
+ "pattern": pattern,
371
+ "path": path,
372
+ "user_id": self.user_id
373
+ })
374
+ matches = [FileInfo(**item) for item in result]
375
+ return GlobResult(matches=matches)
376
+ except Exception as e:
377
+ SYLogger.error(f"[Sandbox] 异步 glob 搜索失败: {e}")
378
+ return GlobResult(error=str(e))
250
379
 
251
380
  def grep(
252
381
  self: "HTTPSandboxBackend",
@@ -277,8 +406,22 @@ class FileOperationsMixin:
277
406
  path: str = None,
278
407
  glob: str = None,
279
408
  ) -> GrepResult:
280
- """异步搜索文件内容"""
281
- return await asyncio.to_thread(self.grep, pattern, path, glob)
409
+ """异步搜索文件内容(真正的异步实现)"""
410
+ try:
411
+ await self._ensure_synced_async()
412
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/grep", {
413
+ "pattern": pattern,
414
+ "user_id": self.user_id,
415
+ "path": path,
416
+ "glob": glob
417
+ })
418
+ if isinstance(result, list):
419
+ matches = [GrepMatch(**item) for item in result]
420
+ return GrepResult(matches=matches)
421
+ return GrepResult(error="Unexpected result format")
422
+ except Exception as e:
423
+ SYLogger.error(f"[Sandbox] 异步 grep 搜索失败: {e}")
424
+ return GrepResult(error=str(e))
282
425
 
283
426
  # ============== 兼容旧版 API(已废弃)==============
284
427
 
@@ -9,10 +9,13 @@ HTTP 客户端沙箱后端
9
9
  import asyncio
10
10
  import base64
11
11
  import os
12
+ import ssl
12
13
  import requests
13
14
  from pathlib import Path
14
15
  from typing import Optional, List
15
16
 
17
+ import aiohttp
18
+
16
19
  from sycommon.logging.kafka_log import SYLogger
17
20
  from sycommon.agent.sandbox.file_ops import FileOperationsMixin, SANDBOX_API_PREFIX
18
21
 
@@ -23,6 +26,13 @@ from deepagents.backends.protocol import (
23
26
  FileUploadResponse,
24
27
  )
25
28
 
29
+ # SSL 上下文
30
+ try:
31
+ import certifi
32
+ _SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
33
+ except ImportError:
34
+ _SSL_CONTEXT = ssl.create_default_context()
35
+
26
36
 
27
37
  class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
28
38
  """
@@ -305,6 +315,58 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
305
315
  """发送同步 POST 请求(带故障转移)"""
306
316
  return self._post_sync_with_failover(endpoint, data, timeout)
307
317
 
318
+ # ============== 内部方法 - 异步版本 ==============
319
+
320
+ async def _post_async(self, endpoint: str, data: dict, timeout: int = None) -> dict:
321
+ """真正的异步 POST 请求(使用 aiohttp)"""
322
+ actual_timeout = timeout or self._timeout
323
+ if "/execute" in endpoint:
324
+ http_timeout = actual_timeout + 60
325
+ else:
326
+ http_timeout = actual_timeout + 30
327
+
328
+ trace_id = SYLogger.get_trace_id()
329
+ headers = {"Content-Type": "application/json"}
330
+ if trace_id:
331
+ headers["x-traceid-header"] = str(trace_id)
332
+
333
+ url = f"{self._base_url}{endpoint}"
334
+ SYLogger.info(f"[Sandbox] Async POST 请求开始: {url}, timeout={http_timeout}s")
335
+
336
+ connector = aiohttp.TCPConnector(ssl=_SSL_CONTEXT)
337
+ timeout_obj = aiohttp.ClientTimeout(total=http_timeout)
338
+
339
+ async with aiohttp.ClientSession(connector=connector, timeout=timeout_obj) as session:
340
+ async with session.post(url, json=data, headers=headers) as resp:
341
+ SYLogger.info(f"[Sandbox] Async POST 响应状态码: {resp.status}")
342
+ if resp.status >= 400:
343
+ text = await resp.text()
344
+ SYLogger.error(f"[Sandbox] HTTP 错误 {resp.status}: {text}")
345
+ resp.raise_for_status()
346
+ result = await resp.json()
347
+ SYLogger.info(f"[Sandbox] Async POST 成功: {url}")
348
+ return result
349
+
350
+ async def _post_async_with_failover(self, endpoint: str, data: dict, timeout: int = None) -> dict:
351
+ """带故障转移的异步 POST 请求"""
352
+ max_retries = 3
353
+
354
+ for attempt in range(max_retries):
355
+ try:
356
+ return await self._post_async(endpoint, data, timeout)
357
+ except (aiohttp.ClientError, asyncio.TimeoutError) as e:
358
+ SYLogger.warning(f"[Sandbox] Async 请求失败 (尝试 {attempt + 1}/{max_retries}): {self._base_url} - {e}")
359
+
360
+ if attempt < max_retries - 1:
361
+ # 尝试故障转移
362
+ switched = await asyncio.to_thread(self._refresh_from_nacos_and_switch_sync)
363
+ if not switched:
364
+ await asyncio.sleep(1)
365
+ else:
366
+ raise RuntimeError(f"沙箱服务不可用: {e}")
367
+
368
+ raise RuntimeError("沙箱服务不可用")
369
+
308
370
  # ============== 属性 ==============
309
371
 
310
372
  @property
@@ -351,8 +413,172 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
351
413
  )
352
414
 
353
415
  async def aexecute(self, command: str, *, timeout: int = None) -> ExecuteResponse:
354
- """异步执行 shell 命令"""
355
- return await asyncio.to_thread(self.execute, command, timeout=timeout)
416
+ """异步执行 shell 命令(真正的异步实现)"""
417
+ SYLogger.info(f"[Sandbox] aexecute 开始: command={command}")
418
+ await self._ensure_synced_async()
419
+ SYLogger.info(f"[Sandbox] _ensure_synced_async 完成,开始执行命令: {command}")
420
+ try:
421
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/execute", {
422
+ "command": command,
423
+ "user_id": self._user_id,
424
+ "timeout": timeout or self._timeout
425
+ })
426
+ response = ExecuteResponse(
427
+ output=result["output"],
428
+ exit_code=result["exit_code"],
429
+ truncated=result.get("truncated", False)
430
+ )
431
+ SYLogger.info(f"[Sandbox] 异步执行完成: exit_code={response.exit_code}")
432
+ return response
433
+ except Exception as e:
434
+ SYLogger.error(f"[Sandbox] 异步执行异常: {e}")
435
+ return ExecuteResponse(
436
+ output=f"Error: {type(e).__name__}: {e}",
437
+ exit_code=1,
438
+ truncated=False
439
+ )
440
+
441
+ async def _ensure_synced_async(self):
442
+ """确保目录已同步(异步版本)"""
443
+ if not self._auto_sync:
444
+ return
445
+
446
+ if self._synced and self._workspace_verified:
447
+ return
448
+
449
+ SYLogger.info(f"[Sandbox] 开始检查工作空间同步状态(异步)...")
450
+
451
+ try:
452
+ workspace_exists = await self._check_workspace_exists_async()
453
+ except Exception as e:
454
+ SYLogger.warning(f"[Sandbox] 检查工作空间异常: {e},跳过检查")
455
+ workspace_exists = True
456
+
457
+ if not workspace_exists:
458
+ SYLogger.info(f"[Sandbox] 工作空间不存在或已被清空,重新同步...")
459
+ self._synced = False
460
+
461
+ if not self._synced:
462
+ SYLogger.info(f"[Sandbox] 开始同步目录(异步)...")
463
+ await self._sync_async()
464
+ self._workspace_verified = True
465
+ SYLogger.info(f"[Sandbox] 同步完成")
466
+
467
+ async def _check_workspace_exists_async(self) -> bool:
468
+ """检查沙箱工作空间是否存在(异步版本)"""
469
+ try:
470
+ await self._post_async(f"{SANDBOX_API_PREFIX}/ls", {
471
+ "path": "/",
472
+ "user_id": self._user_id
473
+ })
474
+ return True
475
+ except Exception as e:
476
+ SYLogger.warning(f"[Sandbox] 检查工作空间失败: {e}")
477
+ return False
478
+
479
+ async def _sync_async(self) -> dict:
480
+ """同步目录(异步版本)"""
481
+ if not self._sync_dirs:
482
+ SYLogger.info("[Sandbox] 没有配置同步目录")
483
+ return {}
484
+
485
+ SYLogger.info(f"[Sandbox] _sync_async 开始, 共 {len(self._sync_dirs)} 个目录")
486
+ results = {}
487
+ for idx, (local_path, remote_path) in enumerate(self._sync_dirs):
488
+ SYLogger.info(f"[Sandbox] 同步目录 [{idx+1}/{len(self._sync_dirs)}]: {local_path} -> {remote_path}")
489
+ local_dir = Path(local_path)
490
+
491
+ if not local_dir.exists():
492
+ SYLogger.error(f"[Sandbox] 本地目录不存在: {local_path}")
493
+ results[local_path] = {"success": 0, "failed": 0, "errors": [{"error": "目录不存在"}]}
494
+ continue
495
+
496
+ if local_dir.is_dir():
497
+ SYLogger.info(f"[Sandbox] 开始上传目录: {local_path}")
498
+ result = await self._upload_directory_async(str(local_dir), remote_path)
499
+ results[local_path] = result
500
+ SYLogger.info(f"[Sandbox] 目录上传完成: {local_path}, 成功={result['success']}, 失败={result['failed']}")
501
+ else:
502
+ # 单文件
503
+ SYLogger.info(f"[Sandbox] 上传单文件: {local_path}")
504
+ content = await asyncio.to_thread(lambda: open(local_dir, "rb").read())
505
+ upload_results = await self.aupload_files([(remote_path, content)])
506
+ if upload_results[0].error:
507
+ results[local_path] = {"success": 0, "failed": 1, "errors": [{"path": remote_path, "error": upload_results[0].error}]}
508
+ else:
509
+ results[local_path] = {"success": 1, "failed": 0, "errors": []}
510
+
511
+ self._synced = True
512
+ SYLogger.info("[Sandbox] _sync_async 完成")
513
+ return results
514
+
515
+ async def _upload_directory_async(self, local_dir: str, remote_path: str = "/") -> dict:
516
+ """上传整个目录到沙箱(异步版本)"""
517
+ local_path = Path(local_dir)
518
+ if not local_path.exists():
519
+ raise FileNotFoundError(f"本地目录不存在: {local_dir}")
520
+
521
+ if not local_path.is_dir():
522
+ raise NotADirectoryError(f"路径不是目录: {local_dir}")
523
+
524
+ SYLogger.info(f"[Sandbox] _upload_directory_async 开始: {local_dir} -> {remote_path}")
525
+
526
+ exclude_patterns = [".git", "__pycache__", ".pyc", ".DS_Store", "node_modules"]
527
+
528
+ def should_exclude(name: str) -> bool:
529
+ for pattern in exclude_patterns:
530
+ if pattern.startswith("*"):
531
+ if name.endswith(pattern[1:]):
532
+ return True
533
+ elif name == pattern:
534
+ return True
535
+ return False
536
+
537
+ files_to_upload = []
538
+
539
+ for root, dirs, files in os.walk(local_path):
540
+ dirs[:] = [d for d in dirs if not should_exclude(d)]
541
+
542
+ for file in files:
543
+ if should_exclude(file):
544
+ continue
545
+
546
+ local_file = Path(root) / file
547
+ relative_path = local_file.relative_to(local_path)
548
+ sandbox_path = str(Path(remote_path) / relative_path)
549
+ files_to_upload.append((sandbox_path, local_file))
550
+
551
+ SYLogger.info(f"[Sandbox] 准备上传 {len(files_to_upload)} 个文件到沙箱")
552
+
553
+ success_count = 0
554
+ failed_count = 0
555
+ errors = []
556
+
557
+ for idx, (sandbox_path, local_file) in enumerate(files_to_upload):
558
+ try:
559
+ if idx % 10 == 0:
560
+ SYLogger.info(f"[Sandbox] 上传进度: {idx}/{len(files_to_upload)}")
561
+
562
+ content = await asyncio.to_thread(lambda f=local_file: open(f, "rb").read())
563
+
564
+ result = await self._post_async(f"{SANDBOX_API_PREFIX}/upload", {
565
+ "path": sandbox_path,
566
+ "content": base64.b64encode(content).decode(),
567
+ "user_id": self._user_id
568
+ })
569
+
570
+ if result.get("error"):
571
+ failed_count += 1
572
+ errors.append({"path": sandbox_path, "error": result["error"]})
573
+ else:
574
+ success_count += 1
575
+
576
+ except Exception as e:
577
+ failed_count += 1
578
+ errors.append({"path": sandbox_path, "error": str(e)})
579
+
580
+ SYLogger.info(f"[Sandbox] _upload_directory_async 完成: 成功={success_count}, 失败={failed_count}")
581
+ return {"success": success_count, "failed": failed_count, "errors": errors}
356
582
 
357
583
  # ============== 后台进程执行 ==============
358
584
 
@@ -396,68 +622,46 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
396
622
  return {"process_id": None, "status": "error", "error": str(e)}
397
623
 
398
624
  async def aexecute_background(self, command: str, *, timeout: int = 600) -> dict:
399
- """异步后台执行 shell 命令"""
400
- return await asyncio.to_thread(self.execute_background, command, timeout=timeout)
401
-
402
- def check_process(self, process_id: str) -> dict:
403
- """检查后台进程状态
404
-
405
- Args:
406
- process_id: execute_background 返回的进程 ID
625
+ """异步后台执行 shell 命令(真正的异步实现)"""
626
+ await self._ensure_synced_async()
627
+ SYLogger.info(f"[Sandbox] 异步后台执行: {command}")
628
+ try:
629
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/execute_background", {
630
+ "command": command,
631
+ "user_id": self._user_id,
632
+ "timeout": timeout
633
+ })
634
+ SYLogger.info(f"[Sandbox] 异步后台进程已启动: process_id={result['process_id']}")
635
+ return result
636
+ except Exception as e:
637
+ SYLogger.error(f"[Sandbox] 异步后台执行失败: {e}")
638
+ return {"process_id": None, "status": "error", "error": str(e)}
407
639
 
408
- Returns:
409
- dict: {
410
- "process_id": str,
411
- "status": "running" | "completed" | "timeout" | "error" | "not_found",
412
- "exit_code": int | None,
413
- "output": str | None, # 仅在进程结束时返回
414
- "truncated": bool,
415
- "command": str | None,
416
- "started_at": str | None
417
- }
418
- """
640
+ async def acheck_process(self, process_id: str) -> dict:
641
+ """异步检查后台进程状态(真正的异步实现)"""
419
642
  try:
420
- result = self._post_sync(f"{SANDBOX_API_PREFIX}/process_status", {
643
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/process_status", {
421
644
  "process_id": process_id,
422
645
  "user_id": self._user_id
423
646
  })
424
647
  return result
425
648
  except Exception as e:
426
- SYLogger.error(f"[Sandbox] 检查进程失败: {e}")
649
+ SYLogger.error(f"[Sandbox] 异步检查进程失败: {e}")
427
650
  return {"process_id": process_id, "status": "error", "error": str(e)}
428
651
 
429
- async def acheck_process(self, process_id: str) -> dict:
430
- """异步检查后台进程状态"""
431
- return await asyncio.to_thread(self.check_process, process_id)
432
-
433
- def kill_process(self, process_id: str) -> dict:
434
- """终止后台进程
435
-
436
- Args:
437
- process_id: execute_background 返回的进程 ID
438
-
439
- Returns:
440
- dict: {
441
- "process_id": str,
442
- "status": "killed" | "not_found" | "already_completed",
443
- "output": str | None # 进程截止到被杀死时的输出
444
- }
445
- """
652
+ async def akill_process(self, process_id: str) -> dict:
653
+ """异步终止后台进程(真正的异步实现)"""
446
654
  try:
447
- result = self._post_sync(f"{SANDBOX_API_PREFIX}/kill_process", {
655
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/kill_process", {
448
656
  "process_id": process_id,
449
657
  "user_id": self._user_id
450
658
  })
451
- SYLogger.info(f"[Sandbox] 进程终止: {process_id}, status={result['status']}")
659
+ SYLogger.info(f"[Sandbox] 异步进程终止: {process_id}, status={result['status']}")
452
660
  return result
453
661
  except Exception as e:
454
- SYLogger.error(f"[Sandbox] 终止进程失败: {e}")
662
+ SYLogger.error(f"[Sandbox] 异步终止进程失败: {e}")
455
663
  return {"process_id": process_id, "status": "error", "error": str(e)}
456
664
 
457
- async def akill_process(self, process_id: str) -> dict:
458
- """异步终止后台进程"""
459
- return await asyncio.to_thread(self.kill_process, process_id)
460
-
461
665
  # ============== 批量操作 ==============
462
666
 
463
667
  def upload_files(self, files: List[tuple[str, bytes]]) -> List[FileUploadResponse]:
@@ -479,8 +683,22 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
479
683
  return results
480
684
 
481
685
  async def aupload_files(self, files: List[tuple[str, bytes]]) -> List[FileUploadResponse]:
482
- """异步上传多个文件"""
483
- return await asyncio.to_thread(self.upload_files, files)
686
+ """异步上传多个文件(真正的异步实现)"""
687
+ results = []
688
+ for path, content in files:
689
+ try:
690
+ result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/upload", {
691
+ "path": path,
692
+ "content": base64.b64encode(content).decode(),
693
+ "user_id": self._user_id
694
+ })
695
+ results.append(FileUploadResponse(
696
+ path=result["path"],
697
+ error=result.get("error")
698
+ ))
699
+ except Exception as e:
700
+ results.append(FileUploadResponse(path=path, error=str(e)))
701
+ return results
484
702
 
485
703
  def download_files(self, paths: List[str]) -> List[FileDownloadResponse]:
486
704
  """下载多个文件"""
@@ -504,8 +722,25 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
504
722
  return results
505
723
 
506
724
  async def adownload_files(self, paths: List[str]) -> List[FileDownloadResponse]:
507
- """异步下载多个文件"""
508
- return await asyncio.to_thread(self.download_files, paths)
725
+ """异步下载多个文件(真正的异步实现)"""
726
+ results = []
727
+ for path in paths:
728
+ try:
729
+ result = await self._post_async_with_failover(
730
+ f"{SANDBOX_API_PREFIX}/download",
731
+ {"path": path, "user_id": self._user_id}
732
+ )
733
+ if result.get("error"):
734
+ results.append(FileDownloadResponse(
735
+ path=path,
736
+ error=result["error"]
737
+ ))
738
+ else:
739
+ content = base64.b64decode(result["content"])
740
+ results.append(FileDownloadResponse(path=path, content=content))
741
+ except Exception as e:
742
+ results.append(FileDownloadResponse(path=path, error=str(e)))
743
+ return results
509
744
 
510
745
  # ============== 目录同步 ==============
511
746
 
@@ -555,8 +790,8 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
555
790
  return self._sync_sync()
556
791
 
557
792
  async def async_sync(self) -> dict:
558
- """异步手动同步所有配置的目录到沙箱"""
559
- return await asyncio.to_thread(self.sync)
793
+ """异步手动同步所有配置的目录到沙箱(真正的异步实现)"""
794
+ return await self._sync_async()
560
795
 
561
796
  def sync_dirs(self, dirs: List[tuple[str, str]]) -> dict:
562
797
  """同步指定的目录列表到沙箱