digitalkin 1.0.5.dev5__tar.gz → 1.0.5.dev6__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.
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/PKG-INFO +5 -3
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/pyproject.toml +5 -3
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/__version__.py +1 -1
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/profiling/task_profiler.py +2 -2
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/module_runner.py +7 -1
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/redis/redis_client.py +1 -1
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +6 -4
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/task_executor.py +4 -4
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/task_session.py +5 -5
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/gateway_servicer.py +86 -27
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/interceptors/validation.py +18 -3
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/module_server.py +0 -2
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/setup_types.py +1 -1
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/server/grpc.py +7 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/default_setup.py +4 -4
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/setup_strategy.py +2 -2
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/default_storage.py +6 -8
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/storage_strategy.py +3 -3
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin.egg-info/PKG-INFO +5 -3
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin.egg-info/requires.txt +4 -2
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/LICENSE +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/README.md +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/mock/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/mock/mock_pb2.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/server_async_insecure.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/server_async_secure.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/server_sync_insecure.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/base_server/server_sync_secure.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/echo_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/models/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/models/input.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/models/output.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/models/secret.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/models/setup.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/server.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/triggers/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/bench_module/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/modules/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/modules/archetype_with_tools_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/modules/cpu_intensive_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/modules/dynamic_setup_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/modules/minimal_llm_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/modules/text_transform_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/monitoring/tests/test_metrics.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/client.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/echo_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/models/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/models/input.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/models/output.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/models/secret.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/models/setup.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/server.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/triggers/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/redis_demo/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/services/filesystem_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/examples/services/storage_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/setup.cfg +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/agno_adapter.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/agui_tools.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/hitl.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/models.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/module_toolkit.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/base.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/chat_history.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/defaults.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/action.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/base.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/kins/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/kins/action.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/kins/kit.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/loader/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/loader/action.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/loader/kit.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/services/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/services/action.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/services/kit.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/tools/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/tools/action.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/registry/tools/kit.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/community/agno/toolkits/user_profile.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/common/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/common/factories.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/job_manager/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/profiling/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/profiling/step_timer.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/resilience/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/resilience/bulkhead.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/_base_server.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/interceptors/permission.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/interceptors/request_ids.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/module_servicer.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/stream_session.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/logger.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/agui_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/base_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/cost_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/file_history_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/logger_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/mixins/storage_mixin.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/core/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/core/job_manager_models.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/core/redis.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/core/task_monitor.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/events/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/events/agent_events.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/grpc_servers/models.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/grpc_servers/stream_error_codes.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/grpc_servers/types.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/ag_ui.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/base_types.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/knowledge_files.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/loaded_tools.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/module_context.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/module_types.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/request_metadata.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/select_schema.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/tool_cache.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/tool_reference.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/module/utility.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/services/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/services/cost.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/services/filesystem.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/services/registry.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/services/services.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/services/storage.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/certificate.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/client/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/client/channel.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/client/circuit_breaker.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/client/client.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/client/grpc.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/gateway.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/log.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/profiling.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/queue.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/redis.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/registry.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/resilience.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/server/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/server/channel.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/server/server.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/server/servicer.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/task_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/utils/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/utils/channel.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/utils/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/_base_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/archetype_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/tool_module.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/trigger_handler.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/triggers/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/py.typed +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/base_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/communication/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/communication/communication_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/communication/default_communication.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/communication/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/communication/grpc_communication.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/cost/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/cost/cost_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/cost/default_cost.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/cost/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/cost/grpc_cost.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/filesystem/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/filesystem/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/identity/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/identity/default_identity.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/identity/identity_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/registry/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/registry/default_registry.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/registry/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/registry/grpc_registry.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/registry/registry_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/secret/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/secret/default_secret.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/secret/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/secret/grpc_secret.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/secret/secret_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/services_config.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/services_models.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/grpc_setup.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/grpc_storage.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/task_manager/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/task_manager/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/user_profile/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/user_profile/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/__init__.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/arg_parser.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/conditional_schema.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/development_mode_action.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/env_manager.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/env_template.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/exceptions.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/json_structure.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/llm_ready_schema.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/package_discover.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/proto_utils.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/schema_splitter.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/utils/setup_content_validator.py +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin.egg-info/SOURCES.txt +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin.egg-info/dependency_links.txt +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin.egg-info/top_level.txt +0 -0
- {digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/tests/test_exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: digitalkin
|
|
3
|
-
Version: 1.0.5.
|
|
3
|
+
Version: 1.0.5.dev6
|
|
4
4
|
Summary: SDK to build kin used in DigitalKin
|
|
5
5
|
Author-email: "DigitalKin.ai" <contact@digitalkin.ai>
|
|
6
6
|
License: Attribution-NonCommercial-ShareAlike 4.0 International
|
|
@@ -454,16 +454,18 @@ Description-Content-Type: text/markdown
|
|
|
454
454
|
License-File: LICENSE
|
|
455
455
|
Requires-Dist: ag-ui-protocol==0.1.21.dev1787220377
|
|
456
456
|
Requires-Dist: agentic-mesh-protocol==1.2.0
|
|
457
|
-
Requires-Dist: agno<3
|
|
457
|
+
Requires-Dist: agno<4,>=3.1.1
|
|
458
458
|
Requires-Dist: anyio>=4.13.0
|
|
459
|
+
Requires-Dist: cel-python>=0.5
|
|
459
460
|
Requires-Dist: grpcio-health-checking==1.82.1
|
|
460
461
|
Requires-Dist: grpcio-reflection==1.82.1
|
|
461
462
|
Requires-Dist: grpcio-status==1.82.1
|
|
463
|
+
Requires-Dist: protovalidate<2,>=1.2.0
|
|
462
464
|
Requires-Dist: pydantic-settings>=2.14.1
|
|
463
465
|
Requires-Dist: pydantic>=2.12.4
|
|
464
466
|
Requires-Dist: redis[hiredis]<9,>=7.4.0
|
|
465
467
|
Provides-Extra: agno
|
|
466
|
-
Requires-Dist: agno
|
|
468
|
+
Requires-Dist: agno<4,>=3.1.1; extra == "agno"
|
|
467
469
|
Provides-Extra: performance
|
|
468
470
|
Requires-Dist: uvloop>=0.21; extra == "performance"
|
|
469
471
|
Provides-Extra: profiling
|
|
@@ -32,19 +32,21 @@
|
|
|
32
32
|
# because it is a dated prerelease; move to >=0.1.21 once that ships stable.
|
|
33
33
|
"ag-ui-protocol==0.1.21.dev1787220377",
|
|
34
34
|
"agentic-mesh-protocol==1.2.0",
|
|
35
|
-
"agno>=
|
|
35
|
+
"agno>=3.1.1,<4",
|
|
36
36
|
"anyio>=4.13.0",
|
|
37
|
+
"cel-python>=0.5",
|
|
37
38
|
"grpcio-health-checking==1.82.1",
|
|
38
39
|
"grpcio-reflection==1.82.1",
|
|
39
40
|
"grpcio-status==1.82.1",
|
|
41
|
+
"protovalidate>=1.2.0,<2",
|
|
40
42
|
"pydantic-settings>=2.14.1",
|
|
41
43
|
"pydantic>=2.12.4",
|
|
42
44
|
"redis[hiredis]>=7.4.0,<9",
|
|
43
45
|
]
|
|
44
|
-
version = "1.0.5.
|
|
46
|
+
version = "1.0.5.dev6"
|
|
45
47
|
|
|
46
48
|
[project.optional-dependencies]
|
|
47
|
-
agno = [ "agno>=
|
|
49
|
+
agno = [ "agno>=3.1.1,<4" ]
|
|
48
50
|
performance = [ "uvloop>=0.21" ]
|
|
49
51
|
profiling = [
|
|
50
52
|
"pyinstrument>=5.1.2",
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/profiling/task_profiler.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Per-task profiling wrapper for VizTracer, Yappi, and Pyinstrument."""
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
3
|
+
import datetime as dt
|
|
4
4
|
import io
|
|
5
5
|
import os
|
|
6
6
|
from pathlib import Path
|
|
@@ -101,7 +101,7 @@ class TaskProfiler:
|
|
|
101
101
|
return
|
|
102
102
|
|
|
103
103
|
try: # ruff: ignore[too-many-statements-in-try-clause]
|
|
104
|
-
timestamp =
|
|
104
|
+
timestamp = dt.datetime.now(dt.timezone.utc).strftime("%Y%m%dT%H%M%S%f")
|
|
105
105
|
base = f"{self._task_id}_{timestamp}"
|
|
106
106
|
|
|
107
107
|
if self._mode == ProfilerMode.VIZTRACER:
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/module_runner.py
RENAMED
|
@@ -151,9 +151,13 @@ class ModuleRunner:
|
|
|
151
151
|
last_write_ns = time.monotonic_ns()
|
|
152
152
|
data = output_data.model_dump(mode="json")
|
|
153
153
|
if data.get("root", {}).get("protocol") == "stream.end":
|
|
154
|
+
if eos_written:
|
|
155
|
+
# TODO(validate): EOS-ONCE a stream gets a single end-of-stream marker
|
|
156
|
+
logger.info("[VALIDATE EOS-ONCE] duplicate stream.end skipped", extra=log_extra)
|
|
157
|
+
return
|
|
154
158
|
t_eos_write_start = time.perf_counter_ns()
|
|
155
|
-
eos_written = True
|
|
156
159
|
await self._redis_client.xadd(stream_key, {"eos": b"true"})
|
|
160
|
+
eos_written = True
|
|
157
161
|
await self._redis_client.expire(stream_key, stream_settings.redis_stream_ttl)
|
|
158
162
|
await self._redis_client.expire(idem_key, stream_settings.redis_stream_ttl)
|
|
159
163
|
# TODO(validate): IDEM-TTL-EOS idem TTL drops to redis_stream_ttl once EOS is written
|
|
@@ -372,6 +376,8 @@ class ModuleRunner:
|
|
|
372
376
|
logger.warning(
|
|
373
377
|
"[VALIDATE RUNNER-LEAK] task never created; stopping preloaded instance", extra=log_extra
|
|
374
378
|
)
|
|
379
|
+
# Only a failure path gets here, and on_fatal already wrote stream.error + EOS.
|
|
380
|
+
eos_written = True
|
|
375
381
|
try:
|
|
376
382
|
await module.stop(cancel_reason)
|
|
377
383
|
finally:
|
|
@@ -331,7 +331,7 @@ class RedisClient: # ruff: ignore[too-many-public-methods]
|
|
|
331
331
|
"""
|
|
332
332
|
return await self._client.decr(name)
|
|
333
333
|
|
|
334
|
-
async def eval(self, script: str, keys: list[str], args: list[str]) ->
|
|
334
|
+
async def eval(self, script: str, keys: list[str], args: list[str]) -> Any:
|
|
335
335
|
"""Execute a Lua script on Redis.
|
|
336
336
|
|
|
337
337
|
Args:
|
|
@@ -33,11 +33,12 @@ class RedisIdempotency:
|
|
|
33
33
|
"""
|
|
34
34
|
self._redis_client = redis_client
|
|
35
35
|
|
|
36
|
-
async def claim(self, task_id: str, instance_id: str) -> ClaimResult:
|
|
36
|
+
async def claim(self, task_id: str, instance_id: str, mission_id: str) -> ClaimResult:
|
|
37
37
|
"""Atomically claim execution of ``task_id``.
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
The claim stores ``{instance_id}|{mission_id}`` so ``SendSignal`` can tie a
|
|
40
|
+
cancel to the owning mission. ``CLAIMED`` on the first claim; ``RECLAIMED``
|
|
41
|
+
if the same owner already holds it (same replica retrying, TTL left untouched); ``TAKEN``
|
|
41
42
|
if another replica owns it. The GET/SET is a single Lua eval so concurrent
|
|
42
43
|
callers can never both win. The script returns the ``ClaimResult``
|
|
43
44
|
integer value (0/1/2) — a Redis integer reply.
|
|
@@ -45,6 +46,7 @@ class RedisIdempotency:
|
|
|
45
46
|
Args:
|
|
46
47
|
task_id: Task whose execution is being claimed.
|
|
47
48
|
instance_id: Stable per-process identifier of the claimer.
|
|
49
|
+
mission_id: Mission that owns the task.
|
|
48
50
|
|
|
49
51
|
Returns:
|
|
50
52
|
The claim outcome.
|
|
@@ -63,7 +65,7 @@ class RedisIdempotency:
|
|
|
63
65
|
raw = await self._redis_client.eval(
|
|
64
66
|
script,
|
|
65
67
|
[f"idem:{task_id}"],
|
|
66
|
-
[instance_id, str(get_redis_settings().idem_ttl)],
|
|
68
|
+
[f"{instance_id}|{mission_id}", str(get_redis_settings().idem_ttl)],
|
|
67
69
|
)
|
|
68
70
|
value = raw.decode() if isinstance(raw, bytes) else raw
|
|
69
71
|
# An unexpected nil reply is treated as TAKEN so we never double-execute.
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/task_executor.py
RENAMED
|
@@ -9,7 +9,7 @@ hard cancel that only changes the recorded reason.
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import asyncio
|
|
12
|
-
import datetime
|
|
12
|
+
import datetime as dt
|
|
13
13
|
from collections.abc import Awaitable, Callable, Coroutine
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
@@ -82,7 +82,7 @@ class TaskExecutor:
|
|
|
82
82
|
async def _run() -> None:
|
|
83
83
|
nonlocal started
|
|
84
84
|
started = True
|
|
85
|
-
session.started_at =
|
|
85
|
+
session.started_at = dt.datetime.now(dt.timezone.utc)
|
|
86
86
|
try:
|
|
87
87
|
await session.set_status("running")
|
|
88
88
|
await coro
|
|
@@ -100,7 +100,7 @@ class TaskExecutor:
|
|
|
100
100
|
logger.exception("Task failed: '%s'", task_id, extra=ids)
|
|
101
101
|
finally:
|
|
102
102
|
coro.close()
|
|
103
|
-
session.completed_at =
|
|
103
|
+
session.completed_at = dt.datetime.now(dt.timezone.utc)
|
|
104
104
|
session.close_stream()
|
|
105
105
|
|
|
106
106
|
duration = (
|
|
@@ -118,7 +118,7 @@ class TaskExecutor:
|
|
|
118
118
|
await _finalize()
|
|
119
119
|
|
|
120
120
|
async def _early_cancel() -> None:
|
|
121
|
-
session.completed_at =
|
|
121
|
+
session.completed_at = dt.datetime.now(dt.timezone.utc)
|
|
122
122
|
await _cancelled()
|
|
123
123
|
session.close_stream()
|
|
124
124
|
await _finalize()
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/core/task_manager/task_session.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
-
import datetime
|
|
6
|
+
import datetime as dt
|
|
7
7
|
import time
|
|
8
8
|
import traceback
|
|
9
9
|
from typing import TYPE_CHECKING
|
|
@@ -30,9 +30,9 @@ class TaskSession:
|
|
|
30
30
|
task_id: str
|
|
31
31
|
mission_id: str
|
|
32
32
|
|
|
33
|
-
created_at:
|
|
34
|
-
started_at:
|
|
35
|
-
completed_at:
|
|
33
|
+
created_at: dt.datetime
|
|
34
|
+
started_at: dt.datetime | None
|
|
35
|
+
completed_at: dt.datetime | None
|
|
36
36
|
|
|
37
37
|
is_cancelled: asyncio.Event
|
|
38
38
|
cancellation_reason: CancellationReason
|
|
@@ -72,7 +72,7 @@ class TaskSession:
|
|
|
72
72
|
self.task_id = task_id
|
|
73
73
|
self.mission_id = mission_id
|
|
74
74
|
|
|
75
|
-
self.created_at =
|
|
75
|
+
self.created_at = dt.datetime.now(dt.timezone.utc)
|
|
76
76
|
self.started_at = None
|
|
77
77
|
self.completed_at = None
|
|
78
78
|
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/gateway_servicer.py
RENAMED
|
@@ -166,7 +166,7 @@ class GatewayServicer:
|
|
|
166
166
|
"""
|
|
167
167
|
await context.abort(grpc.StatusCode.UNIMPLEMENTED, "AssociateTask is served by the backend")
|
|
168
168
|
|
|
169
|
-
async def StartStream( # ruff: ignore[too-many-return-statements, too-many-locals, too-many-statements]
|
|
169
|
+
async def StartStream( # ruff: ignore[complex-structure, too-many-return-statements, too-many-locals, too-many-statements]
|
|
170
170
|
self,
|
|
171
171
|
request: Any,
|
|
172
172
|
context: grpc.aio.ServicerContext,
|
|
@@ -225,7 +225,7 @@ class GatewayServicer:
|
|
|
225
225
|
# consumer), so a re-issued StartStream for an already-claimed/running task is
|
|
226
226
|
# REFUSED — one task_id maps to exactly one dial.
|
|
227
227
|
try:
|
|
228
|
-
claim = await self._idempotency.claim(task_id, SharedRedisListener.PROCESS_ID)
|
|
228
|
+
claim = await self._idempotency.claim(task_id, SharedRedisListener.PROCESS_ID, request.mission_id)
|
|
229
229
|
except RedisError:
|
|
230
230
|
return gateway_dto_pb2.StartStreamResponse(accepted=False, task_id=task_id)
|
|
231
231
|
timer.mark("idempotency_claim")
|
|
@@ -260,18 +260,36 @@ class GatewayServicer:
|
|
|
260
260
|
stream_key = f"task:{task_id}:stream"
|
|
261
261
|
# A fresh claim owns the task: drop any stream/cursor left by a released failed dial so a
|
|
262
262
|
# retry never replays its stream.error + eos, and arm a TTL before the first output.
|
|
263
|
+
cancel_key = f"cancel:{task_id}"
|
|
263
264
|
pipe = self._redis_client.pipeline()
|
|
264
265
|
pipe.delete(stream_key, f"task:{task_id}:cursor")
|
|
265
266
|
pipe.xadd(stream_key, {"pb": start_info.SerializeToString(), "seq": "0"})
|
|
266
267
|
pipe.expire(stream_key, get_gateway_settings().stream.redis_stream_initial_ttl)
|
|
268
|
+
pipe.get(cancel_key)
|
|
267
269
|
try:
|
|
268
|
-
stale, _, _ = await pipe.execute()
|
|
270
|
+
stale, _, _, tombstone = await pipe.execute()
|
|
269
271
|
except RedisError:
|
|
270
272
|
# Claimed + registered but the stream couldn't be seeded: undo both so a retry re-runs.
|
|
271
273
|
with contextlib.suppress(RedisError):
|
|
272
274
|
await self._idempotency.release(task_id)
|
|
273
275
|
await self._registry.unregister(task_id)
|
|
274
276
|
return gateway_dto_pb2.StartStreamResponse(accepted=False, task_id=task_id)
|
|
277
|
+
# A pre-start cancel binds the task unless another mission sent it (a mission-less one binds).
|
|
278
|
+
if tombstone and tombstone.decode() != request.mission_id:
|
|
279
|
+
# Compare-and-delete: a cancel the owner sends meanwhile overwrites the value and is kept.
|
|
280
|
+
with contextlib.suppress(RedisError):
|
|
281
|
+
dropped = await self._redis_client.eval(
|
|
282
|
+
"if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0",
|
|
283
|
+
[cancel_key],
|
|
284
|
+
[tombstone.decode()],
|
|
285
|
+
)
|
|
286
|
+
# TODO(validate): CANCEL-FOREIGN a cancel tombstone from another mission is dropped at StartStream
|
|
287
|
+
logger.warning(
|
|
288
|
+
"[VALIDATE CANCEL-FOREIGN] cancel tombstone from mission=%r dropped=%s",
|
|
289
|
+
tombstone,
|
|
290
|
+
bool(dropped),
|
|
291
|
+
extra=log_extra,
|
|
292
|
+
)
|
|
275
293
|
timer.mark("xadd_stream_start")
|
|
276
294
|
# TODO(validate): SEED-TTL stream.start seed carries a TTL and drops stale keys
|
|
277
295
|
logger.info("[VALIDATE SEED-TTL] stream.start seeded with TTL; stale keys dropped=%s", stale, extra=log_extra)
|
|
@@ -428,9 +446,10 @@ class GatewayServicer:
|
|
|
428
446
|
|
|
429
447
|
if first_msg.data and len(first_msg.data.fields) > 0:
|
|
430
448
|
# TODO(validate): NO-INPUT follow-up data is dropped when there is no input consumer
|
|
431
|
-
logger.
|
|
432
|
-
"[VALIDATE NO-INPUT] Stream first message data
|
|
433
|
-
first_msg.data,
|
|
449
|
+
logger.info(
|
|
450
|
+
"[VALIDATE NO-INPUT] Stream first message data is not used yet; dropped bytes=%d keys=%s",
|
|
451
|
+
first_msg.data.ByteSize(),
|
|
452
|
+
list(first_msg.data.fields),
|
|
434
453
|
extra={"task_id": task_id},
|
|
435
454
|
)
|
|
436
455
|
|
|
@@ -470,9 +489,10 @@ class GatewayServicer:
|
|
|
470
489
|
break
|
|
471
490
|
if msg.data and len(msg.data.fields) > 0:
|
|
472
491
|
# TODO(validate): NO-INPUT follow-up data is dropped when there is no input consumer
|
|
473
|
-
logger.
|
|
474
|
-
"[VALIDATE NO-INPUT] Stream follow-up
|
|
475
|
-
msg.data,
|
|
492
|
+
logger.info(
|
|
493
|
+
"[VALIDATE NO-INPUT] Stream follow-up input is not used yet; dropped bytes=%d keys=%s",
|
|
494
|
+
msg.data.ByteSize(),
|
|
495
|
+
list(msg.data.fields),
|
|
476
496
|
extra={"task_id": task_id},
|
|
477
497
|
)
|
|
478
498
|
except asyncio.CancelledError:
|
|
@@ -480,7 +500,7 @@ class GatewayServicer:
|
|
|
480
500
|
except Exception:
|
|
481
501
|
logger.exception("Peer upstream reader error: task_id=%s", task_id)
|
|
482
502
|
|
|
483
|
-
async def SendSignal(
|
|
503
|
+
async def SendSignal( # ruff: ignore[complex-structure, too-many-return-statements]
|
|
484
504
|
self,
|
|
485
505
|
request: Any,
|
|
486
506
|
context: grpc.aio.ServicerContext, # ruff: ignore[unused-method-argument]
|
|
@@ -555,22 +575,46 @@ class GatewayServicer:
|
|
|
555
575
|
timer.mark("validate_task_id")
|
|
556
576
|
last_mark = "validate_task_id"
|
|
557
577
|
|
|
558
|
-
# The task may run on any replica, or not be registered yet:
|
|
559
|
-
#
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
578
|
+
# The task may run on any replica, or not be registered yet: publish, and leave a
|
|
579
|
+
# durable tombstone (the caller's mission) the ModuleRunner checks around registration.
|
|
580
|
+
# A claimed task (``idem`` = ``{process}|{mission}``) only accepts its own mission's cancel;
|
|
581
|
+
# a caller without ``x-mission-id`` is let through and logged (CANCEL-NO-MISSION).
|
|
582
|
+
mission_id = RequestContext.current().get("mission_id", "")
|
|
583
|
+
idem_exists, receivers = await self._redis_client.eval(
|
|
584
|
+
"local owner = redis.call('GET', KEYS[1])\n"
|
|
585
|
+
"local sep = owner and string.find(owner, '|', 1, true)\n"
|
|
586
|
+
"if sep and ARGV[1] ~= '' and string.sub(owner, sep + 1) ~= ARGV[1] then\n"
|
|
587
|
+
" return {-1, 0}\n"
|
|
588
|
+
"end\n"
|
|
589
|
+
"redis.call('SET', KEYS[2], ARGV[1], 'EX', tonumber(ARGV[2]))\n"
|
|
590
|
+
"return {owner and 1 or 0, redis.call('PUBLISH', KEYS[3], ARGV[3])}",
|
|
591
|
+
[f"idem:{task_id}", f"cancel:{task_id}", f"signal_ch:{task_id}"],
|
|
592
|
+
[
|
|
593
|
+
mission_id,
|
|
594
|
+
str(get_gateway_settings().stream.redis_stream_initial_ttl),
|
|
595
|
+
json.dumps({"action": "cancel", "task_id": task_id, "published_at_ns": time.time_ns()}),
|
|
596
|
+
],
|
|
570
597
|
)
|
|
571
|
-
idem_exists, _, receivers = await pipe.execute()
|
|
572
598
|
timer.mark("redis_publish")
|
|
573
599
|
last_mark = "redis_publish"
|
|
600
|
+
if idem_exists == -1:
|
|
601
|
+
# TODO(validate): CANCEL-OWNER a cancel from another mission is refused and leaves no tombstone
|
|
602
|
+
logger.warning(
|
|
603
|
+
"[VALIDATE CANCEL-OWNER] SendSignal_failed: failure=Forbidden caller_mission=%r "
|
|
604
|
+
"does not own the task; nothing published signal=%s task_id=%s",
|
|
605
|
+
mission_id,
|
|
606
|
+
signal,
|
|
607
|
+
task_id,
|
|
608
|
+
extra=log_extra,
|
|
609
|
+
)
|
|
610
|
+
return gateway_dto_pb2.SendSignalResponse(success=False, task_id=task_id)
|
|
611
|
+
if not mission_id:
|
|
612
|
+
# TODO(validate): CANCEL-NO-MISSION a cancel without x-mission-id skips the owner check
|
|
613
|
+
logger.warning(
|
|
614
|
+
"[VALIDATE CANCEL-NO-MISSION] cancel without x-mission-id accepted unchecked task_id=%s",
|
|
615
|
+
task_id,
|
|
616
|
+
extra=log_extra,
|
|
617
|
+
)
|
|
574
618
|
# TODO(validate): SIGNAL-PUB SendSignal publishes without the local registry
|
|
575
619
|
logger.info(
|
|
576
620
|
"[VALIDATE SIGNAL-PUB] SendSignal published: signal=%s idem_exists=%s receivers=%s task_id=%s",
|
|
@@ -582,7 +626,8 @@ class GatewayServicer:
|
|
|
582
626
|
)
|
|
583
627
|
if not idem_exists:
|
|
584
628
|
logger.warning(
|
|
585
|
-
"[gateway] SendSignal_failed: failure=TaskNotFound at_step=%s elapsed_ms=%.2f signal=%s
|
|
629
|
+
"[gateway] SendSignal_failed: failure=TaskNotFound at_step=%s elapsed_ms=%.2f signal=%s "
|
|
630
|
+
"task_id=%s; tombstone kept for a later StartStream",
|
|
586
631
|
last_mark,
|
|
587
632
|
timer.elapsed_now_ms(),
|
|
588
633
|
signal,
|
|
@@ -600,6 +645,19 @@ class GatewayServicer:
|
|
|
600
645
|
)
|
|
601
646
|
return gateway_dto_pb2.SendSignalResponse(success=True, task_id=task_id)
|
|
602
647
|
|
|
648
|
+
except RedisError as exc:
|
|
649
|
+
# The response has no reason field: the log is the only place the cause is recorded.
|
|
650
|
+
logger.error(
|
|
651
|
+
"[gateway] SendSignal_failed: failure=RedisUnavailable (%s) at_step=%s elapsed_ms=%.2f signal=%s "
|
|
652
|
+
"task_id=%s; signal not delivered",
|
|
653
|
+
type(exc).__name__,
|
|
654
|
+
last_mark,
|
|
655
|
+
timer.elapsed_now_ms(),
|
|
656
|
+
signal,
|
|
657
|
+
task_id,
|
|
658
|
+
extra=log_extra,
|
|
659
|
+
)
|
|
660
|
+
return gateway_dto_pb2.SendSignalResponse(success=False, task_id=task_id)
|
|
603
661
|
except Exception as exc:
|
|
604
662
|
logger.warning(
|
|
605
663
|
"[gateway] SendSignal_failed: failure=%s at_step=%s elapsed_ms=%.2f signal=%s task_id=%s",
|
|
@@ -1114,9 +1172,10 @@ class GatewayServicer:
|
|
|
1114
1172
|
first = False
|
|
1115
1173
|
continue
|
|
1116
1174
|
# TODO(validate): NO-INPUT follow-up data is dropped when there is no input consumer
|
|
1117
|
-
logger.
|
|
1118
|
-
"[VALIDATE NO-INPUT] Dial-back follow-up
|
|
1119
|
-
upstream.data,
|
|
1175
|
+
logger.info(
|
|
1176
|
+
"[VALIDATE NO-INPUT] Dial-back follow-up input is not used yet; dropped bytes=%d keys=%s",
|
|
1177
|
+
upstream.data.ByteSize(),
|
|
1178
|
+
list(upstream.data.fields),
|
|
1120
1179
|
extra=log_extra,
|
|
1121
1180
|
)
|
|
1122
1181
|
except grpc.aio.AioRpcError as exc:
|
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import inspect
|
|
6
7
|
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
9
|
+
import celpy
|
|
8
10
|
import grpc
|
|
9
11
|
import grpc.aio
|
|
10
12
|
import protovalidate
|
|
11
13
|
|
|
14
|
+
from digitalkin.logger import logger
|
|
15
|
+
from digitalkin.models.settings.server.server import get_server_settings
|
|
16
|
+
|
|
12
17
|
if TYPE_CHECKING:
|
|
13
18
|
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
14
19
|
|
|
@@ -16,6 +21,8 @@ if TYPE_CHECKING:
|
|
|
16
21
|
class ValidationServerInterceptor(grpc.aio.ServerInterceptor):
|
|
17
22
|
"""Reject a unary request that breaks its ``buf.validate`` rules with ``INVALID_ARGUMENT``.
|
|
18
23
|
|
|
24
|
+
A validator failure (rule compilation or evaluation) aborts with ``INTERNAL``.
|
|
25
|
+
|
|
19
26
|
Request-streaming RPCs pass through untouched: the gateway ``Stream`` reports
|
|
20
27
|
failures in-band as ``stream.error`` and never aborts. Synchronous handlers
|
|
21
28
|
(e.g. the ``grpc_health`` ``HealthServicer``) also pass through untouched:
|
|
@@ -23,7 +30,7 @@ class ValidationServerInterceptor(grpc.aio.ServerInterceptor):
|
|
|
23
30
|
would bypass by awaiting their plain return value.
|
|
24
31
|
"""
|
|
25
32
|
|
|
26
|
-
async def intercept_service( # ruff: ignore[no-self-use]
|
|
33
|
+
async def intercept_service( # ruff: ignore[no-self-use, complex-structure]
|
|
27
34
|
self,
|
|
28
35
|
continuation: Callable[[grpc.HandlerCallDetails], Awaitable[grpc.RpcMethodHandler[Any, Any] | None]],
|
|
29
36
|
handler_call_details: grpc.HandlerCallDetails,
|
|
@@ -46,13 +53,21 @@ class ValidationServerInterceptor(grpc.aio.ServerInterceptor):
|
|
|
46
53
|
|
|
47
54
|
async def _validate(request: Any, context: grpc.aio.ServicerContext) -> None:
|
|
48
55
|
try:
|
|
49
|
-
|
|
56
|
+
# Validation costs ~0.3 ms/KiB of CPU: large requests leave the event loop.
|
|
57
|
+
if request.ByteSize() > get_server_settings().grpc.validation_offload_bytes:
|
|
58
|
+
await asyncio.to_thread(protovalidate.validate, request)
|
|
59
|
+
else:
|
|
60
|
+
protovalidate.validate(request)
|
|
50
61
|
except protovalidate.ValidationError as e:
|
|
51
62
|
details = "; ".join(
|
|
52
63
|
f"{'.'.join(el.field_name for el in v.proto.field.elements) or v.proto.rule_id}: {v.proto.message}"
|
|
53
|
-
for v in e.violations
|
|
64
|
+
for v in e.violations[:10]
|
|
54
65
|
)
|
|
55
66
|
await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"invalid {request.DESCRIPTOR.name}: {details}")
|
|
67
|
+
except (protovalidate.CompilationError, celpy.CELEvalError):
|
|
68
|
+
# A rule that fails to compile or evaluate is a server defect; keep its text out of the reply.
|
|
69
|
+
logger.exception("Request validation could not run for %s", request.DESCRIPTOR.full_name)
|
|
70
|
+
await context.abort(grpc.StatusCode.INTERNAL, f"could not validate {request.DESCRIPTOR.name}")
|
|
56
71
|
|
|
57
72
|
if handler.response_streaming:
|
|
58
73
|
stream_behavior: Any = handler.unary_stream
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/grpc_servers/module_server.py
RENAMED
|
@@ -134,8 +134,6 @@ class ModuleServer(BaseServer):
|
|
|
134
134
|
service_descriptor=gateway_service_pb2.DESCRIPTOR, # type: ignore[arg-type] # protobuf FileDescriptor; see above
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
-
logger.info("GatewayServicer + ModuleRunner registered (Redis: %s)", redis_url)
|
|
138
|
-
|
|
139
137
|
listener = SharedRedisListener.singleton_or_none()
|
|
140
138
|
if listener is not None:
|
|
141
139
|
listener.set_cache_invalidator(self._handle_cache_invalidation)
|
|
@@ -251,7 +251,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
251
251
|
return results
|
|
252
252
|
|
|
253
253
|
@classmethod
|
|
254
|
-
async def _refresh_annotation(cls, annotation:
|
|
254
|
+
async def _refresh_annotation(cls, annotation: Any) -> "type | None":
|
|
255
255
|
"""Refresh dynamic fields in an annotation, handling Unions and single models.
|
|
256
256
|
|
|
257
257
|
Args:
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/models/settings/server/grpc.py
RENAMED
|
@@ -20,6 +20,7 @@ class GrpcServerSettings(BaseSettings):
|
|
|
20
20
|
max_send_message_length (NonNegativeInt): Maximum message size the server can send, in bytes.
|
|
21
21
|
max_pings_without_data (NonNegativeInt): Maximum number of pings the server allows without receiving any data.
|
|
22
22
|
keepalive_permit_without_calls (bool): Allow clients to send keepalive pings even when there are no active RPCs.
|
|
23
|
+
validation_offload_bytes (NonNegativeInt): Request size above which ``buf.validate`` runs in a worker thread.
|
|
23
24
|
|
|
24
25
|
"""
|
|
25
26
|
|
|
@@ -74,6 +75,12 @@ class GrpcServerSettings(BaseSettings):
|
|
|
74
75
|
"alive through proxies and detecting dead clients.",
|
|
75
76
|
alias="SERVER_GRPC_OPTIONS_KEEPALIVE_PERMIT_WITHOUT_CALLS",
|
|
76
77
|
)
|
|
78
|
+
validation_offload_bytes: NonNegativeInt = Field(
|
|
79
|
+
96 * 1024,
|
|
80
|
+
description="Request size above which buf.validate runs in a worker thread instead of on the event "
|
|
81
|
+
"loop. Validation costs ~0.3 ms/KiB, so the default keeps inline validation under ~30 ms.",
|
|
82
|
+
alias="SERVER_GRPC_VALIDATION_OFFLOAD_BYTES",
|
|
83
|
+
)
|
|
77
84
|
|
|
78
85
|
@property
|
|
79
86
|
def options(self) -> list[tuple[str, Any]]:
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/default_setup.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""In-memory setup strategy mirroring the SetupService protocol."""
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
3
|
+
import datetime as dt
|
|
4
4
|
import secrets
|
|
5
5
|
import string
|
|
6
6
|
from typing import Any
|
|
@@ -187,7 +187,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
187
187
|
documentation=setup_dict.get("documentation") or "",
|
|
188
188
|
content=setup_dict.get("content") or {},
|
|
189
189
|
structure=JsonStructure.clip(setup_dict.get("structure") or {}),
|
|
190
|
-
creation_date=
|
|
190
|
+
creation_date=dt.datetime.now(dt.timezone.utc),
|
|
191
191
|
),
|
|
192
192
|
)
|
|
193
193
|
except ValidationError as e:
|
|
@@ -239,7 +239,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
239
239
|
documentation=setup_dict.get("documentation") or "",
|
|
240
240
|
content=content,
|
|
241
241
|
structure=JsonStructure.clip(setup_dict.get("structure") or {}),
|
|
242
|
-
creation_date=
|
|
242
|
+
creation_date=dt.datetime.now(dt.timezone.utc),
|
|
243
243
|
)
|
|
244
244
|
history.append(version)
|
|
245
245
|
if setup_dict.get("set_as_current", True):
|
|
@@ -315,7 +315,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
315
315
|
documentation=setup_dict.get("documentation") or "",
|
|
316
316
|
content=content,
|
|
317
317
|
structure=JsonStructure.clip(setup_dict.get("structure") or {}),
|
|
318
|
-
creation_date=
|
|
318
|
+
creation_date=dt.datetime.now(dt.timezone.utc),
|
|
319
319
|
)
|
|
320
320
|
self.versions.setdefault(setup.id, [setup.current_setup_version]).append(version)
|
|
321
321
|
if setup_dict.get("set_as_current"):
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/setup/setup_strategy.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""This module contains the abstract base class for setup strategies."""
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
3
|
+
import datetime as dt
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ class SetupVersionData(BaseModel):
|
|
|
31
31
|
documentation: str = ""
|
|
32
32
|
content: dict[str, Any]
|
|
33
33
|
structure: dict[str, str] | None = None
|
|
34
|
-
creation_date:
|
|
34
|
+
creation_date: dt.datetime = Field(validation_alias=AliasChoices("creation_date", "created_at"))
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class SetupVersionPage(BaseModel):
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/default_storage.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""This module implements the default storage strategy."""
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
3
|
+
import datetime as dt
|
|
4
4
|
import json
|
|
5
5
|
import tempfile
|
|
6
6
|
import uuid
|
|
@@ -37,7 +37,7 @@ class DefaultStorage(StorageStrategy):
|
|
|
37
37
|
Raises:
|
|
38
38
|
TypeError: If the object is not serializable
|
|
39
39
|
"""
|
|
40
|
-
if isinstance(o,
|
|
40
|
+
if isinstance(o, dt.datetime):
|
|
41
41
|
return o.isoformat()
|
|
42
42
|
msg = f"Type {o.__class__.__name__} not serializable"
|
|
43
43
|
raise TypeError(msg)
|
|
@@ -69,10 +69,8 @@ class DefaultStorage(StorageStrategy):
|
|
|
69
69
|
data=data_model,
|
|
70
70
|
data_type=DataType[rd["data_type"]],
|
|
71
71
|
visibility=Visibility[rd["visibility"]] if rd.get("visibility") else Visibility.UNSPECIFIED,
|
|
72
|
-
creation_date=
|
|
73
|
-
if rd.get("
|
|
74
|
-
else None,
|
|
75
|
-
update_date=datetime.datetime.fromisoformat(rd["update_date"]) if rd.get("update_date") else None,
|
|
72
|
+
creation_date=dt.datetime.fromisoformat(rd["creation_date"]) if rd.get("creation_date") else None,
|
|
73
|
+
update_date=dt.datetime.fromisoformat(rd["update_date"]) if rd.get("update_date") else None,
|
|
76
74
|
)
|
|
77
75
|
out[key] = rec
|
|
78
76
|
except Exception:
|
|
@@ -126,7 +124,7 @@ class DefaultStorage(StorageStrategy):
|
|
|
126
124
|
if key in self.storage:
|
|
127
125
|
msg = f"Document {key!r} already exists"
|
|
128
126
|
raise ValueError(msg)
|
|
129
|
-
now =
|
|
127
|
+
now = dt.datetime.now(dt.timezone.utc)
|
|
130
128
|
record.creation_date = now
|
|
131
129
|
record.update_date = now
|
|
132
130
|
record.storage_id = f"storage:{uuid.uuid4()}"
|
|
@@ -183,7 +181,7 @@ class DefaultStorage(StorageStrategy):
|
|
|
183
181
|
rec.data = data
|
|
184
182
|
if visibility is not Visibility.UNSPECIFIED:
|
|
185
183
|
rec.visibility = visibility
|
|
186
|
-
rec.update_date =
|
|
184
|
+
rec.update_date = dt.datetime.now(dt.timezone.utc)
|
|
187
185
|
self._save_to_file()
|
|
188
186
|
logger.debug("Modified %s", key)
|
|
189
187
|
return rec
|
{digitalkin-1.0.5.dev5 → digitalkin-1.0.5.dev6}/src/digitalkin/services/storage/storage_strategy.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""This module contains the abstract base class for storage strategies."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import datetime
|
|
4
|
+
import datetime as dt
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from typing import Any, Literal, TypeGuard
|
|
7
7
|
from uuid import uuid4
|
|
@@ -26,8 +26,8 @@ class StorageRecord(BaseModel):
|
|
|
26
26
|
description="Read-access scope of this record (UNSPECIFIED = storage-service default)",
|
|
27
27
|
)
|
|
28
28
|
data: BaseModel = Field(..., description="The typed payload of this record")
|
|
29
|
-
creation_date:
|
|
30
|
-
update_date:
|
|
29
|
+
creation_date: dt.datetime | None = Field(default=None, description="When this record was first created")
|
|
30
|
+
update_date: dt.datetime | None = Field(default=None, description="When this record was last modified")
|
|
31
31
|
storage_id: str = Field(
|
|
32
32
|
default="", description="Service-assigned `storage:<id>` for this record; empty until stored"
|
|
33
33
|
)
|