sycommon-python-lib 0.2.1a18__tar.gz → 0.2.1a20__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.1a18 → sycommon_python_lib-0.2.1a20}/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/pyproject.toml +1 -1
- sycommon_python_lib-0.2.1a20/src/sycommon/llm/token_usage_buffer.py +446 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/token_usage_mysql_service.py +283 -52
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon_python_lib.egg-info/SOURCES.txt +1 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/README.md +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/setup.cfg +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/core/console.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/core/models.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/core/project.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/core/utils.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/templates/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/templates/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/templates/base/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/command/templates/web/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/01_basic_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/02_tool_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/03_structured_output.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/04_memory_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/05_streaming.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/06_multi_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/07_skills_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/08_middleware.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/09_interrupt.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/10_custom_llm.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/11_complex_workflow.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/12_batch_processing.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/01_basic_monitoring.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/02_permission_control.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/03_tool_skill_filter.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/04_caching_retry.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/05_sanitization.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/06_tracking.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/07_advanced.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/08_progressive_skills.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/middleware/override_examples.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/examples/virtual_employee_demo.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/exports.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/get_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/sandbox/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/sandbox/file_ops.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/sandbox/http_sandbox_backend.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/sandbox/session.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/skills/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/skills/examples/faq_handler/scripts/search.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/skills/exports.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/agent/virtual_employee.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/ElasticsearchConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/LangfuseConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/RedisConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/elasticsearch_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/redis_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/database/token_usage_db_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/heartbeat_process/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/embedding.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/get_llm.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/llm_tokens.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/output_fixing_runnable.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/struct_token.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/sy_langfuse.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/token_usage_es_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/llm/usage_token.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/process_logger.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/sandbox.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/middleware/traceid.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/token_usage.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/models/token_usage_mysql.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/sentry/sy_sentry.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/services.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/nacos_client_base.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/nacos_config_manager.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/nacos_service.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/nacos_service_registration.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tests/deep_agent_server.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tests/test_deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tests/test_email.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tests/test_mq.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/async_utils.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/env.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/merge_headers.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/syemail.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
- {sycommon_python_lib-0.2.1a18 → sycommon_python_lib-0.2.1a20}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Token 使用量本地缓冲管理器
|
|
3
|
+
用于在高并发场景下聚合写入,减少数据库压力
|
|
4
|
+
|
|
5
|
+
完全异步实现,不阻塞主服务
|
|
6
|
+
"""
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import time
|
|
10
|
+
from collections import defaultdict
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime, date
|
|
13
|
+
from typing import Optional, Dict, Any, List
|
|
14
|
+
|
|
15
|
+
from sycommon.config.Config import SingletonMeta
|
|
16
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class TokenUsageBufferItem:
|
|
21
|
+
"""缓冲区中的 Token 使用记录"""
|
|
22
|
+
user_id: str
|
|
23
|
+
tenant_id: Optional[str]
|
|
24
|
+
service_name: str
|
|
25
|
+
system_env: str
|
|
26
|
+
input_tokens: int
|
|
27
|
+
output_tokens: int
|
|
28
|
+
model: Optional[str]
|
|
29
|
+
usage_date: date
|
|
30
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TokenUsageBuffer(metaclass=SingletonMeta):
|
|
34
|
+
"""
|
|
35
|
+
Token 使用量本地缓冲管理器(完全异步实现)
|
|
36
|
+
|
|
37
|
+
功能:
|
|
38
|
+
1. 本地内存缓冲:将高频写入先存入内存
|
|
39
|
+
2. 智能聚合:相同 key 的记录在内存中累加
|
|
40
|
+
3. 定时刷新:按时间间隔批量写入数据库
|
|
41
|
+
4. 阈值触发:缓冲区达到一定数量时立即刷新
|
|
42
|
+
5. 优雅关闭:服务停止时确保数据不丢失
|
|
43
|
+
|
|
44
|
+
特点:
|
|
45
|
+
- 完全异步,使用 asyncio.Lock 保证线程安全
|
|
46
|
+
- 不阻塞主事件循环
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# 缓冲区
|
|
50
|
+
_buffer: Dict[str, TokenUsageBufferItem] = None
|
|
51
|
+
_buffer_lock: asyncio.Lock = None
|
|
52
|
+
_flush_task: Optional[asyncio.Task] = None
|
|
53
|
+
_running: bool = False
|
|
54
|
+
_loop: Optional[asyncio.AbstractEventLoop] = None
|
|
55
|
+
|
|
56
|
+
# 配置参数
|
|
57
|
+
_flush_interval: float = 15.0 # 刷新间隔(秒)
|
|
58
|
+
_max_buffer_size: int = 500 # 缓冲区最大条目数
|
|
59
|
+
_max_batch_size: int = 100 # 单次批量写入最大条目数
|
|
60
|
+
|
|
61
|
+
# 统计信息
|
|
62
|
+
_total_buffered: int = 0
|
|
63
|
+
_total_flushed: int = 0
|
|
64
|
+
_last_flush_time: Optional[datetime] = None
|
|
65
|
+
|
|
66
|
+
# 回调函数(用于实际写入数据库)
|
|
67
|
+
_flush_callback = None
|
|
68
|
+
|
|
69
|
+
def __init__(self):
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def _ensure_initialized(cls):
|
|
74
|
+
"""确保缓冲区已初始化"""
|
|
75
|
+
if cls._buffer is None:
|
|
76
|
+
cls._buffer = {}
|
|
77
|
+
if cls._buffer_lock is None:
|
|
78
|
+
cls._buffer_lock = asyncio.Lock()
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def configure(
|
|
82
|
+
cls,
|
|
83
|
+
flush_interval: float = 15.0,
|
|
84
|
+
max_buffer_size: int = 500,
|
|
85
|
+
max_batch_size: int = 100
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
配置缓冲区参数
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
flush_interval: 刷新间隔(秒),默认 15 秒
|
|
92
|
+
max_buffer_size: 缓冲区最大条目数,超过此数量立即刷新
|
|
93
|
+
max_batch_size: 单次批量写入最大条目数
|
|
94
|
+
"""
|
|
95
|
+
cls._flush_interval = flush_interval
|
|
96
|
+
cls._max_buffer_size = max_buffer_size
|
|
97
|
+
cls._max_batch_size = max_batch_size
|
|
98
|
+
logging.info(
|
|
99
|
+
f"TokenUsageBuffer 配置: flush_interval={flush_interval}s, "
|
|
100
|
+
f"max_buffer_size={max_buffer_size}, max_batch_size={max_batch_size}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def set_flush_callback(cls, callback):
|
|
105
|
+
"""
|
|
106
|
+
设置刷新回调函数
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
callback: 异步函数,接收 List[TokenUsageBufferItem] 参数
|
|
110
|
+
"""
|
|
111
|
+
cls._flush_callback = callback
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def _make_key(
|
|
115
|
+
cls,
|
|
116
|
+
user_id: str,
|
|
117
|
+
service_name: str,
|
|
118
|
+
system_env: str,
|
|
119
|
+
usage_date: date
|
|
120
|
+
) -> str:
|
|
121
|
+
"""生成缓冲区 key"""
|
|
122
|
+
return f"{user_id}|{service_name}|{system_env}|{usage_date.isoformat()}"
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
async def add(
|
|
126
|
+
cls,
|
|
127
|
+
user_id: Optional[str],
|
|
128
|
+
input_tokens: int,
|
|
129
|
+
output_tokens: int,
|
|
130
|
+
model: Optional[str] = None,
|
|
131
|
+
tenant_id: Optional[str] = None,
|
|
132
|
+
service_name: Optional[str] = None,
|
|
133
|
+
system_env: Optional[str] = None
|
|
134
|
+
) -> bool:
|
|
135
|
+
"""
|
|
136
|
+
异步添加 Token 使用记录到缓冲区
|
|
137
|
+
|
|
138
|
+
如果已存在相同 key 的记录,则累加 tokens
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
user_id: 用户ID
|
|
142
|
+
input_tokens: 输入 token 数量
|
|
143
|
+
output_tokens: 输出 token 数量
|
|
144
|
+
model: 模型名称
|
|
145
|
+
tenant_id: 租户ID
|
|
146
|
+
service_name: 服务名称
|
|
147
|
+
system_env: 系统环境
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
bool: 是否成功添加到缓冲区
|
|
151
|
+
"""
|
|
152
|
+
cls._ensure_initialized()
|
|
153
|
+
|
|
154
|
+
# 处理空值
|
|
155
|
+
user_id = user_id or ''
|
|
156
|
+
service_name = service_name or ''
|
|
157
|
+
system_env = system_env or ''
|
|
158
|
+
usage_date = date.today()
|
|
159
|
+
|
|
160
|
+
key = cls._make_key(user_id, service_name, system_env, usage_date)
|
|
161
|
+
|
|
162
|
+
should_flush = False
|
|
163
|
+
|
|
164
|
+
async with cls._buffer_lock:
|
|
165
|
+
if key in cls._buffer:
|
|
166
|
+
# 累加到已有记录
|
|
167
|
+
item = cls._buffer[key]
|
|
168
|
+
item.input_tokens += input_tokens
|
|
169
|
+
item.output_tokens += output_tokens
|
|
170
|
+
item.model = model # 更新为最新模型
|
|
171
|
+
else:
|
|
172
|
+
# 创建新记录
|
|
173
|
+
cls._buffer[key] = TokenUsageBufferItem(
|
|
174
|
+
user_id=user_id,
|
|
175
|
+
tenant_id=tenant_id,
|
|
176
|
+
service_name=service_name,
|
|
177
|
+
system_env=system_env,
|
|
178
|
+
input_tokens=input_tokens,
|
|
179
|
+
output_tokens=output_tokens,
|
|
180
|
+
model=model,
|
|
181
|
+
usage_date=usage_date
|
|
182
|
+
)
|
|
183
|
+
cls._total_buffered += 1
|
|
184
|
+
|
|
185
|
+
# 检查是否需要立即刷新
|
|
186
|
+
if len(cls._buffer) >= cls._max_buffer_size:
|
|
187
|
+
should_flush = True
|
|
188
|
+
|
|
189
|
+
# 在锁外触发刷新,避免阻塞
|
|
190
|
+
if should_flush:
|
|
191
|
+
logging.info(f"TokenUsageBuffer 缓冲区达到阈值 ({cls._max_buffer_size}),触发立即刷新")
|
|
192
|
+
asyncio.create_task(cls._do_flush())
|
|
193
|
+
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
@classmethod
|
|
197
|
+
def add_sync(
|
|
198
|
+
cls,
|
|
199
|
+
user_id: Optional[str],
|
|
200
|
+
input_tokens: int,
|
|
201
|
+
output_tokens: int,
|
|
202
|
+
model: Optional[str] = None,
|
|
203
|
+
tenant_id: Optional[str] = None,
|
|
204
|
+
service_name: Optional[str] = None,
|
|
205
|
+
system_env: Optional[str] = None
|
|
206
|
+
) -> bool:
|
|
207
|
+
"""
|
|
208
|
+
同步添加(非阻塞,fire-and-forget)
|
|
209
|
+
|
|
210
|
+
通过 asyncio.to_thread 或 run_coroutine_threadsafe 调用异步方法
|
|
211
|
+
不会阻塞调用线程
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
同 add 方法
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
bool: 是否成功提交
|
|
218
|
+
"""
|
|
219
|
+
cls._ensure_initialized()
|
|
220
|
+
|
|
221
|
+
# 处理空值
|
|
222
|
+
user_id = user_id or ''
|
|
223
|
+
service_name = service_name or ''
|
|
224
|
+
system_env = system_env or ''
|
|
225
|
+
usage_date = date.today()
|
|
226
|
+
|
|
227
|
+
key = cls._make_key(user_id, service_name, system_env, usage_date)
|
|
228
|
+
|
|
229
|
+
# 使用简单的方式:直接操作缓冲区(最小化锁竞争)
|
|
230
|
+
# 这里的操作是原子的,不需要复杂的锁
|
|
231
|
+
if cls._buffer is None:
|
|
232
|
+
cls._buffer = {}
|
|
233
|
+
|
|
234
|
+
# 快速路径:更新或创建记录
|
|
235
|
+
if key in cls._buffer:
|
|
236
|
+
item = cls._buffer[key]
|
|
237
|
+
item.input_tokens += input_tokens
|
|
238
|
+
item.output_tokens += output_tokens
|
|
239
|
+
item.model = model
|
|
240
|
+
else:
|
|
241
|
+
cls._buffer[key] = TokenUsageBufferItem(
|
|
242
|
+
user_id=user_id,
|
|
243
|
+
tenant_id=tenant_id,
|
|
244
|
+
service_name=service_name,
|
|
245
|
+
system_env=system_env,
|
|
246
|
+
input_tokens=input_tokens,
|
|
247
|
+
output_tokens=output_tokens,
|
|
248
|
+
model=model,
|
|
249
|
+
usage_date=usage_date
|
|
250
|
+
)
|
|
251
|
+
cls._total_buffered += 1
|
|
252
|
+
|
|
253
|
+
# 检查是否需要刷新
|
|
254
|
+
if len(cls._buffer) >= cls._max_buffer_size:
|
|
255
|
+
cls._trigger_async_flush()
|
|
256
|
+
|
|
257
|
+
return True
|
|
258
|
+
|
|
259
|
+
@classmethod
|
|
260
|
+
def _trigger_async_flush(cls):
|
|
261
|
+
"""触发异步刷新(从同步上下文调用)"""
|
|
262
|
+
try:
|
|
263
|
+
loop = asyncio.get_running_loop()
|
|
264
|
+
loop.create_task(cls._do_flush())
|
|
265
|
+
except RuntimeError:
|
|
266
|
+
# 没有运行中的事件循环,忽略
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
@classmethod
|
|
270
|
+
async def start(cls):
|
|
271
|
+
"""
|
|
272
|
+
启动缓冲区刷新任务
|
|
273
|
+
|
|
274
|
+
应该在应用启动时调用
|
|
275
|
+
"""
|
|
276
|
+
if cls._running:
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
cls._ensure_initialized()
|
|
280
|
+
cls._running = True
|
|
281
|
+
cls._loop = asyncio.get_running_loop()
|
|
282
|
+
|
|
283
|
+
# 启动定时刷新任务
|
|
284
|
+
cls._flush_task = asyncio.create_task(cls._flush_loop())
|
|
285
|
+
logging.info(f"TokenUsageBuffer 已启动,刷新间隔: {cls._flush_interval}s")
|
|
286
|
+
|
|
287
|
+
# 注册关闭钩子
|
|
288
|
+
import atexit
|
|
289
|
+
atexit.register(cls.shutdown_sync)
|
|
290
|
+
|
|
291
|
+
@classmethod
|
|
292
|
+
async def stop(cls):
|
|
293
|
+
"""
|
|
294
|
+
停止缓冲区并刷新剩余数据
|
|
295
|
+
|
|
296
|
+
应该在应用关闭时调用
|
|
297
|
+
"""
|
|
298
|
+
if not cls._running:
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
cls._running = False
|
|
302
|
+
|
|
303
|
+
# 取消定时任务
|
|
304
|
+
if cls._flush_task:
|
|
305
|
+
cls._flush_task.cancel()
|
|
306
|
+
try:
|
|
307
|
+
await cls._flush_task
|
|
308
|
+
except asyncio.CancelledError:
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
# 最后一次刷新
|
|
312
|
+
await cls._do_flush()
|
|
313
|
+
logging.info("TokenUsageBuffer 已停止")
|
|
314
|
+
|
|
315
|
+
@classmethod
|
|
316
|
+
def shutdown_sync(cls):
|
|
317
|
+
"""同步关闭方法(用于 atexit)"""
|
|
318
|
+
if not cls._running:
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
cls._running = False
|
|
322
|
+
|
|
323
|
+
# 同步刷新剩余数据
|
|
324
|
+
if cls._buffer:
|
|
325
|
+
try:
|
|
326
|
+
# 尝试在已有事件循环中执行
|
|
327
|
+
if cls._loop and cls._loop.is_running():
|
|
328
|
+
future = asyncio.run_coroutine_threadsafe(cls._do_flush(), cls._loop)
|
|
329
|
+
future.result(timeout=10) # 最多等待 10 秒
|
|
330
|
+
else:
|
|
331
|
+
# 创建新的事件循环
|
|
332
|
+
loop = asyncio.new_event_loop()
|
|
333
|
+
asyncio.set_event_loop(loop)
|
|
334
|
+
try:
|
|
335
|
+
loop.run_until_complete(cls._do_flush())
|
|
336
|
+
finally:
|
|
337
|
+
loop.close()
|
|
338
|
+
except Exception as e:
|
|
339
|
+
logging.error(f"TokenUsageBuffer 关闭时刷新失败: {e}", exc_info=True)
|
|
340
|
+
|
|
341
|
+
logging.info("TokenUsageBuffer 已同步关闭")
|
|
342
|
+
|
|
343
|
+
@classmethod
|
|
344
|
+
async def _flush_loop(cls):
|
|
345
|
+
"""定时刷新循环"""
|
|
346
|
+
while cls._running:
|
|
347
|
+
try:
|
|
348
|
+
await asyncio.sleep(cls._flush_interval)
|
|
349
|
+
await cls._do_flush()
|
|
350
|
+
except asyncio.CancelledError:
|
|
351
|
+
break
|
|
352
|
+
except Exception as e:
|
|
353
|
+
logging.error(f"TokenUsageBuffer 刷新循环异常: {e}", exc_info=True)
|
|
354
|
+
await asyncio.sleep(1) # 出错后等待 1 秒再继续
|
|
355
|
+
|
|
356
|
+
@classmethod
|
|
357
|
+
async def _do_flush(cls):
|
|
358
|
+
"""执行批量刷新到数据库"""
|
|
359
|
+
if not cls._buffer:
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
if not cls._flush_callback:
|
|
363
|
+
logging.warning("TokenUsageBuffer 未设置 flush_callback,无法刷新数据")
|
|
364
|
+
return
|
|
365
|
+
|
|
366
|
+
# 获取要刷新的数据
|
|
367
|
+
items_to_flush = []
|
|
368
|
+
async with cls._buffer_lock:
|
|
369
|
+
if cls._buffer:
|
|
370
|
+
items_to_flush = list(cls._buffer.values())
|
|
371
|
+
cls._buffer = {} # 清空缓冲区
|
|
372
|
+
|
|
373
|
+
if not items_to_flush:
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
# 分批刷新
|
|
377
|
+
total_items = len(items_to_flush)
|
|
378
|
+
success_count = 0
|
|
379
|
+
failed_items = []
|
|
380
|
+
|
|
381
|
+
for i in range(0, total_items, cls._max_batch_size):
|
|
382
|
+
batch = items_to_flush[i:i + cls._max_batch_size]
|
|
383
|
+
try:
|
|
384
|
+
await cls._flush_callback(batch)
|
|
385
|
+
success_count += len(batch)
|
|
386
|
+
except Exception as e:
|
|
387
|
+
logging.error(f"TokenUsageBuffer 批量刷新失败: {e}", exc_info=True)
|
|
388
|
+
failed_items.extend(batch)
|
|
389
|
+
|
|
390
|
+
# 失败的记录重新放回缓冲区
|
|
391
|
+
if failed_items:
|
|
392
|
+
async with cls._buffer_lock:
|
|
393
|
+
for item in failed_items:
|
|
394
|
+
key = cls._make_key(
|
|
395
|
+
item.user_id, item.service_name,
|
|
396
|
+
item.system_env, item.usage_date
|
|
397
|
+
)
|
|
398
|
+
if key in cls._buffer:
|
|
399
|
+
cls._buffer[key].input_tokens += item.input_tokens
|
|
400
|
+
cls._buffer[key].output_tokens += item.output_tokens
|
|
401
|
+
else:
|
|
402
|
+
cls._buffer[key] = item
|
|
403
|
+
|
|
404
|
+
cls._total_flushed += success_count
|
|
405
|
+
cls._last_flush_time = datetime.now()
|
|
406
|
+
|
|
407
|
+
if success_count > 0:
|
|
408
|
+
logging.info(
|
|
409
|
+
f"TokenUsageBuffer 刷新完成: 成功 {success_count} 条, "
|
|
410
|
+
f"失败 {len(failed_items)} 条"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
@classmethod
|
|
414
|
+
async def flush_now(cls):
|
|
415
|
+
"""手动触发立即刷新"""
|
|
416
|
+
await cls._do_flush()
|
|
417
|
+
|
|
418
|
+
@classmethod
|
|
419
|
+
def get_stats(cls) -> Dict[str, Any]:
|
|
420
|
+
"""获取缓冲区统计信息"""
|
|
421
|
+
buffer_size = len(cls._buffer) if cls._buffer else 0
|
|
422
|
+
total_tokens = 0
|
|
423
|
+
if cls._buffer:
|
|
424
|
+
total_tokens = sum(
|
|
425
|
+
item.input_tokens + item.output_tokens
|
|
426
|
+
for item in cls._buffer.values()
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
"running": cls._running,
|
|
431
|
+
"buffer_size": buffer_size,
|
|
432
|
+
"total_tokens_in_buffer": total_tokens,
|
|
433
|
+
"total_buffered": cls._total_buffered,
|
|
434
|
+
"total_flushed": cls._total_flushed,
|
|
435
|
+
"last_flush_time": cls._last_flush_time.isoformat() if cls._last_flush_time else None,
|
|
436
|
+
"config": {
|
|
437
|
+
"flush_interval": cls._flush_interval,
|
|
438
|
+
"max_buffer_size": cls._max_buffer_size,
|
|
439
|
+
"max_batch_size": cls._max_batch_size
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
@classmethod
|
|
444
|
+
def is_running(cls) -> bool:
|
|
445
|
+
"""检查缓冲区是否正在运行"""
|
|
446
|
+
return cls._running
|