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.
Files changed (167) hide show
  1. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/PKG-INFO +1 -1
  2. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/pyproject.toml +1 -1
  3. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/__init__.py +4 -0
  4. sycommon_python_lib-0.2.1a21/src/sycommon/agent/sandbox/sandbox_recovery.py +197 -0
  5. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/token_usage_mysql_service.py +52 -278
  6. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
  7. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/SOURCES.txt +1 -1
  8. sycommon_python_lib-0.2.1a19/src/sycommon/llm/token_usage_buffer.py +0 -446
  9. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/README.md +0 -0
  10. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/setup.cfg +0 -0
  11. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/__init__.py +0 -0
  12. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/cli.py +0 -0
  13. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/__init__.py +0 -0
  14. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/console.py +0 -0
  15. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/models.py +0 -0
  16. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/project.py +0 -0
  17. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/core/utils.py +0 -0
  18. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/__init__.py +0 -0
  19. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/agent/__init__.py +0 -0
  20. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/base/__init__.py +0 -0
  21. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/command/templates/web/__init__.py +0 -0
  22. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/__init__.py +0 -0
  23. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/__init__.py +0 -0
  24. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/01_basic_agent.py +0 -0
  25. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/02_tool_agent.py +0 -0
  26. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/03_structured_output.py +0 -0
  27. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/04_memory_agent.py +0 -0
  28. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/05_streaming.py +0 -0
  29. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/06_multi_agent.py +0 -0
  30. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/07_skills_agent.py +0 -0
  31. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/08_middleware.py +0 -0
  32. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/09_interrupt.py +0 -0
  33. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/10_custom_llm.py +0 -0
  34. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/11_complex_workflow.py +0 -0
  35. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/12_batch_processing.py +0 -0
  36. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/__init__.py +0 -0
  37. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/01_basic_monitoring.py +0 -0
  38. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/02_permission_control.py +0 -0
  39. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/03_tool_skill_filter.py +0 -0
  40. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/04_caching_retry.py +0 -0
  41. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/05_sanitization.py +0 -0
  42. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/06_tracking.py +0 -0
  43. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/07_advanced.py +0 -0
  44. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/08_progressive_skills.py +0 -0
  45. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/__init__.py +0 -0
  46. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/middleware/override_examples.py +0 -0
  47. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/examples/virtual_employee_demo.py +0 -0
  48. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/exports.py +0 -0
  49. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/get_agent.py +0 -0
  50. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/file_ops.py +0 -0
  51. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/http_sandbox_backend.py +0 -0
  52. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
  53. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/sandbox/session.py +0 -0
  54. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/skills/__init__.py +0 -0
  55. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/skills/examples/faq_handler/scripts/search.py +0 -0
  56. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/skills/exports.py +0 -0
  57. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/agent/virtual_employee.py +0 -0
  58. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/Config.py +0 -0
  59. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/DatabaseConfig.py +0 -0
  60. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/ElasticsearchConfig.py +0 -0
  61. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/EmbeddingConfig.py +0 -0
  62. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/LLMConfig.py +0 -0
  63. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/LangfuseConfig.py +0 -0
  64. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/MQConfig.py +0 -0
  65. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/RedisConfig.py +0 -0
  66. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/RerankerConfig.py +0 -0
  67. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/SentryConfig.py +0 -0
  68. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/config/__init__.py +0 -0
  69. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/async_base_db_service.py +0 -0
  70. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/async_database_service.py +0 -0
  71. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/base_db_service.py +0 -0
  72. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/database_service.py +0 -0
  73. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/elasticsearch_service.py +0 -0
  74. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/redis_service.py +0 -0
  75. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/database/token_usage_db_service.py +0 -0
  76. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/__init__.py +0 -0
  77. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/health_check.py +0 -0
  78. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/metrics.py +0 -0
  79. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/health/ping.py +0 -0
  80. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/__init__.py +0 -0
  81. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
  82. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
  83. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
  84. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/__init__.py +0 -0
  85. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/embedding.py +0 -0
  86. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/get_llm.py +0 -0
  87. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/llm_logger.py +0 -0
  88. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/llm_tokens.py +0 -0
  89. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
  90. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
  91. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/output_fixing_runnable.py +0 -0
  92. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/struct_token.py +0 -0
  93. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/sy_langfuse.py +0 -0
  94. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/token_usage_es_service.py +0 -0
  95. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/llm/usage_token.py +0 -0
  96. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/__init__.py +0 -0
  97. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/async_sql_logger.py +0 -0
  98. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/kafka_log.py +0 -0
  99. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/logger_levels.py +0 -0
  100. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/logger_wrapper.py +0 -0
  101. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/process_logger.py +0 -0
  102. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/logging/sql_logger.py +0 -0
  103. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/__init__.py +0 -0
  104. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/context.py +0 -0
  105. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/cors.py +0 -0
  106. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/docs.py +0 -0
  107. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/exception.py +0 -0
  108. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/middleware.py +0 -0
  109. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/monitor_memory.py +0 -0
  110. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/mq.py +0 -0
  111. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/sandbox.py +0 -0
  112. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/timeout.py +0 -0
  113. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/middleware/traceid.py +0 -0
  114. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/__init__.py +0 -0
  115. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/base_http.py +0 -0
  116. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/log.py +0 -0
  117. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/mqlistener_config.py +0 -0
  118. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/mqmsg_model.py +0 -0
  119. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/mqsend_config.py +0 -0
  120. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/sso_user.py +0 -0
  121. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/token_usage.py +0 -0
  122. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/models/token_usage_mysql.py +0 -0
  123. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/notice/__init__.py +0 -0
  124. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/notice/uvicorn_monitor.py +0 -0
  125. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
  126. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
  127. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
  128. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
  129. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
  130. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
  131. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
  132. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
  133. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
  134. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sentry/__init__.py +0 -0
  135. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sentry/sy_sentry.py +0 -0
  136. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/services.py +0 -0
  137. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sse/__init__.py +0 -0
  138. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sse/event.py +0 -0
  139. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/sse/sse.py +0 -0
  140. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/__init__.py +0 -0
  141. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/example.py +0 -0
  142. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/example2.py +0 -0
  143. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/feign.py +0 -0
  144. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/feign_client.py +0 -0
  145. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_client_base.py +0 -0
  146. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_config_manager.py +0 -0
  147. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
  148. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_service.py +0 -0
  149. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
  150. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/nacos_service_registration.py +0 -0
  151. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/synacos/param.py +0 -0
  152. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/deep_agent_server.py +0 -0
  153. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/test_deep_agent.py +0 -0
  154. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/test_email.py +0 -0
  155. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tests/test_mq.py +0 -0
  156. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/__init__.py +0 -0
  157. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/async_utils.py +0 -0
  158. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/docs.py +0 -0
  159. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/env.py +0 -0
  160. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/merge_headers.py +0 -0
  161. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/snowflake.py +0 -0
  162. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/syemail.py +0 -0
  163. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon/tools/timing.py +0 -0
  164. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  165. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  166. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
  167. {sycommon_python_lib-0.2.1a19 → sycommon_python_lib-0.2.1a21}/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.1a19
3
+ Version: 0.2.1a21
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.1a19"
3
+ version = "0.2.1a21"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -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
- if cls._use_buffer:
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
- return True
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 记录任务失败: {e}", exc_info=True)
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
- # Retry loop with exponential backoff
451
- last_exception = None
452
- for attempt in range(cls._max_retries):
453
- try:
454
- async with cls._db_service.session() as session:
455
- # 使用 MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE
456
- stmt = insert(TokenUsageMySQL).values(
457
- user_id=user_id,
458
- tenant_id=tenant_id,
459
- service_name=service_name,
460
- system_env=system_env,
461
- input_tokens=input_tokens,
462
- output_tokens=output_tokens,
463
- total_tokens=total,
464
- model=model,
465
- usage_date=today
466
- )
467
-
468
- # 冲突时累加更新
469
- stmt = stmt.on_duplicate_key_update(
470
- input_tokens=TokenUsageMySQL.input_tokens + input_tokens,
471
- output_tokens=TokenUsageMySQL.output_tokens + output_tokens,
472
- total_tokens=TokenUsageMySQL.total_tokens + total,
473
- model=model, # 更新为最新使用的模型
474
- updated_at=datetime.now()
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
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.1a19
3
+ Version: 0.2.1a21
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -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