sycommon-python-lib 0.2.1a19__tar.gz → 0.2.1a21__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.1a19 → sycommon_python_lib-0.2.1a21}/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/pyproject.toml +1 -1
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/__init__.py +4 -0
- sycommon_python_lib-0.2.1a21/src/sycommon/agent/sandbox/sandbox_recovery.py +197 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/token_usage_mysql_service.py +52 -278
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/SOURCES.txt +1 -1
- sycommon_python_lib-0.2.1a19/src/sycommon/llm/token_usage_buffer.py +0 -446
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/README.md +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/setup.cfg +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/console.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/models.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/project.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/utils.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/base/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/web/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/01_basic_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/02_tool_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/03_structured_output.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/04_memory_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/05_streaming.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/06_multi_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/07_skills_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/08_middleware.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/09_interrupt.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/10_custom_llm.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/11_complex_workflow.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/12_batch_processing.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/01_basic_monitoring.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/02_permission_control.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/03_tool_skill_filter.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/04_caching_retry.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/05_sanitization.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/06_tracking.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/07_advanced.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/08_progressive_skills.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/override_examples.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/virtual_employee_demo.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/exports.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/get_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/file_ops.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/http_sandbox_backend.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/session.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/skills/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/skills/examples/faq_handler/scripts/search.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/skills/exports.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/virtual_employee.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/ElasticsearchConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/LangfuseConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/RedisConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/elasticsearch_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/redis_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/token_usage_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/embedding.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/get_llm.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/llm_tokens.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/output_fixing_runnable.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/struct_token.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/sy_langfuse.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/token_usage_es_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/usage_token.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/process_logger.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/sandbox.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/traceid.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/token_usage.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/token_usage_mysql.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sentry/sy_sentry.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/services.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_client_base.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_config_manager.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_service.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_service_registration.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/deep_agent_server.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/test_deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/test_email.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/test_mq.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/async_utils.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/env.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/merge_headers.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/syemail.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
- {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
{sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/__init__.py
RENAMED
|
@@ -76,6 +76,7 @@ from sycommon.agent.sandbox.sandbox_pool import (
|
|
|
76
76
|
areset_all_sandboxes,
|
|
77
77
|
aget_sandbox_status,
|
|
78
78
|
)
|
|
79
|
+
from sycommon.agent.sandbox.sandbox_recovery import SandboxRecoveryManager
|
|
79
80
|
|
|
80
81
|
__all__ = [
|
|
81
82
|
# 后端
|
|
@@ -85,6 +86,9 @@ __all__ = [
|
|
|
85
86
|
"SandboxPool",
|
|
86
87
|
"SandboxInstance",
|
|
87
88
|
|
|
89
|
+
# 恢复管理
|
|
90
|
+
"SandboxRecoveryManager",
|
|
91
|
+
|
|
88
92
|
# 便捷函数
|
|
89
93
|
"areset_all_sandboxes",
|
|
90
94
|
"aget_sandbox_status",
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
沙箱恢复管理器
|
|
3
|
+
|
|
4
|
+
处理沙箱不可用时的自动切换和恢复逻辑
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
11
|
+
from sycommon.synacos.nacos_service import NacosService
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SandboxRecoveryManager:
|
|
15
|
+
"""沙箱恢复管理器 - 处理沙箱不可用时的自动切换
|
|
16
|
+
|
|
17
|
+
使用示例:
|
|
18
|
+
from sycommon.agent.sandbox import HTTPSandboxBackend, SandboxRecoveryManager
|
|
19
|
+
|
|
20
|
+
sandbox = HTTPSandboxBackend.from_nacos("sandbox-service", user_id="user123")
|
|
21
|
+
recovery = SandboxRecoveryManager(sandbox)
|
|
22
|
+
|
|
23
|
+
# 尝试恢复
|
|
24
|
+
if await recovery.recover():
|
|
25
|
+
print("沙箱已恢复")
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
sandbox_backend: "HTTPSandboxBackend",
|
|
31
|
+
check_interval: int = 15,
|
|
32
|
+
max_failures: int = 3,
|
|
33
|
+
health_check_timeout: int = 15,
|
|
34
|
+
sync_timeout: int = 300,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
初始化恢复管理器
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
sandbox_backend: 沙箱后端实例
|
|
41
|
+
check_interval: 健康检查间隔(秒),默认15秒
|
|
42
|
+
max_failures: 触发切换的连续失败次数,默认3次(连续3次失败才触发切换)
|
|
43
|
+
health_check_timeout: 单次健康检查超时(秒),默认15秒
|
|
44
|
+
sync_timeout: 目录同步超时(秒),默认300秒(5分钟)
|
|
45
|
+
"""
|
|
46
|
+
self.backend = sandbox_backend
|
|
47
|
+
self._check_interval = check_interval
|
|
48
|
+
self._max_failures = max_failures
|
|
49
|
+
self._health_check_timeout = health_check_timeout
|
|
50
|
+
self._sync_timeout = sync_timeout
|
|
51
|
+
self._consecutive_failures = 0
|
|
52
|
+
|
|
53
|
+
async def check_health(self) -> bool:
|
|
54
|
+
"""检查沙箱是否健康(单次检查)
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
bool: 健康返回 True,否则返回 False
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
result = await asyncio.wait_for(
|
|
61
|
+
self.backend.aexecute("echo 'health_check'", timeout=10),
|
|
62
|
+
timeout=self._health_check_timeout
|
|
63
|
+
)
|
|
64
|
+
is_healthy = result.exit_code == 0
|
|
65
|
+
if is_healthy:
|
|
66
|
+
# 一次成功,立马重置失败计数,沙箱健康
|
|
67
|
+
self._consecutive_failures = 0
|
|
68
|
+
return is_healthy
|
|
69
|
+
except Exception as e:
|
|
70
|
+
SYLogger.debug(f"[Sandbox] 健康检查异常: {e}")
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
async def recover(self) -> bool:
|
|
74
|
+
"""尝试恢复:执行健康检查,连续失败达阈值时切换沙箱
|
|
75
|
+
|
|
76
|
+
逻辑:
|
|
77
|
+
- 一次健康检查成功 → 立马返回 True(沙箱健康)
|
|
78
|
+
- 连续失败 < max_failures → 返回 False(继续等待检查)
|
|
79
|
+
- 连续失败 >= max_failures → 触发切换
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
bool: 恢复成功/沙箱健康返回 True,否则返回 False
|
|
83
|
+
"""
|
|
84
|
+
# 健康检查(成功会自动重置失败计数)
|
|
85
|
+
if await self.check_health():
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
# 检查失败,累计失败次数
|
|
89
|
+
self._consecutive_failures += 1
|
|
90
|
+
SYLogger.warning(
|
|
91
|
+
f"[Sandbox] 健康检查失败 "
|
|
92
|
+
f"({self._consecutive_failures}/{self._max_failures})"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if self._consecutive_failures < self._max_failures:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
# 连续失败达到阈值,切换沙箱
|
|
99
|
+
SYLogger.info("[Sandbox] 连续失败达到阈值,尝试切换沙箱...")
|
|
100
|
+
return await self._switch_to_healthy_sandbox()
|
|
101
|
+
|
|
102
|
+
async def wait_and_recover(self) -> bool:
|
|
103
|
+
"""等待检查间隔后尝试恢复
|
|
104
|
+
|
|
105
|
+
每等待 check_interval 秒后执行一次 recover()
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
bool: 恢复成功返回 True,否则返回 False
|
|
109
|
+
"""
|
|
110
|
+
await asyncio.sleep(self._check_interval)
|
|
111
|
+
return await self.recover()
|
|
112
|
+
|
|
113
|
+
async def _switch_to_healthy_sandbox(self) -> bool:
|
|
114
|
+
"""切换到健康的沙箱实例
|
|
115
|
+
|
|
116
|
+
优先使用底层已有的切换逻辑(会尝试迁移工作空间文件),
|
|
117
|
+
如果失败则手动从 Nacos 获取新实例并同步。
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
bool: 切换成功返回 True,否则返回 False
|
|
121
|
+
"""
|
|
122
|
+
# 优先使用底层已有的切换逻辑(会尝试迁移工作空间文件)
|
|
123
|
+
switched = await self.backend._refresh_from_nacos_and_switch()
|
|
124
|
+
|
|
125
|
+
if switched:
|
|
126
|
+
self._consecutive_failures = 0
|
|
127
|
+
SYLogger.info(f"[Sandbox] 已切换到新沙箱: {self.backend.base_url}")
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
# 底层切换失败,手动切换并同步原始目录
|
|
131
|
+
return await self._manual_switch()
|
|
132
|
+
|
|
133
|
+
async def _manual_switch(self) -> bool:
|
|
134
|
+
"""手动切换:重新从 Nacos 获取并同步原始目录
|
|
135
|
+
|
|
136
|
+
当底层 _refresh_from_nacos_and_switch 失败时(如旧沙箱已完全不可用),
|
|
137
|
+
手动从 Nacos 获取新实例,并同步用户原始的 sync_dirs 目录。
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
bool: 切换成功返回 True,否则返回 False
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
nacos_manager = NacosService(None)
|
|
144
|
+
|
|
145
|
+
if not hasattr(nacos_manager, 'discovery'):
|
|
146
|
+
SYLogger.error("[Sandbox] NacosService 未初始化")
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
instances = nacos_manager.get_service_instances(
|
|
150
|
+
self.backend._nacos_service_name,
|
|
151
|
+
group=self.backend._nacos_group
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if not instances:
|
|
155
|
+
SYLogger.error(f"[Sandbox] Nacos 无可用实例: {self.backend._nacos_service_name}")
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
# 找一个不同的实例
|
|
159
|
+
for instance in instances:
|
|
160
|
+
new_url = f"http://{instance['ip']}:{instance['port']}"
|
|
161
|
+
if new_url != self.backend.base_url:
|
|
162
|
+
old_url = self.backend.base_url
|
|
163
|
+
SYLogger.info(f"[Sandbox] 手动切换: {old_url} -> {new_url}")
|
|
164
|
+
|
|
165
|
+
# 切换 URL
|
|
166
|
+
self.backend._base_url = new_url
|
|
167
|
+
self.backend._synced = False
|
|
168
|
+
|
|
169
|
+
# 同步用户原始目录
|
|
170
|
+
if self.backend._sync_dirs:
|
|
171
|
+
SYLogger.info("[Sandbox] 同步原始目录到新沙箱...")
|
|
172
|
+
try:
|
|
173
|
+
await asyncio.wait_for(
|
|
174
|
+
self.backend.async_sync(),
|
|
175
|
+
timeout=self._sync_timeout
|
|
176
|
+
)
|
|
177
|
+
except asyncio.TimeoutError:
|
|
178
|
+
SYLogger.warning(f"[Sandbox] 同步超时 ({self._sync_timeout}s),继续执行")
|
|
179
|
+
|
|
180
|
+
self._consecutive_failures = 0
|
|
181
|
+
SYLogger.info("[Sandbox] 手动切换完成")
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
SYLogger.warning("[Sandbox] 无其他可用实例")
|
|
185
|
+
return False
|
|
186
|
+
except Exception as e:
|
|
187
|
+
SYLogger.error(f"[Sandbox] 手动切换失败: {e}")
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def consecutive_failures(self) -> int:
|
|
192
|
+
"""连续失败次数"""
|
|
193
|
+
return self._consecutive_failures
|
|
194
|
+
|
|
195
|
+
def reset_failures(self):
|
|
196
|
+
"""重置失败计数"""
|
|
197
|
+
self._consecutive_failures = 0
|
|
@@ -3,34 +3,22 @@ Token 使用量记录服务(MySQL版本)
|
|
|
3
3
|
按用户+系统+日期维度聚合存储,每个人每天每个系统只保留一条记录
|
|
4
4
|
|
|
5
5
|
使用独立的数据库连接(llm 配置中的数据库),与业务系统数据库隔离
|
|
6
|
-
|
|
7
|
-
高并发优化:
|
|
8
|
-
1. 本地内存缓冲:减少数据库写入频率
|
|
9
|
-
2. 智能聚合:相同 key 的请求在内存中累加
|
|
10
|
-
3. 批量写入:合并多次写入为单次批量操作
|
|
11
|
-
4. 异步刷新:定时将缓冲区数据写入数据库
|
|
12
6
|
"""
|
|
13
7
|
import asyncio
|
|
14
8
|
import logging
|
|
15
|
-
import random
|
|
16
9
|
from datetime import datetime, date
|
|
17
10
|
from typing import Optional, Dict, Any, List
|
|
18
11
|
|
|
19
12
|
from sqlalchemy import select, text
|
|
20
13
|
from sqlalchemy.dialects.mysql import insert
|
|
21
|
-
from sqlalchemy.exc import OperationalError
|
|
22
14
|
|
|
23
15
|
from sycommon.config.Config import SingletonMeta, Config
|
|
24
16
|
from sycommon.logging.kafka_log import SYLogger
|
|
25
17
|
from sycommon.database.token_usage_db_service import TokenUsageDBService
|
|
26
18
|
from sycommon.models.token_usage_mysql import TokenUsageMySQL
|
|
27
|
-
from sycommon.llm.token_usage_buffer import TokenUsageBuffer, TokenUsageBufferItem
|
|
28
19
|
|
|
29
20
|
logger = SYLogger
|
|
30
21
|
|
|
31
|
-
# MySQL lock wait timeout error code
|
|
32
|
-
MYSQL_LOCK_WAIT_TIMEOUT = 1205
|
|
33
|
-
|
|
34
22
|
|
|
35
23
|
class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
36
24
|
"""Token 使用量记录服务(MySQL异步版本)
|
|
@@ -45,16 +33,6 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
45
33
|
_service_name: Optional[str] = None
|
|
46
34
|
_system_env: Optional[str] = None
|
|
47
35
|
|
|
48
|
-
# Retry configuration for lock wait timeout
|
|
49
|
-
_max_retries: int = 3
|
|
50
|
-
_base_delay: float = 0.5 # Base delay in seconds
|
|
51
|
-
_max_delay: float = 5.0 # Maximum delay in seconds
|
|
52
|
-
|
|
53
|
-
# Buffer configuration (for high concurrency optimization)
|
|
54
|
-
_use_buffer: bool = True # 是否启用缓冲(默认启用)
|
|
55
|
-
_buffer_flush_interval: float = 15.0 # 缓冲刷新间隔(秒)
|
|
56
|
-
_buffer_max_size: int = 500 # 缓冲区最大条目数
|
|
57
|
-
|
|
58
36
|
def __init__(self):
|
|
59
37
|
pass
|
|
60
38
|
|
|
@@ -104,81 +82,11 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
104
82
|
asyncio.create_task(cls._ensure_table())
|
|
105
83
|
cls._initialized = True
|
|
106
84
|
logging.info("TokenUsageMySQLService 初始化成功")
|
|
107
|
-
|
|
108
|
-
# 初始化缓冲区(高并发优化)
|
|
109
|
-
if cls._use_buffer:
|
|
110
|
-
cls._init_buffer()
|
|
111
85
|
else:
|
|
112
86
|
logging.warning("TokenUsageMySQLService 初始化失败: 数据库服务未就绪")
|
|
113
87
|
except Exception as e:
|
|
114
88
|
logging.warning(f"TokenUsageMySQLService 初始化失败: {e}")
|
|
115
89
|
|
|
116
|
-
@classmethod
|
|
117
|
-
def _init_buffer(cls):
|
|
118
|
-
"""初始化缓冲区"""
|
|
119
|
-
TokenUsageBuffer.configure(
|
|
120
|
-
flush_interval=cls._buffer_flush_interval,
|
|
121
|
-
max_buffer_size=cls._buffer_max_size,
|
|
122
|
-
max_batch_size=100
|
|
123
|
-
)
|
|
124
|
-
TokenUsageBuffer.set_flush_callback(cls._flush_buffer_to_db)
|
|
125
|
-
|
|
126
|
-
# 启动缓冲区(异步)
|
|
127
|
-
try:
|
|
128
|
-
loop = asyncio.get_running_loop()
|
|
129
|
-
loop.create_task(TokenUsageBuffer.start())
|
|
130
|
-
logging.info("TokenUsageMySQLService 缓冲区已启动")
|
|
131
|
-
except RuntimeError:
|
|
132
|
-
# 没有运行中的事件循环,延迟启动
|
|
133
|
-
logging.info("TokenUsageMySQLService 缓冲区将在首次使用时启动")
|
|
134
|
-
|
|
135
|
-
@classmethod
|
|
136
|
-
async def _flush_buffer_to_db(cls, items: List[TokenUsageBufferItem]):
|
|
137
|
-
"""
|
|
138
|
-
将缓冲区数据批量写入数据库
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
items: 缓冲区中的记录列表
|
|
142
|
-
"""
|
|
143
|
-
if not items or not cls._db_service:
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
# 确保表存在
|
|
148
|
-
await cls._ensure_table()
|
|
149
|
-
|
|
150
|
-
async with cls._db_service.session() as session:
|
|
151
|
-
# 构建批量 upsert 语句
|
|
152
|
-
values = []
|
|
153
|
-
for item in items:
|
|
154
|
-
values.append({
|
|
155
|
-
'user_id': item.user_id,
|
|
156
|
-
'tenant_id': item.tenant_id,
|
|
157
|
-
'service_name': item.service_name,
|
|
158
|
-
'system_env': item.system_env,
|
|
159
|
-
'input_tokens': item.input_tokens,
|
|
160
|
-
'output_tokens': item.output_tokens,
|
|
161
|
-
'total_tokens': item.input_tokens + item.output_tokens,
|
|
162
|
-
'model': item.model,
|
|
163
|
-
'usage_date': item.usage_date
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
if values:
|
|
167
|
-
stmt = insert(TokenUsageMySQL).values(values)
|
|
168
|
-
stmt = stmt.on_duplicate_key_update(
|
|
169
|
-
input_tokens=TokenUsageMySQL.input_tokens + insert(TokenUsageMySQL).input_tokens,
|
|
170
|
-
output_tokens=TokenUsageMySQL.output_tokens + insert(TokenUsageMySQL).output_tokens,
|
|
171
|
-
total_tokens=TokenUsageMySQL.total_tokens + insert(TokenUsageMySQL).total_tokens,
|
|
172
|
-
model=insert(TokenUsageMySQL).model,
|
|
173
|
-
updated_at=datetime.now()
|
|
174
|
-
)
|
|
175
|
-
await session.execute(stmt)
|
|
176
|
-
logging.info(f"TokenUsageMySQLService 批量写入 {len(items)} 条记录成功")
|
|
177
|
-
|
|
178
|
-
except Exception as e:
|
|
179
|
-
logging.error(f"TokenUsageMySQLService 批量写入失败: {e}", exc_info=True)
|
|
180
|
-
raise
|
|
181
|
-
|
|
182
90
|
@classmethod
|
|
183
91
|
async def _ensure_table(cls):
|
|
184
92
|
"""确保表存在"""
|
|
@@ -231,10 +139,7 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
231
139
|
system_env: Optional[str] = None
|
|
232
140
|
) -> bool:
|
|
233
141
|
"""
|
|
234
|
-
同步记录 Token 使用量(Fire-and-forget
|
|
235
|
-
|
|
236
|
-
高并发优化:使用本地缓冲区,定时批量写入数据库
|
|
237
|
-
完全不阻塞主服务,使用内存缓冲 + 后台异步刷新
|
|
142
|
+
同步记录 Token 使用量(Fire-and-forget 模式)
|
|
238
143
|
|
|
239
144
|
Args:
|
|
240
145
|
user_id: 用户ID(从请求头获取,没有则为空)
|
|
@@ -265,38 +170,32 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
265
170
|
if system_env is None:
|
|
266
171
|
system_env = cls._system_env
|
|
267
172
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
# 确保缓冲区已启动(异步启动,不阻塞)
|
|
271
|
-
cls._ensure_buffer_started()
|
|
272
|
-
# 使用同步添加方法,完全非阻塞
|
|
273
|
-
return TokenUsageBuffer.add_sync(
|
|
274
|
-
user_id=user_id,
|
|
275
|
-
input_tokens=input_tokens,
|
|
276
|
-
output_tokens=output_tokens,
|
|
277
|
-
model=model,
|
|
278
|
-
tenant_id=tenant_id,
|
|
279
|
-
service_name=service_name,
|
|
280
|
-
system_env=system_env
|
|
281
|
-
)
|
|
173
|
+
logging.info(
|
|
174
|
+
f"TokenUsageMySQLService record_sync: service_name={service_name}, system_env={system_env}, cls._service_name={cls._service_name}, cls._system_env={cls._system_env}")
|
|
282
175
|
|
|
283
|
-
# 缓冲区未启用,异步写入数据库(非阻塞)
|
|
284
176
|
try:
|
|
285
|
-
loop = asyncio.get_running_loop()
|
|
286
|
-
task = loop.create_task(cls._do_record(
|
|
287
|
-
user_id=user_id,
|
|
288
|
-
input_tokens=input_tokens,
|
|
289
|
-
output_tokens=output_tokens,
|
|
290
|
-
model=model,
|
|
291
|
-
tenant_id=tenant_id,
|
|
292
|
-
service_name=service_name,
|
|
293
|
-
system_env=system_env
|
|
294
|
-
))
|
|
295
|
-
task.add_done_callback(cls._handle_task_exception)
|
|
296
|
-
return True
|
|
297
|
-
except RuntimeError:
|
|
298
|
-
# 没有运行中的事件循环,使用 asyncio.run(会阻塞,但这是边缘情况)
|
|
299
177
|
try:
|
|
178
|
+
loop = asyncio.get_running_loop()
|
|
179
|
+
# 创建任务并添加回调处理异常,避免静默失败
|
|
180
|
+
task = loop.create_task(cls._do_record(
|
|
181
|
+
user_id=user_id,
|
|
182
|
+
input_tokens=input_tokens,
|
|
183
|
+
output_tokens=output_tokens,
|
|
184
|
+
model=model,
|
|
185
|
+
tenant_id=tenant_id,
|
|
186
|
+
service_name=service_name,
|
|
187
|
+
system_env=system_env
|
|
188
|
+
))
|
|
189
|
+
# 添加回调处理异常
|
|
190
|
+
|
|
191
|
+
def _handle_task_exception(t):
|
|
192
|
+
try:
|
|
193
|
+
t.result()
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logging.error(f"Token 记录任务失败: {e}", exc_info=True)
|
|
196
|
+
task.add_done_callback(_handle_task_exception)
|
|
197
|
+
except RuntimeError:
|
|
198
|
+
# 没有运行中的事件循环,使用 asyncio.run
|
|
300
199
|
asyncio.run(cls._do_record(
|
|
301
200
|
user_id=user_id,
|
|
302
201
|
input_tokens=input_tokens,
|
|
@@ -306,30 +205,10 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
306
205
|
service_name=service_name,
|
|
307
206
|
system_env=system_env
|
|
308
207
|
))
|
|
309
|
-
|
|
310
|
-
except Exception as e:
|
|
311
|
-
logging.error(f"记录 Token 使用量失败: {e}", exc_info=True)
|
|
312
|
-
return False
|
|
313
|
-
|
|
314
|
-
@classmethod
|
|
315
|
-
def _ensure_buffer_started(cls):
|
|
316
|
-
"""确保缓冲区已启动(非阻塞)"""
|
|
317
|
-
if TokenUsageBuffer.is_running():
|
|
318
|
-
return
|
|
319
|
-
|
|
320
|
-
try:
|
|
321
|
-
loop = asyncio.get_running_loop()
|
|
322
|
-
loop.create_task(TokenUsageBuffer.start())
|
|
323
|
-
except RuntimeError:
|
|
324
|
-
pass
|
|
325
|
-
|
|
326
|
-
@classmethod
|
|
327
|
-
def _handle_task_exception(cls, task):
|
|
328
|
-
"""处理异步任务异常"""
|
|
329
|
-
try:
|
|
330
|
-
task.result()
|
|
208
|
+
return True
|
|
331
209
|
except Exception as e:
|
|
332
|
-
logging.error(f"Token
|
|
210
|
+
logging.error(f"记录 Token 使用量失败: {e}", exc_info=True)
|
|
211
|
+
return False
|
|
333
212
|
|
|
334
213
|
@classmethod
|
|
335
214
|
async def record(
|
|
@@ -346,8 +225,6 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
346
225
|
"""
|
|
347
226
|
异步记录 Token 使用量
|
|
348
227
|
|
|
349
|
-
高并发优化:优先使用本地缓冲区,定时批量写入数据库
|
|
350
|
-
|
|
351
228
|
每个用户每个服务每天只保留一条记录,如果存在则累加更新
|
|
352
229
|
|
|
353
230
|
Args:
|
|
@@ -378,23 +255,6 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
378
255
|
if system_env is None:
|
|
379
256
|
system_env = cls._system_env
|
|
380
257
|
|
|
381
|
-
# 优先使用缓冲区(高并发优化,非阻塞)
|
|
382
|
-
if cls._use_buffer:
|
|
383
|
-
# 确保缓冲区已启动
|
|
384
|
-
if not TokenUsageBuffer.is_running():
|
|
385
|
-
await TokenUsageBuffer.start()
|
|
386
|
-
# 使用同步添加方法(非阻塞)
|
|
387
|
-
return TokenUsageBuffer.add_sync(
|
|
388
|
-
user_id=user_id,
|
|
389
|
-
input_tokens=input_tokens,
|
|
390
|
-
output_tokens=output_tokens,
|
|
391
|
-
model=model,
|
|
392
|
-
tenant_id=tenant_id,
|
|
393
|
-
service_name=service_name,
|
|
394
|
-
system_env=system_env
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
# 缓冲区未启用,直接写入数据库
|
|
398
258
|
return await cls._do_record(
|
|
399
259
|
user_id=user_id,
|
|
400
260
|
input_tokens=input_tokens,
|
|
@@ -418,7 +278,6 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
418
278
|
) -> bool:
|
|
419
279
|
"""
|
|
420
280
|
执行记录操作(使用 MySQL INSERT ON DUPLICATE KEY UPDATE 实现 upsert)
|
|
421
|
-
包含重试机制处理锁等待超时
|
|
422
281
|
"""
|
|
423
282
|
if not cls._db_service:
|
|
424
283
|
return False
|
|
@@ -447,69 +306,31 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
447
306
|
service_name = service_name or ''
|
|
448
307
|
system_env = system_env or ''
|
|
449
308
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
await session.execute(stmt)
|
|
478
|
-
return True
|
|
479
|
-
|
|
480
|
-
except OperationalError as e:
|
|
481
|
-
last_exception = e
|
|
482
|
-
# Check if it's a lock wait timeout error
|
|
483
|
-
if hasattr(e, 'orig') and hasattr(e.orig, 'args'):
|
|
484
|
-
error_code = e.orig.args[0] if e.orig.args else None
|
|
485
|
-
elif hasattr(e, 'args') and e.args:
|
|
486
|
-
error_code = e.args[0]
|
|
487
|
-
else:
|
|
488
|
-
error_code = None
|
|
489
|
-
|
|
490
|
-
if error_code == MYSQL_LOCK_WAIT_TIMEOUT:
|
|
491
|
-
if attempt < cls._max_retries - 1:
|
|
492
|
-
# Calculate delay with exponential backoff and jitter
|
|
493
|
-
delay = min(
|
|
494
|
-
cls._base_delay * (2 ** attempt) + random.uniform(0, 0.5),
|
|
495
|
-
cls._max_delay
|
|
496
|
-
)
|
|
497
|
-
logging.warning(
|
|
498
|
-
f"Token 记录遇到锁等待超时,第 {attempt + 1} 次重试,"
|
|
499
|
-
f"等待 {delay:.2f} 秒后重试..."
|
|
500
|
-
)
|
|
501
|
-
await asyncio.sleep(delay)
|
|
502
|
-
continue
|
|
503
|
-
else:
|
|
504
|
-
logging.error(
|
|
505
|
-
f"Token 记录在 {cls._max_retries} 次重试后仍然失败(锁等待超时)"
|
|
506
|
-
)
|
|
507
|
-
else:
|
|
508
|
-
# Re-raise non-lock-timeout operational errors
|
|
509
|
-
raise
|
|
510
|
-
|
|
511
|
-
logging.error(f"记录 Token 使用量到 MySQL 失败: {last_exception}", exc_info=True)
|
|
512
|
-
return False
|
|
309
|
+
async with cls._db_service.session() as session:
|
|
310
|
+
# 使用 MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE
|
|
311
|
+
stmt = insert(TokenUsageMySQL).values(
|
|
312
|
+
user_id=user_id,
|
|
313
|
+
tenant_id=tenant_id,
|
|
314
|
+
service_name=service_name,
|
|
315
|
+
system_env=system_env,
|
|
316
|
+
input_tokens=input_tokens,
|
|
317
|
+
output_tokens=output_tokens,
|
|
318
|
+
total_tokens=total,
|
|
319
|
+
model=model,
|
|
320
|
+
usage_date=today
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# 冲突时累加更新
|
|
324
|
+
stmt = stmt.on_duplicate_key_update(
|
|
325
|
+
input_tokens=TokenUsageMySQL.input_tokens + input_tokens,
|
|
326
|
+
output_tokens=TokenUsageMySQL.output_tokens + output_tokens,
|
|
327
|
+
total_tokens=TokenUsageMySQL.total_tokens + total,
|
|
328
|
+
model=model, # 更新为最新使用的模型
|
|
329
|
+
updated_at=datetime.now()
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
await session.execute(stmt)
|
|
333
|
+
return True
|
|
513
334
|
|
|
514
335
|
except Exception as e:
|
|
515
336
|
logging.error(f"记录 Token 使用量到 MySQL 失败: {e}", exc_info=True)
|
|
@@ -1059,50 +880,3 @@ class TokenUsageMySQLService(metaclass=SingletonMeta):
|
|
|
1059
880
|
order_by="total_tokens",
|
|
1060
881
|
order_desc=True
|
|
1061
882
|
)
|
|
1062
|
-
|
|
1063
|
-
@classmethod
|
|
1064
|
-
def configure_buffer(
|
|
1065
|
-
cls,
|
|
1066
|
-
use_buffer: bool = True,
|
|
1067
|
-
flush_interval: float = 5.0,
|
|
1068
|
-
max_buffer_size: int = 500
|
|
1069
|
-
):
|
|
1070
|
-
"""
|
|
1071
|
-
配置缓冲区参数
|
|
1072
|
-
|
|
1073
|
-
Args:
|
|
1074
|
-
use_buffer: 是否启用缓冲区(默认 True)
|
|
1075
|
-
flush_interval: 刷新间隔(秒),默认 5 秒
|
|
1076
|
-
max_buffer_size: 缓冲区最大条目数,默认 500
|
|
1077
|
-
"""
|
|
1078
|
-
cls._use_buffer = use_buffer
|
|
1079
|
-
cls._buffer_flush_interval = flush_interval
|
|
1080
|
-
cls._buffer_max_size = max_buffer_size
|
|
1081
|
-
|
|
1082
|
-
if cls._use_buffer:
|
|
1083
|
-
TokenUsageBuffer.configure(
|
|
1084
|
-
flush_interval=flush_interval,
|
|
1085
|
-
max_buffer_size=max_buffer_size
|
|
1086
|
-
)
|
|
1087
|
-
logging.info(
|
|
1088
|
-
f"TokenUsageMySQLService 缓冲区配置: enabled={use_buffer}, "
|
|
1089
|
-
f"flush_interval={flush_interval}s, max_buffer_size={max_buffer_size}"
|
|
1090
|
-
)
|
|
1091
|
-
else:
|
|
1092
|
-
logging.info("TokenUsageMySQLService 缓冲区已禁用")
|
|
1093
|
-
|
|
1094
|
-
@classmethod
|
|
1095
|
-
async def flush_buffer(cls):
|
|
1096
|
-
"""手动刷新缓冲区到数据库"""
|
|
1097
|
-
await TokenUsageBuffer.flush_now()
|
|
1098
|
-
|
|
1099
|
-
@classmethod
|
|
1100
|
-
def get_buffer_stats(cls) -> Dict[str, Any]:
|
|
1101
|
-
"""获取缓冲区统计信息"""
|
|
1102
|
-
return TokenUsageBuffer.get_stats()
|
|
1103
|
-
|
|
1104
|
-
@classmethod
|
|
1105
|
-
async def stop_buffer(cls):
|
|
1106
|
-
"""停止缓冲区并刷新剩余数据(服务关闭时调用)"""
|
|
1107
|
-
await TokenUsageBuffer.stop()
|
|
1108
|
-
|
|
@@ -45,6 +45,7 @@ src/sycommon/agent/sandbox/__init__.py
|
|
|
45
45
|
src/sycommon/agent/sandbox/file_ops.py
|
|
46
46
|
src/sycommon/agent/sandbox/http_sandbox_backend.py
|
|
47
47
|
src/sycommon/agent/sandbox/sandbox_pool.py
|
|
48
|
+
src/sycommon/agent/sandbox/sandbox_recovery.py
|
|
48
49
|
src/sycommon/agent/sandbox/session.py
|
|
49
50
|
src/sycommon/agent/skills/__init__.py
|
|
50
51
|
src/sycommon/agent/skills/exports.py
|
|
@@ -85,7 +86,6 @@ src/sycommon/llm/native_with_fallback_runnable.py
|
|
|
85
86
|
src/sycommon/llm/output_fixing_runnable.py
|
|
86
87
|
src/sycommon/llm/struct_token.py
|
|
87
88
|
src/sycommon/llm/sy_langfuse.py
|
|
88
|
-
src/sycommon/llm/token_usage_buffer.py
|
|
89
89
|
src/sycommon/llm/token_usage_es_service.py
|
|
90
90
|
src/sycommon/llm/token_usage_mysql_service.py
|
|
91
91
|
src/sycommon/llm/usage_token.py
|