digitalkin 1.0.4.dev6__tar.gz → 1.0.4.dev7__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.4.dev6 → digitalkin-1.0.4.dev7}/PKG-INFO +2 -2
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/pyproject.toml +11 -11
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/__version__.py +1 -1
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/resilience/bulkhead.py +5 -6
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/redis/proto_streams.py +1 -1
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/_base_server.py +4 -4
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/module_server.py +12 -3
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/module_servicer.py +5 -9
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +3 -3
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +6 -6
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/filesystem_mixin.py +10 -5
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/models.py +28 -37
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/certificate.py +90 -0
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/client/__init__.py +1 -0
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/client/channel.py +68 -0
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/client/circuit_breaker.py +19 -0
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/client/client.py +55 -0
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/client/grpc.py +148 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/module.py +17 -2
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/profiling.py +1 -1
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/redis.py +5 -1
- digitalkin-1.0.4.dev7/src/digitalkin/models/settings/resilience.py +61 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/server/channel.py +23 -3
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/server/grpc.py +1 -1
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/server/server.py +4 -2
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/utils/channel.py +45 -7
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/filesystem/default_filesystem.py +6 -1
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/services_config.py +16 -4
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/development_mode_action.py +10 -18
- digitalkin-1.0.4.dev7/src/digitalkin/utils/env_manager.py +139 -0
- digitalkin-1.0.4.dev7/src/digitalkin/utils/env_template.py +233 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin.egg-info/PKG-INFO +2 -2
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin.egg-info/SOURCES.txt +8 -1
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin.egg-info/requires.txt +1 -1
- digitalkin-1.0.4.dev6/src/digitalkin/models/settings/grpc_client.py +0 -127
- digitalkin-1.0.4.dev6/src/digitalkin/models/settings/resilience.py +0 -31
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/LICENSE +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/README.md +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/mock/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/mock/mock_pb2.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/server_async_insecure.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/server_async_secure.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/server_sync_insecure.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/base_server/server_sync_secure.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/echo_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/models/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/models/input.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/models/output.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/models/secret.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/models/setup.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/server.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/triggers/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/bench_module/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/modules/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/modules/archetype_with_tools_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/modules/cpu_intensive_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/modules/dynamic_setup_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/modules/minimal_llm_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/modules/text_transform_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/monitoring/tests/test_metrics.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/client.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/echo_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/models/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/models/input.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/models/output.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/models/secret.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/models/setup.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/server.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/triggers/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/redis_demo/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/services/filesystem_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/examples/services/storage_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/setup.cfg +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/agno_adapter.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/agui_tools.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/hitl.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/models.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/module_toolkit.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/base.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/chat_history.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/defaults.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/action.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/base.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/kins/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/kins/action.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/kins/kit.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/loader/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/loader/action.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/loader/kit.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/services/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/services/action.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/services/kit.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/tools/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/tools/action.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/registry/tools/kit.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/community/agno/toolkits/user_profile.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/common/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/common/factories.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/job_manager/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/profiling/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/profiling/step_timer.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/profiling/task_profiler.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/resilience/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/module_runner.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/redis/redis_client.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/task_executor.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/core/task_manager/task_session.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/gateway_servicer.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/interceptors/permission.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/interceptors/request_ids.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/stream_session.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/logger.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/agui_mixin.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/base_mixin.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/cost_mixin.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/file_history_mixin.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/logger_mixin.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/mixins/storage_mixin.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/core/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/core/job_manager_models.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/core/redis.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/core/task_monitor.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/events/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/events/agent_events.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/stream_error_codes.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/types.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/ag_ui.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/base_types.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/knowledge_files.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/loaded_tools.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/module_context.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/module_types.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/request_metadata.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/select_schema.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/setup_types.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/tool_cache.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/tool_reference.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/module/utility.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/services/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/services/cost.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/services/filesystem.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/services/registry.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/services/services.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/services/storage.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/gateway.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/log.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/queue.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/registry.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/server/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/server/servicer.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/task_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/settings/utils/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/utils/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/_base_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/archetype_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/tool_module.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/trigger_handler.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/triggers/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/py.typed +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/base_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/communication/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/communication/communication_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/communication/default_communication.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/communication/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/communication/grpc_communication.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/cost/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/cost/cost_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/cost/default_cost.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/cost/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/cost/grpc_cost.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/filesystem/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/filesystem/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/identity/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/identity/default_identity.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/identity/identity_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/registry/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/registry/default_registry.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/registry/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/registry/grpc_registry.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/registry/registry_models.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/registry/registry_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/secret/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/secret/default_secret.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/secret/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/secret/grpc_secret.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/secret/secret_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/services_models.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/setup/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/setup/default_setup.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/setup/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/setup/grpc_setup.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/setup/setup_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/storage/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/storage/default_storage.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/storage/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/storage/grpc_storage.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/storage/storage_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/task_manager/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/task_manager/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/user_profile/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/user_profile/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/__init__.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/arg_parser.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/conditional_schema.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/exceptions.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/json_structure.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/llm_ready_schema.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/package_discover.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/proto_utils.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/schema_splitter.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/utils/setup_content_validator.py +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin.egg-info/dependency_links.txt +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin.egg-info/top_level.txt +0 -0
- {digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/tests/test_exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: digitalkin
|
|
3
|
-
Version: 1.0.4.
|
|
3
|
+
Version: 1.0.4.dev7
|
|
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
|
|
@@ -453,7 +453,7 @@ Requires-Python: >=3.10
|
|
|
453
453
|
Description-Content-Type: text/markdown
|
|
454
454
|
License-File: LICENSE
|
|
455
455
|
Requires-Dist: ag-ui-protocol==0.1.21.dev1787220377
|
|
456
|
-
Requires-Dist: agentic-mesh-protocol==1.0.2
|
|
456
|
+
Requires-Dist: agentic-mesh-protocol==1.0.2
|
|
457
457
|
Requires-Dist: agno<3,>=2.8.0
|
|
458
458
|
Requires-Dist: anyio>=4.13.0
|
|
459
459
|
Requires-Dist: grpcio-health-checking==1.82.1
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
# which is how a nested run's output is attributed to its author. Pinned exactly
|
|
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
|
-
"agentic-mesh-protocol==1.0.2
|
|
34
|
+
"agentic-mesh-protocol==1.0.2",
|
|
35
35
|
"agno>=2.8.0,<3",
|
|
36
36
|
"anyio>=4.13.0",
|
|
37
37
|
"grpcio-health-checking==1.82.1",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"pydantic>=2.12.4",
|
|
42
42
|
"redis[hiredis]>=7.4.0,<9",
|
|
43
43
|
]
|
|
44
|
-
version = "1.0.4.
|
|
44
|
+
version = "1.0.4.dev7"
|
|
45
45
|
|
|
46
46
|
[project.optional-dependencies]
|
|
47
47
|
agno = [ "agno>=2.6" ]
|
|
@@ -208,15 +208,15 @@
|
|
|
208
208
|
]
|
|
209
209
|
|
|
210
210
|
ignore = [
|
|
211
|
-
"any-type",
|
|
212
|
-
"missing-trailing-comma",
|
|
213
|
-
"docstring-extraneous-exception",
|
|
214
|
-
"unused-import",
|
|
215
|
-
"invalid-function-name",
|
|
216
|
-
"import-outside-top-level",
|
|
217
|
-
"bad-dunder-method-name",
|
|
218
|
-
"hardcoded-password-string",
|
|
219
|
-
"suspicious-pickle-import",
|
|
211
|
+
"any-type", # Allow typing.Any — gRPC stubs, callbacks, and dynamic APIs require it
|
|
212
|
+
"missing-trailing-comma", # Disable because of formatter incompatibility
|
|
213
|
+
"docstring-extraneous-exception", # Allow extraneous-exception in docstring
|
|
214
|
+
"unused-import", # Allow unused imports — used for availability checks
|
|
215
|
+
"invalid-function-name", # Allow PascalCase methods — gRPC servicer convention
|
|
216
|
+
"import-outside-top-level", # Allow lazy imports — needed to break circular dependencies
|
|
217
|
+
"bad-dunder-method-name", # Allow __get_pydantic_core_schema__ — Pydantic dunder hook
|
|
218
|
+
"hardcoded-password-string", # Hardcoded-password
|
|
219
|
+
"suspicious-pickle-import", # Allow pickle import
|
|
220
220
|
]
|
|
221
221
|
|
|
222
222
|
fixable = [ "ALL" ]
|
|
@@ -15,13 +15,12 @@ Usage in ModuleContext or service wrapper::
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import asyncio
|
|
18
|
-
import os
|
|
19
18
|
from typing import ClassVar
|
|
20
19
|
|
|
21
20
|
from typing_extensions import Self
|
|
22
21
|
|
|
23
22
|
from digitalkin.core.exceptions import BulkheadFullError
|
|
24
|
-
from digitalkin.models.settings.resilience import get_bulkhead_settings
|
|
23
|
+
from digitalkin.models.settings.resilience import BulkheadServiceSettings, get_bulkhead_settings
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class Bulkhead:
|
|
@@ -62,10 +61,10 @@ class Bulkhead:
|
|
|
62
61
|
del cls._instances[oldest]
|
|
63
62
|
|
|
64
63
|
settings = get_bulkhead_settings()
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
max_concurrent =
|
|
64
|
+
# The override carries the service id in the variable name; the settings
|
|
65
|
+
# class builds that prefix itself, so nothing here touches the environment.
|
|
66
|
+
override = BulkheadServiceSettings(service_id).max
|
|
67
|
+
max_concurrent = override if override is not None else settings.default_max
|
|
69
68
|
|
|
70
69
|
inst = cls(
|
|
71
70
|
service_id=service_id,
|
|
@@ -43,7 +43,7 @@ class ProtoStreamReader:
|
|
|
43
43
|
"""Initialize proto stream reader.
|
|
44
44
|
|
|
45
45
|
Cursor TTL comes from ``GatewayStreamSettings.redis_cursor_ttl`` (env
|
|
46
|
-
``
|
|
46
|
+
``DIGITALKIN_GATEWAY_STREAM_REDIS_CURSOR_TTL``).
|
|
47
47
|
|
|
48
48
|
Args:
|
|
49
49
|
task_id: Unique task identifier.
|
|
@@ -35,6 +35,7 @@ from digitalkin.logger import logger
|
|
|
35
35
|
from digitalkin.models.grpc_servers.types import GrpcServer, ServiceDescriptor, T
|
|
36
36
|
from digitalkin.models.settings.server.server import get_server_settings
|
|
37
37
|
from digitalkin.models.settings.utils.channel import ControlFlow, SecurityMode
|
|
38
|
+
from digitalkin.utils.env_manager import EnvManager
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class BaseServer(abc.ABC):
|
|
@@ -257,10 +258,9 @@ class BaseServer(abc.ABC):
|
|
|
257
258
|
Raises:
|
|
258
259
|
SecurityError: If credentials are missing or unreadable.
|
|
259
260
|
"""
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
raise SecurityError(msg)
|
|
261
|
+
# Explicit SERVER_CHANNEL_CREDENTIALS__* paths, else the CERTIFICATE_CERT_VOLUME
|
|
262
|
+
# directory; the resolution itself raises when the key or certificate is absent.
|
|
263
|
+
creds = EnvManager.server_credentials()
|
|
264
264
|
|
|
265
265
|
try: # ruff: ignore[too-many-statements-in-try-clause]
|
|
266
266
|
if creds.key_path and creds.cert_path:
|
{digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/module_server.py
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Module gRPC server implementation for DigitalKin."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
from collections.abc import Sequence
|
|
5
4
|
from typing import TYPE_CHECKING, Any
|
|
6
5
|
|
|
@@ -15,9 +14,12 @@ from digitalkin.grpc_servers.gateway_servicer import GatewayServicer
|
|
|
15
14
|
from digitalkin.grpc_servers.module_servicer import ModuleServicer
|
|
16
15
|
from digitalkin.logger import logger
|
|
17
16
|
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
17
|
+
from digitalkin.models.services.services import ServicesMode
|
|
18
|
+
from digitalkin.models.settings.redis import get_redis_settings
|
|
18
19
|
from digitalkin.models.settings.server.server import get_server_settings
|
|
19
20
|
from digitalkin.modules._base_module import BaseModule
|
|
20
21
|
from digitalkin.services.registry import GrpcRegistry
|
|
22
|
+
from digitalkin.utils.env_manager import EnvManager
|
|
21
23
|
|
|
22
24
|
if TYPE_CHECKING:
|
|
23
25
|
from digitalkin.services.registry import RegistryStrategy
|
|
@@ -40,6 +42,11 @@ class ModuleServer(BaseServer):
|
|
|
40
42
|
) -> None:
|
|
41
43
|
"""Initialize the module server.
|
|
42
44
|
|
|
45
|
+
In remote mode an omitted ``client_config`` is built from the
|
|
46
|
+
environment, so a module no longer declares one to reach the registry
|
|
47
|
+
and the services provider. Local mode keeps it unset: there is no
|
|
48
|
+
registry to register with.
|
|
49
|
+
|
|
43
50
|
Args:
|
|
44
51
|
module_class: The module class to serve.
|
|
45
52
|
client_config: Client configuration for services and registry.
|
|
@@ -49,6 +56,8 @@ class ModuleServer(BaseServer):
|
|
|
49
56
|
|
|
50
57
|
super().__init__(interceptors=all_interceptors or None)
|
|
51
58
|
self.module_class = module_class
|
|
59
|
+
if client_config is None and EnvManager.services_mode() == ServicesMode.REMOTE:
|
|
60
|
+
client_config = EnvManager.client_config()
|
|
52
61
|
self.client_config = client_config
|
|
53
62
|
self.registry: RegistryStrategy | None = None
|
|
54
63
|
self.module_servicer: ModuleServicer | None = None
|
|
@@ -93,9 +102,9 @@ class ModuleServer(BaseServer):
|
|
|
93
102
|
dispatcher process, queue, or Redis stream for dispatch.
|
|
94
103
|
|
|
95
104
|
Raises:
|
|
96
|
-
RuntimeError: If DIGITALKIN_REDIS_URL
|
|
105
|
+
RuntimeError: If DIGITALKIN_REDIS_URL resolves to an empty URL.
|
|
97
106
|
"""
|
|
98
|
-
redis_url =
|
|
107
|
+
redis_url = get_redis_settings().pool.url.get_secret_value()
|
|
99
108
|
if not redis_url:
|
|
100
109
|
msg = "DIGITALKIN_REDIS_URL is required. The gateway needs Redis for stream persistence."
|
|
101
110
|
raise RuntimeError(msg)
|
{digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/module_servicer.py
RENAMED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
|
-
import os
|
|
6
5
|
import time
|
|
7
6
|
from argparse import ArgumentParser, Namespace
|
|
8
7
|
from collections.abc import Awaitable, Callable
|
|
@@ -28,6 +27,7 @@ from digitalkin.models.module.module import ModuleCodeModel
|
|
|
28
27
|
from digitalkin.models.module.setup_types import SetupModel
|
|
29
28
|
from digitalkin.models.services.services import ServicesMode
|
|
30
29
|
from digitalkin.models.settings.gateway import get_gateway_settings
|
|
30
|
+
from digitalkin.models.settings.redis import get_redis_settings
|
|
31
31
|
from digitalkin.models.settings.server.servicer import get_module_servicer_settings
|
|
32
32
|
from digitalkin.modules._base_module import BaseModule
|
|
33
33
|
from digitalkin.services.registry import GrpcRegistry, RegistryStrategy
|
|
@@ -37,6 +37,7 @@ from digitalkin.services.setup.setup_strategy import SetupStrategy, SetupVersion
|
|
|
37
37
|
from digitalkin.services.user_profile import DefaultUserProfile, GrpcUserProfile, UserProfileStrategy
|
|
38
38
|
from digitalkin.utils.arg_parser import ArgParser
|
|
39
39
|
from digitalkin.utils.development_mode_action import DevelopmentModeMappingAction
|
|
40
|
+
from digitalkin.utils.env_manager import EnvManager
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
@@ -55,9 +56,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
55
56
|
parser.add_argument(
|
|
56
57
|
"-d",
|
|
57
58
|
"--dev-mode",
|
|
58
|
-
env_var="SERVICE_MODE",
|
|
59
59
|
choices=ServicesMode.__members__,
|
|
60
|
-
default="local",
|
|
61
60
|
action=DevelopmentModeMappingAction,
|
|
62
61
|
dest="services_mode",
|
|
63
62
|
help="Define Module Service configurations for endpoints",
|
|
@@ -70,13 +69,13 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
70
69
|
module_class: The module type to serve.
|
|
71
70
|
|
|
72
71
|
Raises:
|
|
73
|
-
RuntimeError: If DIGITALKIN_REDIS_URL
|
|
72
|
+
RuntimeError: If DIGITALKIN_REDIS_URL resolves to an empty URL.
|
|
74
73
|
"""
|
|
75
74
|
super().__init__()
|
|
76
75
|
module_class.discover()
|
|
77
76
|
self.module_class = module_class
|
|
78
77
|
|
|
79
|
-
redis_url =
|
|
78
|
+
redis_url = get_redis_settings().pool.url.get_secret_value()
|
|
80
79
|
if not redis_url:
|
|
81
80
|
msg = "DIGITALKIN_REDIS_URL is required"
|
|
82
81
|
raise RuntimeError(msg)
|
|
@@ -90,10 +89,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
90
89
|
# Access-control client gating the setup cache. Always built, always called (fail-closed).
|
|
91
90
|
if self.args.services_mode == ServicesMode.REMOTE:
|
|
92
91
|
up_cfg = self.module_class.services_config_params.get("user_profile") or {}
|
|
93
|
-
up_client_config = up_cfg.get("client_config")
|
|
94
|
-
if not up_client_config:
|
|
95
|
-
msg = "user_profile client_config is required for setup access control"
|
|
96
|
-
raise RuntimeError(msg)
|
|
92
|
+
up_client_config = up_cfg.get("client_config") or EnvManager.client_config()
|
|
97
93
|
self.user_profile = GrpcUserProfile("", "", "", up_client_config)
|
|
98
94
|
else:
|
|
99
95
|
self.user_profile = DefaultUserProfile("", "", "")
|
{digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/grpc_servers/utils/circuit_breaker.py
RENAMED
|
@@ -15,7 +15,7 @@ from typing import ClassVar
|
|
|
15
15
|
from digitalkin.grpc_servers.exceptions import CircuitOpenError
|
|
16
16
|
from digitalkin.logger import logger
|
|
17
17
|
from digitalkin.models.grpc_servers.circuit_breaker import CBState
|
|
18
|
-
from digitalkin.models.settings.
|
|
18
|
+
from digitalkin.models.settings.client.client import get_client_settings
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class CircuitBreaker:
|
|
@@ -45,7 +45,7 @@ class CircuitBreaker:
|
|
|
45
45
|
"""Get existing circuit breaker for a service or create one.
|
|
46
46
|
|
|
47
47
|
Thresholds come from ``CircuitBreakerSettings`` (env
|
|
48
|
-
``
|
|
48
|
+
``CLIENT_CIRCUIT_BREAKER_FAIL_MAX``, ``CLIENT_CIRCUIT_BREAKER_RESET_TIMEOUT``).
|
|
49
49
|
|
|
50
50
|
Args:
|
|
51
51
|
service_id: Service identifier.
|
|
@@ -54,7 +54,7 @@ class CircuitBreaker:
|
|
|
54
54
|
Circuit breaker for this service.
|
|
55
55
|
"""
|
|
56
56
|
if service_id not in cls._instances:
|
|
57
|
-
settings =
|
|
57
|
+
settings = get_client_settings().circuit_breaker
|
|
58
58
|
cls._instances[service_id] = cls(service_id, settings.fail_max, settings.reset_timeout)
|
|
59
59
|
return cls._instances[service_id]
|
|
60
60
|
|
|
@@ -21,7 +21,7 @@ from digitalkin.grpc_servers.interceptors.request_ids import RequestIdClientInte
|
|
|
21
21
|
from digitalkin.grpc_servers.utils.circuit_breaker import CircuitBreaker
|
|
22
22
|
from digitalkin.logger import logger
|
|
23
23
|
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
24
|
-
from digitalkin.models.settings.
|
|
24
|
+
from digitalkin.models.settings.client.client import get_client_settings
|
|
25
25
|
from digitalkin.models.settings.utils.channel import SecurityMode
|
|
26
26
|
|
|
27
27
|
|
|
@@ -240,7 +240,7 @@ class GrpcClientWrapper:
|
|
|
240
240
|
|
|
241
241
|
Retries on transient errors (UNAVAILABLE, INTERNAL, DEADLINE_EXCEEDED)
|
|
242
242
|
with exponential backoff. Retry count and backoff base are configurable
|
|
243
|
-
via
|
|
243
|
+
via CLIENT_MAX_RETRIES and CLIENT_BACKOFF_BASE_MS.
|
|
244
244
|
|
|
245
245
|
Arguments:
|
|
246
246
|
query_endpoint: rpc query name (e.g., "GetSetup", "CreateSetupVersion")
|
|
@@ -269,10 +269,10 @@ class GrpcClientWrapper:
|
|
|
269
269
|
error_msg = f"[gRPC-client:{self.service_name}.{query_endpoint}] {e}"
|
|
270
270
|
raise ServerError(error_msg) from e
|
|
271
271
|
|
|
272
|
-
|
|
273
|
-
eff_timeout = timeout if timeout is not None else
|
|
274
|
-
max_retries =
|
|
275
|
-
backoff_base_ms =
|
|
272
|
+
client_settings = get_client_settings()
|
|
273
|
+
eff_timeout = timeout if timeout is not None else client_settings.timeout
|
|
274
|
+
max_retries = client_settings.max_retries
|
|
275
|
+
backoff_base_ms = client_settings.backoff_base_ms
|
|
276
276
|
backoff_delays = tuple(backoff_base_ms / 1000 * (2**i) for i in range(max_retries))
|
|
277
277
|
last_error: grpc.RpcError | None = None
|
|
278
278
|
|
|
@@ -71,10 +71,10 @@ class FilesystemMixin:
|
|
|
71
71
|
)
|
|
72
72
|
])
|
|
73
73
|
except Exception:
|
|
74
|
-
logger.exception("Failed to upload file '%s' (%s)", name, content_type)
|
|
74
|
+
logger.exception("Failed to upload file '%s' (%s)", name, content_type, extra=context.session.current_ids())
|
|
75
75
|
return None
|
|
76
76
|
if failed or not records:
|
|
77
|
-
logger.error("Upload rejected for file '%s' (%s)", name, content_type)
|
|
77
|
+
logger.error("Upload rejected for file '%s' (%s)", name, content_type, extra=context.session.current_ids())
|
|
78
78
|
return None
|
|
79
79
|
return records[0]
|
|
80
80
|
|
|
@@ -122,7 +122,7 @@ class FilesystemMixin:
|
|
|
122
122
|
try:
|
|
123
123
|
records, _total = await context.filesystem.get_files(filters, include_content=include_content)
|
|
124
124
|
except Exception:
|
|
125
|
-
logger.exception("Failed to list files")
|
|
125
|
+
logger.exception("Failed to list files", extra=context.session.current_ids())
|
|
126
126
|
return []
|
|
127
127
|
return records
|
|
128
128
|
|
|
@@ -151,7 +151,7 @@ class FilesystemMixin:
|
|
|
151
151
|
permanent=permanent,
|
|
152
152
|
)
|
|
153
153
|
except Exception:
|
|
154
|
-
logger.exception("Failed to delete file '%s'", file_id)
|
|
154
|
+
logger.exception("Failed to delete file '%s'", file_id, extra=context.session.current_ids())
|
|
155
155
|
return False
|
|
156
156
|
return results.get(file_id, False)
|
|
157
157
|
|
|
@@ -181,7 +181,12 @@ class FilesystemMixin:
|
|
|
181
181
|
try:
|
|
182
182
|
record = await context.filesystem.get_file(file.id, context=file_context, include_content=False)
|
|
183
183
|
except Exception:
|
|
184
|
-
logger.warning(
|
|
184
|
+
logger.warning(
|
|
185
|
+
"Could not resolve file '%s'; keeping it as submitted",
|
|
186
|
+
file.id,
|
|
187
|
+
exc_info=True,
|
|
188
|
+
extra=context.session.current_ids(),
|
|
189
|
+
)
|
|
185
190
|
resolved.append(file)
|
|
186
191
|
continue
|
|
187
192
|
resolved.append(
|
{digitalkin-1.0.4.dev6 → digitalkin-1.0.4.dev7}/src/digitalkin/models/grpc_servers/models.py
RENAMED
|
@@ -1,43 +1,15 @@
|
|
|
1
1
|
"""Data models for gRPC server configurations."""
|
|
2
2
|
|
|
3
|
-
from enum import Enum
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
7
|
-
import grpc
|
|
8
6
|
from pydantic import BaseModel, Field, ValidationInfo, field_validator
|
|
9
7
|
|
|
10
8
|
from digitalkin.grpc_servers.exceptions import ConfigurationError, SecurityError
|
|
11
|
-
from digitalkin.models.settings.
|
|
12
|
-
from digitalkin.models.settings.utils.channel import ControlFlow, SecurityMode
|
|
9
|
+
from digitalkin.models.settings.client.client import get_client_settings
|
|
10
|
+
from digitalkin.models.settings.utils.channel import ControlFlow, GrpcCompression, SecurityMode
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
class GrpcCompression(str, Enum):
|
|
16
|
-
"""gRPC compression algorithm.
|
|
17
|
-
|
|
18
|
-
Attributes:
|
|
19
|
-
NONE: No compression
|
|
20
|
-
GZIP: Gzip compression
|
|
21
|
-
DEFLATE: Deflate compression
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
NONE = "none"
|
|
25
|
-
GZIP = "gzip"
|
|
26
|
-
DEFLATE = "deflate"
|
|
27
|
-
|
|
28
|
-
def to_grpc(self) -> grpc.Compression:
|
|
29
|
-
"""Convert to grpc.Compression enum.
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
The corresponding grpc.Compression value.
|
|
33
|
-
"""
|
|
34
|
-
match self:
|
|
35
|
-
case GrpcCompression.NONE:
|
|
36
|
-
return grpc.Compression.NoCompression
|
|
37
|
-
case GrpcCompression.GZIP:
|
|
38
|
-
return grpc.Compression.Gzip
|
|
39
|
-
case GrpcCompression.DEFLATE:
|
|
40
|
-
return grpc.Compression.Deflate
|
|
12
|
+
__all__ = ["ChannelConfig", "ClientConfig", "ClientCredentials", "GrpcCompression", "RetryPolicy"]
|
|
41
13
|
|
|
42
14
|
|
|
43
15
|
class RetryPolicy(BaseModel):
|
|
@@ -82,9 +54,9 @@ class RetryPolicy(BaseModel):
|
|
|
82
54
|
"""Build a retry policy with backoff values sourced from the environment.
|
|
83
55
|
|
|
84
56
|
Returns:
|
|
85
|
-
Retry policy populated from ``
|
|
57
|
+
Retry policy populated from ``GrpcClientRetrySettings``.
|
|
86
58
|
"""
|
|
87
|
-
settings =
|
|
59
|
+
settings = get_client_settings().retry
|
|
88
60
|
return cls(
|
|
89
61
|
max_attempts=settings.max_attempts,
|
|
90
62
|
initial_backoff=settings.initial_backoff,
|
|
@@ -205,9 +177,13 @@ class ChannelConfig(BaseModel):
|
|
|
205
177
|
class ClientConfig(ChannelConfig):
|
|
206
178
|
"""Base configuration for gRPC clients.
|
|
207
179
|
|
|
180
|
+
Every default is sourced from ``ClientSettings`` (env prefixes ``CLIENT_``,
|
|
181
|
+
``CLIENT_CHANNEL_``, ``CLIENT_GRPC_``), so a deployment configures its
|
|
182
|
+
clients through the environment and only overrides per-target values here.
|
|
183
|
+
|
|
208
184
|
Attributes:
|
|
209
|
-
host: Host address
|
|
210
|
-
port: Port
|
|
185
|
+
host: Host address the client dials
|
|
186
|
+
port: Port the client dials
|
|
211
187
|
mode: Client operation mode (sync/async)
|
|
212
188
|
security: Security mode (secure/insecure)
|
|
213
189
|
credentials: Client credentials for secure mode
|
|
@@ -216,13 +192,28 @@ class ClientConfig(ChannelConfig):
|
|
|
216
192
|
compression: gRPC compression algorithm for channel-level compression
|
|
217
193
|
"""
|
|
218
194
|
|
|
195
|
+
host: str = Field(
|
|
196
|
+
default_factory=lambda: get_client_settings().channel.host,
|
|
197
|
+
description="Host address the client dials",
|
|
198
|
+
)
|
|
199
|
+
port: int = Field(default_factory=lambda: get_client_settings().channel.port, description="Port the client dials")
|
|
200
|
+
mode: ControlFlow = Field(
|
|
201
|
+
default_factory=lambda: get_client_settings().channel.communication_mode,
|
|
202
|
+
description="Client operation mode (sync/async)",
|
|
203
|
+
)
|
|
204
|
+
security: SecurityMode = Field(
|
|
205
|
+
default_factory=lambda: get_client_settings().channel.security,
|
|
206
|
+
description="Security mode (secure/insecure)",
|
|
207
|
+
)
|
|
219
208
|
credentials: ClientCredentials | None = Field(None, description="Client credentials for secure mode")
|
|
220
209
|
retry_policy: RetryPolicy = Field(
|
|
221
210
|
default_factory=RetryPolicy.from_settings, description="Retry policy for failed RPCs"
|
|
222
211
|
)
|
|
223
|
-
compression: GrpcCompression = Field(
|
|
212
|
+
compression: GrpcCompression = Field(
|
|
213
|
+
default_factory=lambda: get_client_settings().grpc.compression, description="gRPC compression algorithm"
|
|
214
|
+
)
|
|
224
215
|
channel_options: list[tuple[str, Any]] = Field(
|
|
225
|
-
default_factory=lambda:
|
|
216
|
+
default_factory=lambda: get_client_settings().grpc.options,
|
|
226
217
|
description="Resilient gRPC channel options with DNS re-resolution, keepalive, and retries",
|
|
227
218
|
)
|
|
228
219
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""TLS/mTLS certificate volume settings."""
|
|
2
|
+
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
|
+
|
|
9
|
+
from digitalkin.grpc_servers.exceptions import SecurityError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CertificateSettings(BaseSettings):
|
|
13
|
+
"""Directories holding the certificates a module serves and dials with.
|
|
14
|
+
|
|
15
|
+
Files follow the deployment convention: ``server.key`` / ``server.crt`` for
|
|
16
|
+
the module's own server, ``client.key`` / ``client.crt`` for its outgoing
|
|
17
|
+
calls, and ``ca.crt`` for the root of trust in both directories.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
cert_volume (Path): Directory holding the module's server certificates.
|
|
21
|
+
services_provider_cert_volume (Path): Directory holding the client certificates
|
|
22
|
+
used to dial the services provider.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
model_config = SettingsConfigDict(env_prefix="CERTIFICATE_", case_sensitive=False)
|
|
27
|
+
|
|
28
|
+
cert_volume: Path = Field(
|
|
29
|
+
default=Path("/certificates"),
|
|
30
|
+
description="Directory where the module's own server certificates are stored",
|
|
31
|
+
)
|
|
32
|
+
services_provider_cert_volume: Path = Field(
|
|
33
|
+
default=Path("/certificates"),
|
|
34
|
+
description="Directory where the services-provider client certificates are stored",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def get_server_certificate_paths(self, *, mtls: bool = False) -> tuple[Path, Path, Path | None]:
|
|
38
|
+
"""Resolve the module server's key, certificate and root CA.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
mtls: Return the CA certificate so incoming clients can be authenticated.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (server key, server certificate, root CA or None).
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
SecurityError: If the server key or certificate is missing.
|
|
48
|
+
"""
|
|
49
|
+
key_path = self.cert_volume / "server.key"
|
|
50
|
+
cert_path = self.cert_volume / "server.crt"
|
|
51
|
+
if not key_path.exists() or not cert_path.exists():
|
|
52
|
+
msg = f"Server key or certificate not found in {self.cert_volume}"
|
|
53
|
+
raise SecurityError(msg)
|
|
54
|
+
ca_path = self.cert_volume / "ca.crt"
|
|
55
|
+
return key_path, cert_path, (ca_path if mtls and ca_path.exists() else None)
|
|
56
|
+
|
|
57
|
+
def get_client_certificate_paths(self, *, mtls: bool = False) -> tuple[Path | None, Path | None, Path]:
|
|
58
|
+
"""Resolve the client key, certificate and root CA used to dial the services provider.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
mtls: Return the client key and certificate so the module can present them.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Tuple of (client key or None, client certificate or None, root CA).
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
SecurityError: If a required file is missing.
|
|
68
|
+
"""
|
|
69
|
+
key_path = self.services_provider_cert_volume / "client.key"
|
|
70
|
+
cert_path = self.services_provider_cert_volume / "client.crt"
|
|
71
|
+
ca_path = self.services_provider_cert_volume / "ca.crt"
|
|
72
|
+
if mtls and (not key_path.exists() or not cert_path.exists()):
|
|
73
|
+
msg = f"Missing client.key or client.crt in {self.services_provider_cert_volume}"
|
|
74
|
+
raise SecurityError(msg)
|
|
75
|
+
if not ca_path.exists():
|
|
76
|
+
msg = f"Missing ca.crt in {self.services_provider_cert_volume}"
|
|
77
|
+
raise SecurityError(msg)
|
|
78
|
+
return (key_path if mtls else None, cert_path if mtls else None, ca_path)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@lru_cache(maxsize=1)
|
|
82
|
+
def get_certificate_settings() -> CertificateSettings:
|
|
83
|
+
"""Process-wide ``CertificateSettings`` singleton.
|
|
84
|
+
|
|
85
|
+
Tests must call ``get_certificate_settings.cache_clear()`` after mutating env.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
The shared ``CertificateSettings`` instance.
|
|
89
|
+
"""
|
|
90
|
+
return CertificateSettings()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for client settings."""
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Client channel settings."""
|
|
2
|
+
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
|
|
5
|
+
from pydantic import Field, NonNegativeInt, model_validator
|
|
6
|
+
from pydantic_settings import SettingsConfigDict
|
|
7
|
+
|
|
8
|
+
from digitalkin.models.settings.utils.channel import BaseChannelSettings
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClientChannelSettings(BaseChannelSettings):
|
|
12
|
+
"""Settings for a client channel.
|
|
13
|
+
|
|
14
|
+
Overrides the bind-oriented defaults of ``BaseChannelSettings`` with dial
|
|
15
|
+
defaults: a client connects to a remote address instead of listening on
|
|
16
|
+
every interface.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
host (str): Host address the client dials.
|
|
20
|
+
port (NonNegativeInt): Port the client dials.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
model_config = SettingsConfigDict(
|
|
25
|
+
env_prefix="CLIENT_CHANNEL_",
|
|
26
|
+
env_nested_delimiter="__",
|
|
27
|
+
extra="forbid",
|
|
28
|
+
arbitrary_types_allowed=True,
|
|
29
|
+
validate_assignment=True,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
host: str = Field(
|
|
33
|
+
default="[::]",
|
|
34
|
+
description="Host address the client dials (the services provider)",
|
|
35
|
+
json_schema_extra={"env_required": True},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
port: NonNegativeInt = Field(
|
|
39
|
+
default=50051,
|
|
40
|
+
description="Port the client dials (the services provider)",
|
|
41
|
+
json_schema_extra={"env_required": True},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@model_validator(mode="after")
|
|
45
|
+
def validate_credentials(self) -> "ClientChannelSettings":
|
|
46
|
+
"""Accept secure mode without explicit credential paths.
|
|
47
|
+
|
|
48
|
+
Unlike the server, a client falls back to the ``CERTIFICATE_`` volume:
|
|
49
|
+
``EnvManager.client_config`` resolves the pair there and raises
|
|
50
|
+
``SecurityError`` when the root CA is missing, so secure mode still
|
|
51
|
+
fails closed.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
The validated settings.
|
|
55
|
+
"""
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@lru_cache(maxsize=1)
|
|
60
|
+
def get_client_channel_settings() -> ClientChannelSettings:
|
|
61
|
+
"""Process-wide ``ClientChannelSettings`` singleton.
|
|
62
|
+
|
|
63
|
+
Tests must call ``get_client_channel_settings.cache_clear()`` after mutating env.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The shared ``ClientChannelSettings`` instance.
|
|
67
|
+
"""
|
|
68
|
+
return ClientChannelSettings()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Client circuit-breaker settings."""
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CircuitBreakerSettings(BaseSettings):
|
|
8
|
+
"""Per-service circuit-breaker thresholds.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
fail_max (int): Consecutive failures before the circuit opens.
|
|
12
|
+
reset_timeout (float): Seconds the circuit stays open before a half-open probe.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
model_config = SettingsConfigDict(env_prefix="CLIENT_CIRCUIT_BREAKER_", case_sensitive=False)
|
|
17
|
+
|
|
18
|
+
fail_max: int = Field(default=5, description="Consecutive failures before the circuit opens.")
|
|
19
|
+
reset_timeout: float = Field(default=30.0, description="Seconds the circuit stays open before a half-open probe.")
|