digitalkin 1.0.3.dev1__tar.gz → 1.0.3.dev2__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.3.dev1 → digitalkin-1.0.3.dev2}/PKG-INFO +1 -1
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/pyproject.toml +1 -1
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/__version__.py +1 -1
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/loader/action.py +65 -32
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/__init__.py +8 -0
- digitalkin-1.0.3.dev2/src/digitalkin/models/module/loaded_tools.py +140 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/module_context.py +89 -5
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/tool_cache.py +70 -10
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/_base_module.py +9 -2
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin.egg-info/PKG-INFO +1 -1
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin.egg-info/SOURCES.txt +1 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/LICENSE +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/README.md +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/mock/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/mock/mock_pb2.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/server_async_insecure.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/server_async_secure.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/server_sync_insecure.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/server_sync_secure.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/echo_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/models/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/models/input.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/models/output.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/models/secret.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/models/setup.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/server.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/triggers/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/modules/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/modules/archetype_with_tools_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/modules/cpu_intensive_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/modules/dynamic_setup_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/modules/minimal_llm_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/modules/text_transform_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/monitoring/tests/test_metrics.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/client.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/echo_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/models/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/models/input.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/models/output.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/models/secret.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/models/setup.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/server.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/triggers/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/redis_demo/triggers/message_trigger.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/services/filesystem_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/services/storage_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/setup.cfg +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/agno_adapter.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/agui_tools.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/hitl.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/models.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/module_toolkit.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/base.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/chat_history.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/defaults.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/action.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/base.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/kins/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/kins/action.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/kins/kit.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/loader/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/loader/kit.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/services/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/services/action.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/services/kit.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/tools/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/tools/action.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/registry/tools/kit.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/community/agno/toolkits/user_profile.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/common/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/common/factories.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/job_manager/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/profiling/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/profiling/step_timer.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/profiling/task_profiler.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/resilience/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/resilience/bulkhead.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/module_runner.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/redis/redis_client.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/task_executor.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/core/task_manager/task_session.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/_base_server.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/gateway_servicer.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/interceptors/permission.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/interceptors/request_ids.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/module_server.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/module_servicer.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/stream_session.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/logger.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/agui_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/base_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/cost_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/file_history_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/logger_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/mixins/storage_mixin.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/core/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/core/job_manager_models.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/core/redis.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/core/task_monitor.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/events/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/events/agent_events.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/grpc_servers/models.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/grpc_servers/stream_error_codes.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/grpc_servers/types.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/ag_ui.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/base_types.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/module_types.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/request_metadata.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/select_schema.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/setup_types.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/tool_reference.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/utility.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/services/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/services/cost.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/services/registry.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/services/services.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/services/storage.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/gateway.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/grpc_client.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/log.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/profiling.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/queue.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/redis.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/registry.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/resilience.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/server/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/server/channel.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/server/grpc.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/server/server.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/server/servicer.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/task_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/utils/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/settings/utils/channel.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/utils/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/archetype_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/tool_module.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/trigger_handler.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/triggers/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/py.typed +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/base_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/communication/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/communication/communication_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/communication/default_communication.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/communication/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/communication/grpc_communication.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/cost/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/cost/cost_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/cost/default_cost.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/cost/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/cost/grpc_cost.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/filesystem/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/filesystem/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/identity/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/identity/default_identity.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/identity/identity_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/registry/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/registry/default_registry.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/registry/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/registry/grpc_registry.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/registry/registry_models.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/registry/registry_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/secret/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/secret/default_secret.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/secret/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/secret/grpc_secret.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/secret/secret_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/services_config.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/services_models.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/setup/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/setup/default_setup.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/setup/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/setup/grpc_setup.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/setup/setup_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/storage/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/storage/default_storage.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/storage/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/storage/grpc_storage.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/storage/storage_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/task_manager/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/task_manager/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/user_profile/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/user_profile/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/__init__.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/arg_parser.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/conditional_schema.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/development_mode_action.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/dynamic_schema.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/exceptions.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/llm_ready_schema.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/package_discover.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/proto_utils.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/schema_splitter.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/utils/setup_content_validator.py +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin.egg-info/dependency_links.txt +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin.egg-info/requires.txt +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin.egg-info/top_level.txt +0 -0
- {digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/tests/test_exceptions.py +0 -0
|
@@ -78,6 +78,62 @@ class LoadToolAction(LoadAction):
|
|
|
78
78
|
action: Literal["tool"] = "tool"
|
|
79
79
|
setup_id: str = Field(..., description="The tool's setup id (from a tools_manager search/get) to load.")
|
|
80
80
|
|
|
81
|
+
async def _duplicate_outcome(self, ctx: LoadActionCtx, module_id: str) -> LoadOutcome | None:
|
|
82
|
+
"""Resolve the two "already there" cases, or ``None`` if this is a genuinely new load.
|
|
83
|
+
|
|
84
|
+
Runs BEFORE ``resolve_tool``: ``module_id`` is already known from the registry lookup, so
|
|
85
|
+
a repeat load costs no schema fetch. Reads ``base_tools`` — what is callable right now —
|
|
86
|
+
which after rehydration also covers a tool loaded on an earlier turn of this mission.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
ctx: The load context carrying the live tool list and the module context.
|
|
90
|
+
module_id: The requested setup's module, from the registry lookup.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
An ``already_loaded`` success, a conflict failure, or ``None`` to continue loading.
|
|
94
|
+
"""
|
|
95
|
+
# Imported here, not at module top: ModuleToolkit requires the optional agno dependency at
|
|
96
|
+
# import time, while this module must stay importable without it (same convention as the
|
|
97
|
+
# rest of community.agno).
|
|
98
|
+
from digitalkin.community.agno.module_toolkit import ModuleToolkit
|
|
99
|
+
|
|
100
|
+
loaded = [tool for tool in ctx.base_tools if isinstance(tool, ModuleToolkit)]
|
|
101
|
+
existing = next((tool for tool in loaded if tool.tool_module_info.setup_id == self.setup_id), None)
|
|
102
|
+
if existing is not None:
|
|
103
|
+
# Re-persist rather than just report: the id is only durable if an earlier turn's write
|
|
104
|
+
# actually landed, and that write is fail-soft. Without this, one failed persist makes
|
|
105
|
+
# the tool re-loadable forever but never durable — the model keeps getting "already
|
|
106
|
+
# loaded" and the record never appears. The upsert is a no-op when it did land.
|
|
107
|
+
# Setup-declared tools are excluded: they need no mission record.
|
|
108
|
+
if self.setup_id not in ctx.context.tool_cache.declared:
|
|
109
|
+
await ctx.context.persist_loaded_tool(self.setup_id)
|
|
110
|
+
info = existing.tool_module_info
|
|
111
|
+
name = info.tool_name or info.module_name or info.slug
|
|
112
|
+
# Name the callable functions (they ARE callable right now) so an already-loaded result
|
|
113
|
+
# is as verifiable as a fresh load, instead of an empty list reading as "nothing to call".
|
|
114
|
+
callable_names = sorted({*existing.functions, *existing.async_functions})
|
|
115
|
+
listed = f" You can now call: {', '.join(callable_names)}." if callable_names else ""
|
|
116
|
+
return LoadOutcome(
|
|
117
|
+
ok=True,
|
|
118
|
+
status="already_loaded",
|
|
119
|
+
tool_name=name,
|
|
120
|
+
loaded_functions=callable_names,
|
|
121
|
+
message=f"'{name}' is already loaded; call it directly.{listed}",
|
|
122
|
+
)
|
|
123
|
+
# A *different* setup of an already-loaded module cannot rebind — the live binding wins
|
|
124
|
+
# server-side, so appending it would only add duplicate tool names and confirm a change that
|
|
125
|
+
# never takes effect. Refuse explicitly instead of lying.
|
|
126
|
+
conflict = next((tool for tool in loaded if tool.tool_module_info.module_id == module_id), None)
|
|
127
|
+
if conflict is not None:
|
|
128
|
+
return LoadOutcome(
|
|
129
|
+
ok=False,
|
|
130
|
+
message=(
|
|
131
|
+
f"could not load setup {self.setup_id}: its tool module is already loaded via setup "
|
|
132
|
+
f"{conflict.tool_module_info.setup_id}, whose configuration stays in effect"
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
return None
|
|
136
|
+
|
|
81
137
|
async def execute(self, ctx: LoadActionCtx) -> LoadOutcome: # noqa: C901, PLR0911 — each return is a distinct, LLM-readable outcome
|
|
82
138
|
"""Resolve the setup into a ModuleToolkit and append it to the live tool list.
|
|
83
139
|
|
|
@@ -125,7 +181,11 @@ class LoadToolAction(LoadAction):
|
|
|
125
181
|
),
|
|
126
182
|
)
|
|
127
183
|
|
|
128
|
-
|
|
184
|
+
duplicate = await self._duplicate_outcome(ctx, setup.module_id)
|
|
185
|
+
if duplicate is not None:
|
|
186
|
+
return duplicate
|
|
187
|
+
|
|
188
|
+
# Confirmed tool module, not already loaded — resolve it into a callable toolkit.
|
|
129
189
|
try:
|
|
130
190
|
info = await ctx.context.resolve_tool(self.setup_id)
|
|
131
191
|
except PermissionDeniedError:
|
|
@@ -140,42 +200,15 @@ class LoadToolAction(LoadAction):
|
|
|
140
200
|
ok=False, message=f"could not load setup {self.setup_id}: this tool module exposes no callable tools"
|
|
141
201
|
)
|
|
142
202
|
|
|
143
|
-
# Imported here, not at module top: ModuleToolkit requires the optional agno dependency at
|
|
144
|
-
# import time, while this module must stay importable without it (same convention as the
|
|
145
|
-
# rest of community.agno).
|
|
146
203
|
from digitalkin.community.agno.module_toolkit import ModuleToolkit
|
|
147
204
|
|
|
148
205
|
name = info.tool_name or info.module_name or info.slug
|
|
149
|
-
loaded = [tool for tool in ctx.base_tools if isinstance(tool, ModuleToolkit)]
|
|
150
|
-
# Idempotent: re-loading the exact same setup is a no-op success. Still name the callable
|
|
151
|
-
# functions (they ARE callable right now) so an already-loaded result is as verifiable as a
|
|
152
|
-
# fresh load, instead of an empty ``loaded_functions`` that reads as "nothing to call".
|
|
153
|
-
existing = next((tool for tool in loaded if tool.tool_module_info.setup_id == self.setup_id), None)
|
|
154
|
-
if existing is not None:
|
|
155
|
-
callable_names = sorted({*existing.functions, *existing.async_functions})
|
|
156
|
-
listed = f" You can now call: {', '.join(callable_names)}." if callable_names else ""
|
|
157
|
-
return LoadOutcome(
|
|
158
|
-
ok=True,
|
|
159
|
-
status="already_loaded",
|
|
160
|
-
tool_name=name,
|
|
161
|
-
loaded_functions=callable_names,
|
|
162
|
-
message=f"'{name}' is already loaded; call it directly.{listed}",
|
|
163
|
-
)
|
|
164
|
-
# A *different* setup of an already-loaded module cannot rebind — the live binding wins
|
|
165
|
-
# server-side, so appending it would only add duplicate tool names and confirm a change that
|
|
166
|
-
# never takes effect. Refuse explicitly instead of lying.
|
|
167
|
-
conflict = next((tool for tool in loaded if tool.tool_module_info.module_id == info.module_id), None)
|
|
168
|
-
if conflict is not None:
|
|
169
|
-
return LoadOutcome(
|
|
170
|
-
ok=False,
|
|
171
|
-
message=(
|
|
172
|
-
f"could not load setup {self.setup_id}: its tool module is already loaded via setup "
|
|
173
|
-
f"{conflict.tool_module_info.setup_id}, whose configuration stays in effect"
|
|
174
|
-
),
|
|
175
|
-
)
|
|
176
|
-
|
|
177
206
|
toolkit = ModuleToolkit(ctx.context, info)
|
|
178
207
|
ctx.base_tools.append(toolkit)
|
|
208
|
+
# Persist the id so the load outlives this turn. ``resolve_tool`` only reached the
|
|
209
|
+
# mission-scoped ``dynamic`` cache layer, which is rebuilt from scratch on the next
|
|
210
|
+
# user message; without this the tool would have to be re-loaded every turn.
|
|
211
|
+
await ctx.context.persist_loaded_tool(self.setup_id)
|
|
179
212
|
await ctx.notify("tool_loaded", {"setup_id": self.setup_id, "tool_name": name})
|
|
180
213
|
# Name the now-callable functions so the model can verify and call them directly.
|
|
181
214
|
callable_names = sorted({*toolkit.functions, *toolkit.async_functions})
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"""Module model exports. Import ag_ui types from ``digitalkin.models.module.ag_ui``."""
|
|
2
2
|
|
|
3
|
+
from digitalkin.models.module.loaded_tools import (
|
|
4
|
+
LOADED_TOOLS_STORAGE_CONFIG,
|
|
5
|
+
LoadedToolRecord,
|
|
6
|
+
LoadedToolStore,
|
|
7
|
+
)
|
|
3
8
|
from digitalkin.models.module.module_context import ModuleContext
|
|
4
9
|
from digitalkin.models.module.module_types import (
|
|
5
10
|
DataModel,
|
|
@@ -25,9 +30,12 @@ from digitalkin.models.module.utility import (
|
|
|
25
30
|
)
|
|
26
31
|
|
|
27
32
|
__all__ = [
|
|
33
|
+
"LOADED_TOOLS_STORAGE_CONFIG",
|
|
28
34
|
"DataModel",
|
|
29
35
|
"DataTrigger",
|
|
30
36
|
"EndOfStreamOutput",
|
|
37
|
+
"LoadedToolRecord",
|
|
38
|
+
"LoadedToolStore",
|
|
31
39
|
"ModuleContext",
|
|
32
40
|
"RequestMetadata",
|
|
33
41
|
"SelectSchema",
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Mission-scoped persistence for tools the agent loaded at runtime.
|
|
2
|
+
|
|
3
|
+
A dynamic tool load (``load_manager`` → :meth:`ModuleContext.resolve_tool`) has to
|
|
4
|
+
outlive the turn that made it: every user message builds a fresh module instance,
|
|
5
|
+
so the in-memory toolkit list and the ``dynamic`` layer of the
|
|
6
|
+
:class:`~digitalkin.models.module.tool_cache.ToolCache` are both gone by the next
|
|
7
|
+
message. This module owns the only thing that survives — the list of loaded
|
|
8
|
+
``setup_id``s, written to the mission-scoped ``loaded_tools`` storage collection.
|
|
9
|
+
|
|
10
|
+
Only ids are stored, never resolved schemas: rehydration re-runs ``resolve_tool``,
|
|
11
|
+
which re-checks authorization and picks up any schema change, so a tool revoked or
|
|
12
|
+
altered between two turns is handled correctly instead of being replayed from a
|
|
13
|
+
stale snapshot.
|
|
14
|
+
|
|
15
|
+
Modules that want runtime tool loading must register the collection::
|
|
16
|
+
|
|
17
|
+
services_config_params = {"storage": {"config": {**LOADED_TOOLS_STORAGE_CONFIG}}}
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import ClassVar
|
|
21
|
+
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
24
|
+
from digitalkin.logger import logger
|
|
25
|
+
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
26
|
+
|
|
27
|
+
_LOADED_TOOLS_COLLECTION = "loaded_tools"
|
|
28
|
+
|
|
29
|
+
# The storage service caps a list() at 100 records; ask for the cap rather than the
|
|
30
|
+
# default 20, so a heavily-loaded mission rehydrates completely.
|
|
31
|
+
_LIST_LIMIT = 100
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LoadedToolRecord(BaseModel):
|
|
35
|
+
"""A tool the agent loaded at runtime during this mission."""
|
|
36
|
+
|
|
37
|
+
setup_id: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
LOADED_TOOLS_STORAGE_CONFIG: dict[str, type[BaseModel]] = {_LOADED_TOOLS_COLLECTION: LoadedToolRecord}
|
|
41
|
+
"""Storage config fragment for the ``loaded_tools`` collection."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LoadedToolStore:
|
|
45
|
+
"""Storage wrapper for the mission-scoped ``loaded_tools`` collection.
|
|
46
|
+
|
|
47
|
+
Every method is fail-soft and returns instead of raising: a module that never
|
|
48
|
+
registered the collection (see :data:`LOADED_TOOLS_STORAGE_CONFIG`) must degrade
|
|
49
|
+
to the old turn-scoped behaviour, not crash the run through the HITL runner.
|
|
50
|
+
Registration is checked before each call so a module that does not opt into runtime
|
|
51
|
+
tool loading pays no storage round-trip per turn.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
COLLECTION: ClassVar[str] = _LOADED_TOOLS_COLLECTION
|
|
55
|
+
|
|
56
|
+
def __init__(self, storage: StorageStrategy) -> None:
|
|
57
|
+
"""Initialize the store.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
storage: The module's storage strategy. Records are written under the
|
|
61
|
+
default mission context, which is what scopes a load to one
|
|
62
|
+
conversation.
|
|
63
|
+
"""
|
|
64
|
+
self._storage = storage
|
|
65
|
+
|
|
66
|
+
async def save(self, setup_id: str) -> bool:
|
|
67
|
+
"""Record ``setup_id`` as loaded for this mission (idempotent).
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
setup_id: The loaded tool's registry setup id.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
``True`` if the id is now persisted, ``False`` if persistence is
|
|
74
|
+
unavailable — the caller keeps the tool for the current turn either way.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
if self.COLLECTION not in self._storage.config:
|
|
78
|
+
logger.warning(
|
|
79
|
+
"LoadedToolStore: collection '%s' is not registered on this module "
|
|
80
|
+
"(add LOADED_TOOLS_STORAGE_CONFIG to services_config_params); "
|
|
81
|
+
"tool '%s' will not survive the current turn",
|
|
82
|
+
self.COLLECTION,
|
|
83
|
+
setup_id,
|
|
84
|
+
)
|
|
85
|
+
return False
|
|
86
|
+
await self._storage.upsert(
|
|
87
|
+
collection=self.COLLECTION,
|
|
88
|
+
record_id=setup_id,
|
|
89
|
+
data=LoadedToolRecord(setup_id=setup_id).model_dump(),
|
|
90
|
+
)
|
|
91
|
+
except Exception:
|
|
92
|
+
logger.warning(
|
|
93
|
+
"LoadedToolStore: could not persist loaded tool '%s'; it will not survive this turn",
|
|
94
|
+
setup_id,
|
|
95
|
+
exc_info=True,
|
|
96
|
+
)
|
|
97
|
+
return False
|
|
98
|
+
logger.info("LoadedToolStore: persisted loaded tool '%s'", setup_id)
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
async def list_setup_ids(self) -> list[str]:
|
|
102
|
+
"""List the tool setup ids loaded so far in this mission.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
The persisted setup ids, or an empty list if none exist or the
|
|
106
|
+
collection is not registered.
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
# Checked first so a module that never opted into runtime tool loading pays no
|
|
110
|
+
# storage round-trip on every turn.
|
|
111
|
+
if self.COLLECTION not in self._storage.config:
|
|
112
|
+
return []
|
|
113
|
+
records = await self._storage.list(collection=self.COLLECTION, limit=_LIST_LIMIT)
|
|
114
|
+
except Exception:
|
|
115
|
+
logger.debug("LoadedToolStore: could not list loaded tools", exc_info=True)
|
|
116
|
+
return []
|
|
117
|
+
# ``StorageRecord.data`` holds the instance the strategy validated against the
|
|
118
|
+
# registered model, so this is a narrowing, not a parse. Anything else in the
|
|
119
|
+
# collection is someone else's record and is skipped rather than guessed at.
|
|
120
|
+
setup_ids = [
|
|
121
|
+
record.data.setup_id
|
|
122
|
+
for record in records
|
|
123
|
+
if isinstance(record.data, LoadedToolRecord) and record.data.setup_id
|
|
124
|
+
]
|
|
125
|
+
if setup_ids:
|
|
126
|
+
logger.info("LoadedToolStore: %d loaded tool(s) to rehydrate: %s", len(setup_ids), setup_ids)
|
|
127
|
+
return setup_ids
|
|
128
|
+
|
|
129
|
+
async def forget(self, setup_id: str) -> None:
|
|
130
|
+
"""Drop a persisted id that no longer resolves, so it is not retried every turn.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
setup_id: The setup id to remove from the mission's loaded set.
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
if self.COLLECTION not in self._storage.config:
|
|
137
|
+
return
|
|
138
|
+
await self._storage.remove(collection=self.COLLECTION, record_id=setup_id)
|
|
139
|
+
except Exception:
|
|
140
|
+
logger.debug("LoadedToolStore: could not forget '%s'", setup_id, exc_info=True)
|
{digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/src/digitalkin/models/module/module_context.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Define the module context used in the triggers."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from collections.abc import AsyncGenerator, Callable
|
|
4
5
|
from datetime import tzinfo
|
|
5
6
|
from types import SimpleNamespace
|
|
@@ -8,7 +9,9 @@ from zoneinfo import ZoneInfo
|
|
|
8
9
|
|
|
9
10
|
from google.protobuf import json_format
|
|
10
11
|
|
|
12
|
+
from digitalkin.grpc_servers.exceptions import PermissionDeniedError
|
|
11
13
|
from digitalkin.logger import logger
|
|
14
|
+
from digitalkin.models.module.loaded_tools import LoadedToolStore
|
|
12
15
|
from digitalkin.models.module.request_metadata import RequestMetadata
|
|
13
16
|
from digitalkin.models.module.tool_cache import ToolCache, ToolDefinition, ToolModuleInfo
|
|
14
17
|
from digitalkin.models.settings.module import get_module_settings
|
|
@@ -334,7 +337,7 @@ class ModuleContext:
|
|
|
334
337
|
Returns:
|
|
335
338
|
List of (ToolDefinition, async_generator_function) tuples. Empty if not found.
|
|
336
339
|
"""
|
|
337
|
-
tool_module_info = self.tool_cache.
|
|
340
|
+
tool_module_info = self.tool_cache.get(slug)
|
|
338
341
|
if not tool_module_info:
|
|
339
342
|
return []
|
|
340
343
|
|
|
@@ -355,11 +358,14 @@ class ModuleContext:
|
|
|
355
358
|
"""Resolve a registry ``setup_id`` into a ``ToolModuleInfo`` and cache it.
|
|
356
359
|
|
|
357
360
|
On-demand loader for a discovered tool. ``registry.get_setup`` always runs
|
|
358
|
-
first — it is the permission gate, and the
|
|
359
|
-
missions of the same agent setup, so a cache hit must never skip authz.
|
|
361
|
+
first — it is the permission gate, and the cache's ``declared`` layer is shared
|
|
362
|
+
across missions of the same agent setup, so a cache hit must never skip authz.
|
|
360
363
|
The cache only short-circuits the module discovery + schema fetch.
|
|
361
364
|
Permission denials propagate so callers can surface them distinctly.
|
|
362
365
|
|
|
366
|
+
The result lands in the cache's mission-scoped ``dynamic`` layer. Pair this with
|
|
367
|
+
:meth:`persist_loaded_tool` to make the load outlive the current turn.
|
|
368
|
+
|
|
363
369
|
Args:
|
|
364
370
|
setup_id: The registry setup id to load as an invocable tool.
|
|
365
371
|
|
|
@@ -376,7 +382,7 @@ class ModuleContext:
|
|
|
376
382
|
"resolve_tool: setup '%s' not found or has no module", setup_id, extra=self.session.current_ids()
|
|
377
383
|
)
|
|
378
384
|
return None
|
|
379
|
-
cached = self.tool_cache.
|
|
385
|
+
cached = self.tool_cache.get(setup_id)
|
|
380
386
|
if cached is not None:
|
|
381
387
|
logger.debug(
|
|
382
388
|
"resolve_tool: cache hit for setup '%s' (authz re-checked)", setup_id, extra=self.session.current_ids()
|
|
@@ -401,7 +407,10 @@ class ModuleContext:
|
|
|
401
407
|
)
|
|
402
408
|
return None
|
|
403
409
|
tool_info = await ToolModuleInfo.from_module_info(info, setup_id, setup.name, self.communication)
|
|
404
|
-
|
|
410
|
+
# Mission-scoped layer, never ``declared``: the declared layer is the object the
|
|
411
|
+
# servicer shares with every other mission of this setup, so writing a runtime
|
|
412
|
+
# load there makes it reappear in unrelated conversations.
|
|
413
|
+
self.tool_cache.add_dynamic(tool_info)
|
|
405
414
|
logger.info(
|
|
406
415
|
"resolve_tool: resolved setup '%s' -> module '%s' (%d tools), cached",
|
|
407
416
|
setup_id,
|
|
@@ -411,6 +420,81 @@ class ModuleContext:
|
|
|
411
420
|
)
|
|
412
421
|
return tool_info
|
|
413
422
|
|
|
423
|
+
async def persist_loaded_tool(self, setup_id: str) -> bool:
|
|
424
|
+
"""Record a runtime-loaded tool so it survives into this mission's next turn.
|
|
425
|
+
|
|
426
|
+
Every user message builds a fresh module instance, so the ``dynamic`` cache layer
|
|
427
|
+
and the agent's tool list are both rebuilt from scratch. Persisting the id is what
|
|
428
|
+
turns a load into a property of the *conversation* rather than of the turn.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
setup_id: The loaded tool's registry setup id.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
``True`` if the id was persisted; ``False`` if the module never registered the
|
|
435
|
+
``loaded_tools`` collection, in which case the load lasts only this turn.
|
|
436
|
+
"""
|
|
437
|
+
return await LoadedToolStore(self.storage).save(setup_id)
|
|
438
|
+
|
|
439
|
+
async def rehydrate_loaded_tools(self) -> int:
|
|
440
|
+
"""Re-resolve this mission's previously-loaded tools into the ``dynamic`` layer.
|
|
441
|
+
|
|
442
|
+
Called once per turn from ``BaseModule.prepare()``, before ``initialize()`` builds
|
|
443
|
+
the agent's toolkits. Ids are re-resolved rather than replayed from a snapshot, so
|
|
444
|
+
each turn re-checks authorization and picks up schema changes; an id that no longer
|
|
445
|
+
resolves — revoked, deleted — is dropped from the mission so it is not retried on
|
|
446
|
+
every subsequent message.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
The number of tools restored into the dynamic layer.
|
|
450
|
+
"""
|
|
451
|
+
store = LoadedToolStore(self.storage)
|
|
452
|
+
setup_ids = [sid for sid in await store.list_setup_ids() if sid not in self.tool_cache.dynamic]
|
|
453
|
+
if not setup_ids:
|
|
454
|
+
return 0
|
|
455
|
+
|
|
456
|
+
# Resolve concurrently: each id costs a get_setup + discover + schema fetch, and this
|
|
457
|
+
# sits on the critical path of every user message.
|
|
458
|
+
results = await asyncio.gather(
|
|
459
|
+
*(self.resolve_tool(setup_id) for setup_id in setup_ids),
|
|
460
|
+
return_exceptions=True,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
restored = 0
|
|
464
|
+
for setup_id, result in zip(setup_ids, results, strict=True):
|
|
465
|
+
if isinstance(result, PermissionDeniedError):
|
|
466
|
+
logger.warning(
|
|
467
|
+
"rehydrate_loaded_tools: access to '%s' was revoked; dropping it from the mission",
|
|
468
|
+
setup_id,
|
|
469
|
+
extra=self.session.current_ids(),
|
|
470
|
+
)
|
|
471
|
+
await store.forget(setup_id)
|
|
472
|
+
elif isinstance(result, BaseException):
|
|
473
|
+
# Transient (registry hiccup): keep the id so a later turn can retry.
|
|
474
|
+
logger.warning(
|
|
475
|
+
"rehydrate_loaded_tools: could not restore '%s': %s",
|
|
476
|
+
setup_id,
|
|
477
|
+
result,
|
|
478
|
+
extra=self.session.current_ids(),
|
|
479
|
+
)
|
|
480
|
+
elif result is None:
|
|
481
|
+
logger.warning(
|
|
482
|
+
"rehydrate_loaded_tools: '%s' no longer exists; dropping it from the mission",
|
|
483
|
+
setup_id,
|
|
484
|
+
extra=self.session.current_ids(),
|
|
485
|
+
)
|
|
486
|
+
await store.forget(setup_id)
|
|
487
|
+
else:
|
|
488
|
+
restored += 1
|
|
489
|
+
|
|
490
|
+
logger.info(
|
|
491
|
+
"rehydrate_loaded_tools: restored %d/%d loaded tool(s)",
|
|
492
|
+
restored,
|
|
493
|
+
len(setup_ids),
|
|
494
|
+
extra=self.session.current_ids(),
|
|
495
|
+
)
|
|
496
|
+
return restored
|
|
497
|
+
|
|
414
498
|
@staticmethod
|
|
415
499
|
def _create_single_tool_function(
|
|
416
500
|
communication: CommunicationStrategy,
|
|
@@ -186,20 +186,79 @@ class ToolModuleInfo(ModuleInfo):
|
|
|
186
186
|
|
|
187
187
|
|
|
188
188
|
class ToolCache(BaseModel):
|
|
189
|
-
"""
|
|
189
|
+
"""Two-layer cache of resolved tool references, keyed by ``setup_id``.
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
The layers differ by *lifetime*, which is the whole point of the split:
|
|
192
|
+
|
|
193
|
+
- ``declared`` — the tools the setup itself selects, resolved once by
|
|
194
|
+
``SetupModel.build_tool_cache``. The servicer keeps this layer alive per
|
|
195
|
+
``setup_id`` and hands the same object to every mission of that setup.
|
|
196
|
+
- ``dynamic`` — tools the agent loaded at runtime via
|
|
197
|
+
``ModuleContext.resolve_tool``. This layer is **mission-scoped**: it is
|
|
198
|
+
rebuilt per mission from the ``loaded_tools`` storage collection, so a load
|
|
199
|
+
persists across the turns of one conversation and never leaks into another
|
|
200
|
+
mission of the same setup.
|
|
201
|
+
|
|
202
|
+
Never write a runtime resolution into ``declared`` — doing so is what leaked
|
|
203
|
+
dynamically-loaded tools across missions before the split existed.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
declared: dict[str, ToolModuleInfo] = Field(
|
|
207
|
+
default_factory=dict, description="Setup-selected tools; shared across missions of one setup."
|
|
208
|
+
)
|
|
209
|
+
dynamic: dict[str, ToolModuleInfo] = Field(
|
|
210
|
+
default_factory=dict, description="Runtime-loaded tools; scoped to a single mission."
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def entries(self) -> dict[str, ToolModuleInfo]:
|
|
215
|
+
"""Merged read-only view of both layers, ``dynamic`` winning on conflict.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
A fresh dict — mutating it does not touch either layer (and must not:
|
|
219
|
+
``declared`` is shared with every other mission of this setup).
|
|
220
|
+
"""
|
|
221
|
+
return {**self.declared, **self.dynamic}
|
|
222
|
+
|
|
223
|
+
def mission_view(self, dynamic: dict[str, ToolModuleInfo] | None = None) -> "ToolCache":
|
|
224
|
+
"""Build a per-mission view that shares this cache's ``declared`` entries.
|
|
225
|
+
|
|
226
|
+
The mapping is shallow-copied so a mission can never mutate the shared
|
|
227
|
+
declared layer, while the ``ToolModuleInfo`` values are reused as-is
|
|
228
|
+
(they are treated as immutable, and re-validating them per turn is not free).
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
dynamic: Pre-resolved runtime tools to seed the mission layer with.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
A new ``ToolCache`` whose ``dynamic`` layer is private to this mission.
|
|
235
|
+
"""
|
|
236
|
+
return ToolCache.model_construct(declared=dict(self.declared), dynamic=dynamic or {})
|
|
192
237
|
|
|
193
238
|
def add(self, tool_module_info: ToolModuleInfo) -> None:
|
|
194
|
-
"""Add a tool to the
|
|
239
|
+
"""Add a setup-declared tool to the ``declared`` layer.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
tool_module_info: Resolved tool module information.
|
|
243
|
+
"""
|
|
244
|
+
setup_id = tool_module_info.setup_id
|
|
245
|
+
self.declared[setup_id] = tool_module_info
|
|
246
|
+
logger.debug(
|
|
247
|
+
"Tool cached (declared): module_id=%s",
|
|
248
|
+
tool_module_info.module_id,
|
|
249
|
+
extra={"setup_id": setup_id},
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def add_dynamic(self, tool_module_info: ToolModuleInfo) -> None:
|
|
253
|
+
"""Add a runtime-loaded tool to the mission-scoped ``dynamic`` layer.
|
|
195
254
|
|
|
196
255
|
Args:
|
|
197
256
|
tool_module_info: Resolved tool module information.
|
|
198
257
|
"""
|
|
199
258
|
setup_id = tool_module_info.setup_id
|
|
200
|
-
self.
|
|
259
|
+
self.dynamic[setup_id] = tool_module_info
|
|
201
260
|
logger.debug(
|
|
202
|
-
"Tool cached: module_id=%s",
|
|
261
|
+
"Tool cached (dynamic): module_id=%s",
|
|
203
262
|
tool_module_info.module_id,
|
|
204
263
|
extra={"setup_id": setup_id},
|
|
205
264
|
)
|
|
@@ -208,7 +267,7 @@ class ToolCache(BaseModel):
|
|
|
208
267
|
self,
|
|
209
268
|
setup_id: str,
|
|
210
269
|
) -> ToolModuleInfo | None:
|
|
211
|
-
"""Get a tool from
|
|
270
|
+
"""Get a tool from either layer, preferring the mission-scoped one.
|
|
212
271
|
|
|
213
272
|
Args:
|
|
214
273
|
setup_id: Field name to look up.
|
|
@@ -216,14 +275,15 @@ class ToolCache(BaseModel):
|
|
|
216
275
|
Returns:
|
|
217
276
|
ToolModuleInfo if found, None otherwise.
|
|
218
277
|
"""
|
|
219
|
-
return self.
|
|
278
|
+
return self.dynamic.get(setup_id) or self.declared.get(setup_id)
|
|
220
279
|
|
|
221
280
|
def clear(self) -> None:
|
|
222
|
-
"""Clear
|
|
223
|
-
self.
|
|
281
|
+
"""Clear both layers."""
|
|
282
|
+
self.declared.clear()
|
|
283
|
+
self.dynamic.clear()
|
|
224
284
|
|
|
225
285
|
def list_tools(self) -> list[str]:
|
|
226
|
-
"""List all cached tool names.
|
|
286
|
+
"""List all cached tool names across both layers.
|
|
227
287
|
|
|
228
288
|
Returns:
|
|
229
289
|
List of setup field names in cache.
|
|
@@ -648,9 +648,16 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
|
|
|
648
648
|
self.context.registry,
|
|
649
649
|
self.context.communication,
|
|
650
650
|
)
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
# Installed unconditionally, even when the setup declares no tools: the
|
|
652
|
+
# mission view owns the ``dynamic`` layer, which is where runtime loads land.
|
|
653
|
+
# Gating on a non-empty declared layer used to leave the context holding a
|
|
654
|
+
# throwaway ToolCache, so a setup with no selected tools could never keep one.
|
|
655
|
+
self.context.tool_cache = tool_cache.mission_view()
|
|
653
656
|
timer.mark("build_tool_cache")
|
|
657
|
+
# Restore the tools the agent loaded earlier in this mission, before
|
|
658
|
+
# initialize() builds the toolkits that have to expose them.
|
|
659
|
+
await self.context.rehydrate_loaded_tools()
|
|
660
|
+
timer.mark("rehydrate_loaded_tools")
|
|
654
661
|
|
|
655
662
|
await self.initialize(self.context, setup_data)
|
|
656
663
|
timer.mark("initialize")
|
|
@@ -148,6 +148,7 @@ src/digitalkin/models/grpc_servers/types.py
|
|
|
148
148
|
src/digitalkin/models/module/__init__.py
|
|
149
149
|
src/digitalkin/models/module/ag_ui.py
|
|
150
150
|
src/digitalkin/models/module/base_types.py
|
|
151
|
+
src/digitalkin/models/module/loaded_tools.py
|
|
151
152
|
src/digitalkin/models/module/module.py
|
|
152
153
|
src/digitalkin/models/module/module_context.py
|
|
153
154
|
src/digitalkin/models/module/module_types.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/server_async_insecure.py
RENAMED
|
File without changes
|
|
File without changes
|
{digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/base_server/server_sync_insecure.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-1.0.3.dev1 → digitalkin-1.0.3.dev2}/examples/bench_module/triggers/message_trigger.py
RENAMED
|
File without changes
|