sycommon-python-lib 0.2.1a28__tar.gz → 0.2.1a29__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.
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/pyproject.toml +1 -1
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/sandbox/http_sandbox_backend.py +104 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/sandbox.py +269 -146
- sycommon_python_lib-0.2.1a29/src/sycommon/models/sandbox.py +201 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon_python_lib.egg-info/SOURCES.txt +1 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/README.md +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/setup.cfg +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/core/console.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/core/models.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/core/project.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/core/utils.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/templates/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/templates/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/templates/base/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/command/templates/web/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/01_basic_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/02_tool_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/03_structured_output.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/04_memory_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/05_streaming.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/06_multi_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/07_skills_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/08_middleware.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/09_interrupt.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/10_custom_llm.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/11_complex_workflow.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/12_batch_processing.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/01_basic_monitoring.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/02_permission_control.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/03_tool_skill_filter.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/04_caching_retry.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/05_sanitization.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/06_tracking.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/07_advanced.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/08_progressive_skills.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/middleware/override_examples.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/examples/virtual_employee_demo.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/exports.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/get_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/sandbox/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/sandbox/file_ops.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/sandbox/sandbox_recovery.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/sandbox/session.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/skills/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/skills/examples/faq_handler/scripts/search.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/skills/exports.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/agent/virtual_employee.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/ElasticsearchConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/LangfuseConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/RedisConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/elasticsearch_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/redis_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/database/token_usage_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/heartbeat_process/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/embedding.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/get_llm.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/llm_tokens.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/output_fixing_runnable.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/struct_token.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/sy_langfuse.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/token_usage_es_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/token_usage_mysql_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/llm/usage_token.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/process_logger.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/traceid.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/token_usage.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/models/token_usage_mysql.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/sentry/sy_sentry.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/services.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/nacos_client_base.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/nacos_config_manager.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/nacos_service.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/nacos_service_registration.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tests/deep_agent_server.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tests/test_deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tests/test_email.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tests/test_mq.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/async_utils.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/env.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/merge_headers.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/syemail.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
- {sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -342,6 +342,110 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
|
|
|
342
342
|
"""异步执行 shell 命令"""
|
|
343
343
|
return await asyncio.to_thread(self.execute, command, timeout=timeout)
|
|
344
344
|
|
|
345
|
+
# ============== 后台进程执行 ==============
|
|
346
|
+
|
|
347
|
+
def execute_background(self, command: str, *, timeout: int = 600) -> dict:
|
|
348
|
+
"""后台执行 shell 命令(不阻塞,立即返回 process_id)
|
|
349
|
+
|
|
350
|
+
适用于长时间运行的服务(如 uvicorn、FastAPI 等)
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
command: 要执行的命令
|
|
354
|
+
timeout: 命令超时时间(秒),默认 600 秒
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
dict: {"process_id": str, "status": "started"}
|
|
358
|
+
|
|
359
|
+
Example:
|
|
360
|
+
# 启动后台服务
|
|
361
|
+
result = backend.execute_background("uvicorn main:app --host 0.0.0.0 --port 8000")
|
|
362
|
+
process_id = result["process_id"]
|
|
363
|
+
|
|
364
|
+
# 稍后检查状态
|
|
365
|
+
status = backend.check_process(process_id)
|
|
366
|
+
if status["status"] == "running":
|
|
367
|
+
print("服务正在运行...")
|
|
368
|
+
|
|
369
|
+
# 需要时终止进程
|
|
370
|
+
backend.kill_process(process_id)
|
|
371
|
+
"""
|
|
372
|
+
self._ensure_synced_sync()
|
|
373
|
+
SYLogger.info(f"[Sandbox] 后台执行: {command}")
|
|
374
|
+
try:
|
|
375
|
+
result = self._post_sync(f"{SANDBOX_API_PREFIX}/execute_background", {
|
|
376
|
+
"command": command,
|
|
377
|
+
"user_id": self._user_id,
|
|
378
|
+
"timeout": timeout
|
|
379
|
+
})
|
|
380
|
+
SYLogger.info(f"[Sandbox] 后台进程已启动: process_id={result['process_id']}")
|
|
381
|
+
return result
|
|
382
|
+
except Exception as e:
|
|
383
|
+
SYLogger.error(f"[Sandbox] 后台执行失败: {e}")
|
|
384
|
+
return {"process_id": None, "status": "error", "error": str(e)}
|
|
385
|
+
|
|
386
|
+
async def aexecute_background(self, command: str, *, timeout: int = 600) -> dict:
|
|
387
|
+
"""异步后台执行 shell 命令"""
|
|
388
|
+
return await asyncio.to_thread(self.execute_background, command, timeout=timeout)
|
|
389
|
+
|
|
390
|
+
def check_process(self, process_id: str) -> dict:
|
|
391
|
+
"""检查后台进程状态
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
process_id: execute_background 返回的进程 ID
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
dict: {
|
|
398
|
+
"process_id": str,
|
|
399
|
+
"status": "running" | "completed" | "timeout" | "error" | "not_found",
|
|
400
|
+
"exit_code": int | None,
|
|
401
|
+
"output": str | None, # 仅在进程结束时返回
|
|
402
|
+
"truncated": bool,
|
|
403
|
+
"command": str | None,
|
|
404
|
+
"started_at": str | None
|
|
405
|
+
}
|
|
406
|
+
"""
|
|
407
|
+
try:
|
|
408
|
+
result = self._post_sync(f"{SANDBOX_API_PREFIX}/process_status", {
|
|
409
|
+
"process_id": process_id,
|
|
410
|
+
"user_id": self._user_id
|
|
411
|
+
})
|
|
412
|
+
return result
|
|
413
|
+
except Exception as e:
|
|
414
|
+
SYLogger.error(f"[Sandbox] 检查进程失败: {e}")
|
|
415
|
+
return {"process_id": process_id, "status": "error", "error": str(e)}
|
|
416
|
+
|
|
417
|
+
async def acheck_process(self, process_id: str) -> dict:
|
|
418
|
+
"""异步检查后台进程状态"""
|
|
419
|
+
return await asyncio.to_thread(self.check_process, process_id)
|
|
420
|
+
|
|
421
|
+
def kill_process(self, process_id: str) -> dict:
|
|
422
|
+
"""终止后台进程
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
process_id: execute_background 返回的进程 ID
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
dict: {
|
|
429
|
+
"process_id": str,
|
|
430
|
+
"status": "killed" | "not_found" | "already_completed",
|
|
431
|
+
"output": str | None # 进程截止到被杀死时的输出
|
|
432
|
+
}
|
|
433
|
+
"""
|
|
434
|
+
try:
|
|
435
|
+
result = self._post_sync(f"{SANDBOX_API_PREFIX}/kill_process", {
|
|
436
|
+
"process_id": process_id,
|
|
437
|
+
"user_id": self._user_id
|
|
438
|
+
})
|
|
439
|
+
SYLogger.info(f"[Sandbox] 进程终止: {process_id}, status={result['status']}")
|
|
440
|
+
return result
|
|
441
|
+
except Exception as e:
|
|
442
|
+
SYLogger.error(f"[Sandbox] 终止进程失败: {e}")
|
|
443
|
+
return {"process_id": process_id, "status": "error", "error": str(e)}
|
|
444
|
+
|
|
445
|
+
async def akill_process(self, process_id: str) -> dict:
|
|
446
|
+
"""异步终止后台进程"""
|
|
447
|
+
return await asyncio.to_thread(self.kill_process, process_id)
|
|
448
|
+
|
|
345
449
|
# ============== 批量操作 ==============
|
|
346
450
|
|
|
347
451
|
def upload_files(self, files: List[tuple[str, bytes]]) -> List[FileUploadResponse]:
|
{sycommon_python_lib-0.2.1a28 → sycommon_python_lib-0.2.1a29}/src/sycommon/middleware/sandbox.py
RENAMED
|
@@ -26,12 +26,34 @@ import base64
|
|
|
26
26
|
import shutil
|
|
27
27
|
import tempfile
|
|
28
28
|
import platform
|
|
29
|
+
import uuid
|
|
30
|
+
import threading
|
|
29
31
|
from datetime import datetime
|
|
30
32
|
from fastapi import FastAPI, APIRouter, Request, Header
|
|
31
|
-
from typing import
|
|
33
|
+
from typing import Dict
|
|
32
34
|
|
|
33
|
-
from pydantic import BaseModel
|
|
34
35
|
from sycommon.logging.kafka_log import SYLogger
|
|
36
|
+
from sycommon.models.sandbox import (
|
|
37
|
+
ExecuteRequest, ExecuteResponse,
|
|
38
|
+
UploadRequest, UploadResponse,
|
|
39
|
+
DownloadRequest, DownloadResponse,
|
|
40
|
+
FileInfo, LsRequest, LsResponse,
|
|
41
|
+
GlobRequest, GrepRequest, GrepMatch,
|
|
42
|
+
ReadRequest, ReadResponse,
|
|
43
|
+
WriteRequest, WriteResponse,
|
|
44
|
+
EditRequest, EditResponse,
|
|
45
|
+
HealthResponse, ResetRequest, ResetResponse,
|
|
46
|
+
ExecuteBackgroundRequest, ExecuteBackgroundResponse,
|
|
47
|
+
ProcessStatusRequest, ProcessStatusResponse,
|
|
48
|
+
KillProcessRequest, KillProcessResponse,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ============== 后台进程管理 ==============
|
|
53
|
+
|
|
54
|
+
# 进程存储: process_id -> {process: Popen, command: str, user_id: str, started_at: datetime, workspace: str}
|
|
55
|
+
_background_processes: Dict[str, dict] = {}
|
|
56
|
+
_process_lock = threading.Lock()
|
|
35
57
|
|
|
36
58
|
|
|
37
59
|
# ============== 常量配置 ==============
|
|
@@ -100,150 +122,6 @@ def resolve_sandbox_path(path: str, workspace: str) -> str:
|
|
|
100
122
|
return resolved
|
|
101
123
|
|
|
102
124
|
|
|
103
|
-
# ============== 请求/响应模型 ==============
|
|
104
|
-
|
|
105
|
-
class ExecuteRequest(BaseModel):
|
|
106
|
-
"""执行命令请求"""
|
|
107
|
-
command: str
|
|
108
|
-
user_id: str # 用户ID,用于隔离工作目录
|
|
109
|
-
timeout: Optional[int] = 120 # 默认超时 120 秒
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class ExecuteResponse(BaseModel):
|
|
113
|
-
"""执行命令响应"""
|
|
114
|
-
output: str
|
|
115
|
-
exit_code: int
|
|
116
|
-
truncated: bool = False
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
class UploadRequest(BaseModel):
|
|
120
|
-
"""上传文件请求"""
|
|
121
|
-
path: str
|
|
122
|
-
content: str # base64 encoded
|
|
123
|
-
user_id: str
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class UploadResponse(BaseModel):
|
|
127
|
-
"""上传文件响应"""
|
|
128
|
-
path: str
|
|
129
|
-
error: Optional[str] = None
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class DownloadRequest(BaseModel):
|
|
133
|
-
"""下载文件请求"""
|
|
134
|
-
path: str
|
|
135
|
-
user_id: str
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class DownloadResponse(BaseModel):
|
|
139
|
-
"""下载文件响应"""
|
|
140
|
-
path: str
|
|
141
|
-
content: Optional[str] = None # base64 encoded
|
|
142
|
-
error: Optional[str] = None
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
class FileInfo(BaseModel):
|
|
146
|
-
"""文件信息"""
|
|
147
|
-
path: str
|
|
148
|
-
is_dir: Optional[bool] = None
|
|
149
|
-
size: Optional[int] = None
|
|
150
|
-
modified_at: Optional[str] = None
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class LsRequest(BaseModel):
|
|
154
|
-
"""列出目录请求"""
|
|
155
|
-
path: str
|
|
156
|
-
user_id: str
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class LsResponse(BaseModel):
|
|
160
|
-
"""列出目录响应"""
|
|
161
|
-
files: list[FileInfo] = []
|
|
162
|
-
error: Optional[str] = None
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
class GlobRequest(BaseModel):
|
|
166
|
-
"""glob 搜索请求"""
|
|
167
|
-
pattern: str
|
|
168
|
-
path: str = "/"
|
|
169
|
-
user_id: str
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
class GrepRequest(BaseModel):
|
|
173
|
-
"""grep 搜索请求"""
|
|
174
|
-
pattern: str
|
|
175
|
-
user_id: str
|
|
176
|
-
path: Optional[str] = None
|
|
177
|
-
glob: Optional[str] = None
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
class GrepMatch(BaseModel):
|
|
181
|
-
"""grep 匹配结果"""
|
|
182
|
-
path: str
|
|
183
|
-
line: int
|
|
184
|
-
text: str
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class ReadRequest(BaseModel):
|
|
188
|
-
"""读取文件请求"""
|
|
189
|
-
file_path: str
|
|
190
|
-
user_id: str
|
|
191
|
-
offset: int = 0
|
|
192
|
-
limit: int = 2000
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
class ReadResponse(BaseModel):
|
|
196
|
-
"""读取文件响应"""
|
|
197
|
-
content: Optional[str] = None
|
|
198
|
-
error: Optional[str] = None
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
class WriteRequest(BaseModel):
|
|
202
|
-
"""写入文件请求"""
|
|
203
|
-
file_path: str
|
|
204
|
-
content: str # base64 encoded
|
|
205
|
-
user_id: str
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
class WriteResponse(BaseModel):
|
|
209
|
-
"""写入文件响应"""
|
|
210
|
-
error: Optional[str] = None
|
|
211
|
-
path: Optional[str] = None
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
class EditRequest(BaseModel):
|
|
215
|
-
"""编辑文件请求"""
|
|
216
|
-
file_path: str
|
|
217
|
-
old_string: str # base64 encoded
|
|
218
|
-
new_string: str # base64 encoded
|
|
219
|
-
user_id: str
|
|
220
|
-
replace_all: bool = False
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
class EditResponse(BaseModel):
|
|
224
|
-
"""编辑文件响应"""
|
|
225
|
-
error: Optional[str] = None
|
|
226
|
-
path: Optional[str] = None
|
|
227
|
-
occurrences: Optional[int] = None
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
class HealthResponse(BaseModel):
|
|
231
|
-
"""健康检查响应"""
|
|
232
|
-
status: str
|
|
233
|
-
workspace: str
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
class ResetRequest(BaseModel):
|
|
237
|
-
"""重置请求"""
|
|
238
|
-
user_id: str
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
class ResetResponse(BaseModel):
|
|
242
|
-
"""重置响应"""
|
|
243
|
-
status: str
|
|
244
|
-
path: str
|
|
245
|
-
|
|
246
|
-
|
|
247
125
|
def setup_sandbox_handler(app: FastAPI, config: dict = None):
|
|
248
126
|
"""
|
|
249
127
|
沙箱服务初始化
|
|
@@ -448,6 +326,251 @@ COMMAND_EOF
|
|
|
448
326
|
truncated=False
|
|
449
327
|
)
|
|
450
328
|
|
|
329
|
+
@sandbox_router.post("/execute_background", response_model=ExecuteBackgroundResponse)
|
|
330
|
+
async def execute_background(req: ExecuteBackgroundRequest):
|
|
331
|
+
"""后台执行 shell 命令(不阻塞,立即返回 process_id)"""
|
|
332
|
+
workspace = get_user_workspace(req.user_id)
|
|
333
|
+
|
|
334
|
+
if not req.command:
|
|
335
|
+
raise ValueError("Command must be a non-empty string.")
|
|
336
|
+
|
|
337
|
+
process_id = str(uuid.uuid4())[:8]
|
|
338
|
+
SYLogger.info(f"[Sandbox Server] 后台执行命令: {req.command} (user={req.user_id}, process_id={process_id})")
|
|
339
|
+
|
|
340
|
+
# 创建常用目录
|
|
341
|
+
for subdir in ["tmp", "output", "skills", "memory", "data"]:
|
|
342
|
+
os.makedirs(os.path.join(workspace, subdir), exist_ok=True)
|
|
343
|
+
|
|
344
|
+
# 使用与 execute 相同的 shell 初始化脚本
|
|
345
|
+
init_script = f'''
|
|
346
|
+
# 沙箱环境初始化
|
|
347
|
+
export SANDBOX_ROOT="{workspace}"
|
|
348
|
+
export _SANDBOX_WORKSPACE="{workspace}"
|
|
349
|
+
|
|
350
|
+
sandbox_path() {{
|
|
351
|
+
local path="$1"
|
|
352
|
+
if [[ "$path" == "$_SANDBOX_WORKSPACE"* ]]; then
|
|
353
|
+
echo "$path"
|
|
354
|
+
return
|
|
355
|
+
fi
|
|
356
|
+
if [[ "$path" == /* ]]; then
|
|
357
|
+
path="${{path#/}}"
|
|
358
|
+
echo "$_SANDBOX_WORKSPACE/$path"
|
|
359
|
+
else
|
|
360
|
+
echo "$path"
|
|
361
|
+
fi
|
|
362
|
+
}}
|
|
363
|
+
|
|
364
|
+
cd() {{
|
|
365
|
+
local target="$1"
|
|
366
|
+
if [[ -z "$target" ]]; then
|
|
367
|
+
command cd "$_SANDBOX_WORKSPACE"
|
|
368
|
+
elif [[ "$target" == "$_SANDBOX_WORKSPACE"* ]]; then
|
|
369
|
+
command cd "$target"
|
|
370
|
+
elif [[ "$target" == /* ]]; then
|
|
371
|
+
local mapped="$_SANDBOX_WORKSPACE/${{target#/}}"
|
|
372
|
+
command cd "$mapped"
|
|
373
|
+
else
|
|
374
|
+
command cd "$target"
|
|
375
|
+
fi
|
|
376
|
+
}}
|
|
377
|
+
|
|
378
|
+
mkdir() {{
|
|
379
|
+
local args=("$@")
|
|
380
|
+
local new_args=()
|
|
381
|
+
for arg in "${{args[@]}}"; do
|
|
382
|
+
if [[ "$arg" == "$_SANDBOX_WORKSPACE"* ]]; then
|
|
383
|
+
new_args+=("$arg")
|
|
384
|
+
elif [[ "$arg" == /* ]]; then
|
|
385
|
+
new_args+=("$_SANDBOX_WORKSPACE/${{arg#/}}")
|
|
386
|
+
else
|
|
387
|
+
new_args+=("$arg")
|
|
388
|
+
fi
|
|
389
|
+
done
|
|
390
|
+
command mkdir "${{new_args[@]}}"
|
|
391
|
+
}}
|
|
392
|
+
|
|
393
|
+
cd "$_SANDBOX_WORKSPACE"
|
|
394
|
+
'''
|
|
395
|
+
|
|
396
|
+
full_command = f'''bash <<'COMMAND_EOF'
|
|
397
|
+
{init_script}
|
|
398
|
+
{req.command}
|
|
399
|
+
COMMAND_EOF
|
|
400
|
+
'''
|
|
401
|
+
|
|
402
|
+
env = os.environ.copy()
|
|
403
|
+
env["TMPDIR"] = os.path.join(workspace, "tmp")
|
|
404
|
+
env["TEMP"] = os.path.join(workspace, "tmp")
|
|
405
|
+
env["TMP"] = os.path.join(workspace, "tmp")
|
|
406
|
+
env["SANDBOX_ROOT"] = workspace
|
|
407
|
+
|
|
408
|
+
try:
|
|
409
|
+
process = subprocess.Popen(
|
|
410
|
+
full_command,
|
|
411
|
+
shell=True,
|
|
412
|
+
stdout=subprocess.PIPE,
|
|
413
|
+
stderr=subprocess.PIPE,
|
|
414
|
+
text=True,
|
|
415
|
+
cwd=workspace,
|
|
416
|
+
env=env
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
with _process_lock:
|
|
420
|
+
_background_processes[process_id] = {
|
|
421
|
+
"process": process,
|
|
422
|
+
"command": req.command,
|
|
423
|
+
"user_id": req.user_id,
|
|
424
|
+
"started_at": datetime.now(),
|
|
425
|
+
"workspace": workspace,
|
|
426
|
+
"timeout": req.timeout,
|
|
427
|
+
"output_buffer": "",
|
|
428
|
+
"completed": False,
|
|
429
|
+
"killed": False
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# 启动后台线程监控进程
|
|
433
|
+
def monitor_process():
|
|
434
|
+
try:
|
|
435
|
+
stdout, stderr = process.communicate(timeout=req.timeout)
|
|
436
|
+
except subprocess.TimeoutExpired:
|
|
437
|
+
process.kill()
|
|
438
|
+
stdout, stderr = process.communicate()
|
|
439
|
+
with _process_lock:
|
|
440
|
+
if process_id in _background_processes:
|
|
441
|
+
_background_processes[process_id]["killed"] = True
|
|
442
|
+
_background_processes[process_id]["output_buffer"] = f"Error: Command timed out after {req.timeout} seconds.\nPartial output:\n{stdout}"
|
|
443
|
+
except Exception as e:
|
|
444
|
+
with _process_lock:
|
|
445
|
+
if process_id in _background_processes:
|
|
446
|
+
_background_processes[process_id]["output_buffer"] = f"Error: {type(e).__name__}: {e}"
|
|
447
|
+
return
|
|
448
|
+
|
|
449
|
+
output_parts = []
|
|
450
|
+
if stdout:
|
|
451
|
+
output_parts.append(stdout)
|
|
452
|
+
if stderr:
|
|
453
|
+
stderr_lines = stderr.strip().split("\n")
|
|
454
|
+
output_parts.extend(f"[stderr] {line}" for line in stderr_lines)
|
|
455
|
+
|
|
456
|
+
output = "\n".join(output_parts) if output_parts else "<no output>"
|
|
457
|
+
|
|
458
|
+
if len(output) > MAX_OUTPUT_BYTES:
|
|
459
|
+
output = output[:MAX_OUTPUT_BYTES] + f"\n\n... Output truncated at {MAX_OUTPUT_BYTES} bytes."
|
|
460
|
+
|
|
461
|
+
exit_code = process.returncode
|
|
462
|
+
if exit_code != 0:
|
|
463
|
+
output = f"{output.rstrip()}\n\nExit code: {exit_code}"
|
|
464
|
+
|
|
465
|
+
with _process_lock:
|
|
466
|
+
if process_id in _background_processes:
|
|
467
|
+
_background_processes[process_id]["output_buffer"] = output
|
|
468
|
+
_background_processes[process_id]["exit_code"] = exit_code
|
|
469
|
+
_background_processes[process_id]["completed"] = True
|
|
470
|
+
|
|
471
|
+
monitor_thread = threading.Thread(target=monitor_process, daemon=True)
|
|
472
|
+
monitor_thread.start()
|
|
473
|
+
|
|
474
|
+
return ExecuteBackgroundResponse(process_id=process_id, status="started")
|
|
475
|
+
|
|
476
|
+
except Exception as e:
|
|
477
|
+
SYLogger.error(f"[Sandbox Server] 后台执行异常: {e}")
|
|
478
|
+
raise RuntimeError(f"Failed to start background process: {e}")
|
|
479
|
+
|
|
480
|
+
@sandbox_router.post("/process_status", response_model=ProcessStatusResponse)
|
|
481
|
+
async def get_process_status(req: ProcessStatusRequest):
|
|
482
|
+
"""查询后台进程状态"""
|
|
483
|
+
with _process_lock:
|
|
484
|
+
proc_info = _background_processes.get(req.process_id)
|
|
485
|
+
|
|
486
|
+
if not proc_info:
|
|
487
|
+
return ProcessStatusResponse(
|
|
488
|
+
process_id=req.process_id,
|
|
489
|
+
status="not_found"
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# 检查用户权限
|
|
493
|
+
if proc_info["user_id"] != req.user_id:
|
|
494
|
+
return ProcessStatusResponse(
|
|
495
|
+
process_id=req.process_id,
|
|
496
|
+
status="not_found"
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
process = proc_info["process"]
|
|
500
|
+
|
|
501
|
+
# 判断进程状态
|
|
502
|
+
if proc_info["killed"]:
|
|
503
|
+
status = "timeout"
|
|
504
|
+
elif proc_info["completed"]:
|
|
505
|
+
status = "completed"
|
|
506
|
+
elif process.poll() is not None:
|
|
507
|
+
# 进程已结束但还没被监控线程处理(极少情况)
|
|
508
|
+
status = "completed"
|
|
509
|
+
else:
|
|
510
|
+
status = "running"
|
|
511
|
+
|
|
512
|
+
return ProcessStatusResponse(
|
|
513
|
+
process_id=req.process_id,
|
|
514
|
+
status=status,
|
|
515
|
+
exit_code=proc_info.get("exit_code"),
|
|
516
|
+
output=proc_info.get("output_buffer") if proc_info["completed"] or proc_info["killed"] else None,
|
|
517
|
+
truncated=len(proc_info.get("output_buffer", "")) >= MAX_OUTPUT_BYTES if proc_info.get("output_buffer") else False,
|
|
518
|
+
command=proc_info["command"],
|
|
519
|
+
started_at=proc_info["started_at"].isoformat()
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
@sandbox_router.post("/kill_process", response_model=KillProcessResponse)
|
|
523
|
+
async def kill_process(req: KillProcessRequest):
|
|
524
|
+
"""终止后台进程"""
|
|
525
|
+
with _process_lock:
|
|
526
|
+
proc_info = _background_processes.get(req.process_id)
|
|
527
|
+
|
|
528
|
+
if not proc_info:
|
|
529
|
+
return KillProcessResponse(
|
|
530
|
+
process_id=req.process_id,
|
|
531
|
+
status="not_found"
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
# 检查用户权限
|
|
535
|
+
if proc_info["user_id"] != req.user_id:
|
|
536
|
+
return KillProcessResponse(
|
|
537
|
+
process_id=req.process_id,
|
|
538
|
+
status="not_found"
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
if proc_info["completed"] or proc_info["killed"]:
|
|
542
|
+
return KillProcessResponse(
|
|
543
|
+
process_id=req.process_id,
|
|
544
|
+
status="already_completed",
|
|
545
|
+
output=proc_info.get("output_buffer")
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
process = proc_info["process"]
|
|
549
|
+
try:
|
|
550
|
+
process.kill()
|
|
551
|
+
# 等待进程结束
|
|
552
|
+
process.wait(timeout=5)
|
|
553
|
+
except Exception as e:
|
|
554
|
+
SYLogger.warning(f"[Sandbox Server] 终止进程时出错: {e}")
|
|
555
|
+
|
|
556
|
+
# 获取当前输出(如果有)
|
|
557
|
+
current_output = proc_info.get("output_buffer", "")
|
|
558
|
+
proc_info["killed"] = True
|
|
559
|
+
proc_info["completed"] = True
|
|
560
|
+
proc_info["exit_code"] = process.returncode if process.returncode is not None else -9
|
|
561
|
+
|
|
562
|
+
if not current_output:
|
|
563
|
+
current_output = f"Process killed by user request. Exit code: {proc_info['exit_code']}"
|
|
564
|
+
proc_info["output_buffer"] = current_output
|
|
565
|
+
|
|
566
|
+
SYLogger.info(f"[Sandbox Server] 进程已终止: {req.process_id}")
|
|
567
|
+
|
|
568
|
+
return KillProcessResponse(
|
|
569
|
+
process_id=req.process_id,
|
|
570
|
+
status="killed",
|
|
571
|
+
output=current_output
|
|
572
|
+
)
|
|
573
|
+
|
|
451
574
|
@sandbox_router.post("/ls", response_model=list[FileInfo])
|
|
452
575
|
async def ls_info(req: LsRequest):
|
|
453
576
|
"""列出目录内容"""
|