digitalkin 1.0.0.dev13__tar.gz → 1.0.0.dev15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/PKG-INFO +2 -2
  2. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/pyproject.toml +3 -2
  3. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/__version__.py +1 -1
  4. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/common/factories.py +4 -10
  5. digitalkin-1.0.0.dev15/src/digitalkin/core/exceptions.py +33 -0
  6. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/base_task_manager.py +4 -7
  7. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/local_task_manager.py +6 -10
  8. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/redis_client.py +10 -3
  9. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/remote_task_manager.py +6 -10
  10. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/task_session.py +1 -1
  11. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/gateway_servicer.py +11 -1
  12. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/module_server.py +4 -4
  13. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +29 -11
  14. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/cost_mixin.py +1 -1
  15. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/tool_cache.py +3 -5
  16. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/_base_module.py +4 -4
  17. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/storage/grpc_storage.py +46 -19
  18. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin.egg-info/PKG-INFO +2 -2
  19. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin.egg-info/requires.txt +1 -1
  20. digitalkin-1.0.0.dev13/src/digitalkin/core/exceptions.py +0 -16
  21. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/LICENSE +0 -0
  22. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/README.md +0 -0
  23. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/__init__.py +0 -0
  24. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/mock/__init__.py +0 -0
  25. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/mock/mock_pb2.py +0 -0
  26. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  27. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/server_async_insecure.py +0 -0
  28. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/server_async_secure.py +0 -0
  29. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/server_sync_insecure.py +0 -0
  30. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/base_server/server_sync_secure.py +0 -0
  31. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/__init__.py +0 -0
  32. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/echo_module.py +0 -0
  33. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/models/__init__.py +0 -0
  34. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/models/input.py +0 -0
  35. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/models/output.py +0 -0
  36. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/models/secret.py +0 -0
  37. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/models/setup.py +0 -0
  38. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/server.py +0 -0
  39. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/triggers/__init__.py +0 -0
  40. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/bench_module/triggers/message_trigger.py +0 -0
  41. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/modules/__init__.py +0 -0
  42. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/modules/archetype_with_tools_module.py +0 -0
  43. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/modules/cpu_intensive_module.py +0 -0
  44. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/modules/dynamic_setup_module.py +0 -0
  45. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/modules/minimal_llm_module.py +0 -0
  46. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/modules/text_transform_module.py +0 -0
  47. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
  48. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
  49. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
  50. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
  51. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
  52. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/monitoring/tests/test_metrics.py +0 -0
  53. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/client.py +0 -0
  54. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/echo_module.py +0 -0
  55. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/models/__init__.py +0 -0
  56. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/models/input.py +0 -0
  57. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/models/output.py +0 -0
  58. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/models/secret.py +0 -0
  59. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/models/setup.py +0 -0
  60. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/server.py +0 -0
  61. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/triggers/__init__.py +0 -0
  62. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/redis_demo/triggers/message_trigger.py +0 -0
  63. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/services/filesystem_module.py +0 -0
  64. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/examples/services/storage_module.py +0 -0
  65. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/setup.cfg +0 -0
  66. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/__init__.py +0 -0
  67. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/community/__init__.py +0 -0
  68. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/community/agno/__init__.py +0 -0
  69. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/community/agno/agno_adapter.py +0 -0
  70. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/community/agno/agui_tools.py +0 -0
  71. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/community/agno/hitl.py +0 -0
  72. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/community/agno/models.py +0 -0
  73. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/__init__.py +0 -0
  74. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/common/__init__.py +0 -0
  75. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/job_manager/__init__.py +0 -0
  76. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
  77. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
  78. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/profiling/__init__.py +0 -0
  79. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/profiling/step_timer.py +0 -0
  80. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/profiling/task_profiler.py +0 -0
  81. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/resilience/__init__.py +0 -0
  82. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/resilience/bulkhead.py +0 -0
  83. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
  84. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/__init__.py +0 -0
  85. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/module_runner.py +0 -0
  86. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
  87. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/instrumented.py +0 -0
  88. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
  89. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/redis_checkpoint.py +0 -0
  90. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
  91. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
  92. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
  93. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/redis_streams.py +0 -0
  94. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/redis/shadow.py +0 -0
  95. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/core/task_manager/task_executor.py +0 -0
  96. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/exceptions.py +0 -0
  97. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/__init__.py +0 -0
  98. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  99. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/exceptions.py +0 -0
  100. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
  101. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/interceptors/circuit_breaker_interceptor.py +0 -0
  102. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
  103. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/module_servicer.py +0 -0
  104. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
  105. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/stream_session.py +0 -0
  106. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  107. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +0 -0
  108. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  109. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
  110. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
  111. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/logger.py +0 -0
  112. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/__init__.py +0 -0
  113. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/agui_mixin.py +0 -0
  114. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/base_mixin.py +0 -0
  115. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  116. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  117. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/logger_mixin.py +0 -0
  118. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/mixins/storage_mixin.py +0 -0
  119. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/__init__.py +0 -0
  120. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/core/__init__.py +0 -0
  121. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/core/job_manager_models.py +0 -0
  122. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/core/redis.py +0 -0
  123. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/core/task_monitor.py +0 -0
  124. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/events/__init__.py +0 -0
  125. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/events/agent_events.py +0 -0
  126. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  127. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
  128. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
  129. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/grpc_servers/models.py +0 -0
  130. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/grpc_servers/stream_error_codes.py +0 -0
  131. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/grpc_servers/types.py +0 -0
  132. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/__init__.py +0 -0
  133. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/ag_ui.py +0 -0
  134. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/base_types.py +0 -0
  135. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/module.py +0 -0
  136. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/module_context.py +0 -0
  137. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/module_types.py +0 -0
  138. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/request_metadata.py +0 -0
  139. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/select_schema.py +0 -0
  140. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/setup_types.py +0 -0
  141. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/tool_reference.py +0 -0
  142. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/module/utility.py +0 -0
  143. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/services/__init__.py +0 -0
  144. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/services/cost.py +0 -0
  145. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/services/registry.py +0 -0
  146. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/services/services.py +0 -0
  147. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/services/storage.py +0 -0
  148. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/__init__.py +0 -0
  149. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/consumer.py +0 -0
  150. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/gateway.py +0 -0
  151. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/grpc_client.py +0 -0
  152. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/log.py +0 -0
  153. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/module.py +0 -0
  154. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/profiling.py +0 -0
  155. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/queue.py +0 -0
  156. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/redis.py +0 -0
  157. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/resilience.py +0 -0
  158. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/server/__init__.py +0 -0
  159. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/server/channel.py +0 -0
  160. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/server/grpc.py +0 -0
  161. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/server/server.py +0 -0
  162. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/server/servicer.py +0 -0
  163. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/task_manager.py +0 -0
  164. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/utils/__init__.py +0 -0
  165. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/settings/utils/channel.py +0 -0
  166. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/utils/__init__.py +0 -0
  167. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
  168. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/__init__.py +0 -0
  169. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/archetype_module.py +0 -0
  170. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/tool_module.py +0 -0
  171. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/trigger_handler.py +0 -0
  172. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/triggers/__init__.py +0 -0
  173. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
  174. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
  175. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
  176. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/py.typed +0 -0
  177. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/__init__.py +0 -0
  178. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/base_strategy.py +0 -0
  179. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/communication/__init__.py +0 -0
  180. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/communication/communication_strategy.py +0 -0
  181. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/communication/default_communication.py +0 -0
  182. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/communication/exceptions.py +0 -0
  183. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/communication/grpc_communication.py +0 -0
  184. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/cost/__init__.py +0 -0
  185. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  186. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/cost/default_cost.py +0 -0
  187. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/cost/exceptions.py +0 -0
  188. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/cost/grpc_cost.py +0 -0
  189. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/filesystem/__init__.py +0 -0
  190. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  191. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/filesystem/exceptions.py +0 -0
  192. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  193. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
  194. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/identity/__init__.py +0 -0
  195. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/identity/default_identity.py +0 -0
  196. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  197. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/registry/__init__.py +0 -0
  198. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/registry/default_registry.py +0 -0
  199. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/registry/exceptions.py +0 -0
  200. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/registry/grpc_registry.py +0 -0
  201. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/registry/registry_models.py +0 -0
  202. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/registry/registry_strategy.py +0 -0
  203. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/services_config.py +0 -0
  204. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/services_models.py +0 -0
  205. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/setup/__init__.py +0 -0
  206. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/setup/default_setup.py +0 -0
  207. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/setup/exceptions.py +0 -0
  208. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/setup/grpc_setup.py +0 -0
  209. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  210. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/storage/__init__.py +0 -0
  211. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/storage/default_storage.py +0 -0
  212. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/storage/exceptions.py +0 -0
  213. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  214. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/task_manager/__init__.py +0 -0
  215. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
  216. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/task_manager/exceptions.py +0 -0
  217. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
  218. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
  219. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/user_profile/__init__.py +0 -0
  220. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  221. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/user_profile/exceptions.py +0 -0
  222. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
  223. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
  224. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/__init__.py +0 -0
  225. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/arg_parser.py +0 -0
  226. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/conditional_schema.py +0 -0
  227. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/development_mode_action.py +0 -0
  228. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/dynamic_schema.py +0 -0
  229. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/exceptions.py +0 -0
  230. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  231. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/package_discover.py +0 -0
  232. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/proto_utils.py +0 -0
  233. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin/utils/schema_splitter.py +0 -0
  234. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin.egg-info/SOURCES.txt +0 -0
  235. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  236. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/src/digitalkin.egg-info/top_level.txt +0 -0
  237. {digitalkin-1.0.0.dev13 → digitalkin-1.0.0.dev15}/tests/test_exceptions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 1.0.0.dev13
3
+ Version: 1.0.0.dev15
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
@@ -452,7 +452,7 @@ Classifier: Topic :: Software Development :: Libraries
452
452
  Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
- Requires-Dist: ag-ui-protocol>=0.1.18
455
+ Requires-Dist: ag-ui-protocol>=0.1.19
456
456
  Requires-Dist: agentic-mesh-protocol==1.0.0.dev2
457
457
  Requires-Dist: anyio>=4.13.0
458
458
  Requires-Dist: grpcio-health-checking==1.80.0
@@ -27,7 +27,7 @@
27
27
  ]
28
28
 
29
29
  dependencies = [
30
- "ag-ui-protocol>=0.1.18",
30
+ "ag-ui-protocol>=0.1.19",
31
31
  "agentic-mesh-protocol==1.0.0.dev2",
32
32
  "anyio>=4.13.0",
33
33
  "grpcio-health-checking==1.80.0",
@@ -37,7 +37,7 @@
37
37
  "pydantic-settings>=2.14.1",
38
38
  "redis[hiredis]>=7.4.0,<8",
39
39
  ]
40
- version = "1.0.0.dev13"
40
+ version = "1.0.0.dev15"
41
41
 
42
42
  [project.optional-dependencies]
43
43
  performance = [ "uvloop>=0.21" ]
@@ -305,5 +305,6 @@
305
305
  "smoke: marks critical path tests that should always pass",
306
306
  "stability: marks long-running stability and soak tests",
307
307
  "stress: marks stress/load tests for performance under pressure",
308
+ "unit: marks a plain unit test with no other applicable category",
308
309
  "validation: marks tests for input validation and schema checking",
309
310
  ]
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("digitalkin")
7
7
  except PackageNotFoundError:
8
- __version__ = "1.0.0.dev13"
8
+ __version__ = "1.0.0.dev15"
@@ -64,16 +64,10 @@ class ModuleFactory:
64
64
  raise ValueError(msg)
65
65
 
66
66
  logger.debug(
67
- "Creating module instance: %s for job: %s",
67
+ "Creating module instance: %s (setup_version_id=%s)",
68
68
  module_class.__name__,
69
- job_id,
70
- extra={
71
- "module_class": module_class.__name__,
72
- "job_id": job_id,
73
- "mission_id": mission_id,
74
- "setup_id": setup_id,
75
- "setup_version_id": setup_version_id,
76
- },
69
+ setup_version_id,
70
+ extra={"job_id": job_id, "mission_id": mission_id, "setup_id": setup_id},
77
71
  )
78
72
 
79
73
  return module_class(
@@ -116,5 +110,5 @@ class QueueFactory:
116
110
  msg = "maxsize must be >= 0"
117
111
  raise ValueError(msg)
118
112
 
119
- logger.debug("Creating bounded queue with maxsize: %d", maxsize, extra={"maxsize": maxsize})
113
+ logger.debug("Creating bounded queue with maxsize: %d", maxsize)
120
114
  return asyncio.Queue(maxsize=maxsize)
@@ -0,0 +1,33 @@
1
+ """Exceptions for the DigitalKin core package."""
2
+
3
+
4
+ class BackpressureTimeoutError(Exception):
5
+ """Producer's XADD throttled past the backpressure timeout.
6
+
7
+ Throttled past :data:`GatewayBackpressureSettings.backpressure_timeout_s`.
8
+ Caller (typically the module's ``_on_output`` callback) must surface
9
+ this as ``stream.error(code=BACKPRESSURE_TIMEOUT)`` via the
10
+ ``_emit_fatal_to_redis`` path so the consumer sees a typed sentinel
11
+ instead of a silent stall.
12
+ """
13
+
14
+
15
+ class BulkheadFullError(Exception):
16
+ """Raised when a bulkhead semaphore cannot be acquired within timeout."""
17
+
18
+
19
+ class RedisUnreachableError(Exception):
20
+ """Raised at gateway boot when Redis ping fails.
21
+
22
+ Redis is a required dependency for gateway operation (stream persistence,
23
+ pub/sub signals). Failing fast at boot is preferable to lazy first-request
24
+ failures that surface as opaque task errors.
25
+ """
26
+
27
+ def __init__(self, masked_url: str) -> None:
28
+ """Initialize the error with a (masked) Redis URL for context.
29
+
30
+ Args:
31
+ masked_url: Redis connection URL with credentials masked.
32
+ """
33
+ super().__init__(f"Redis ping failed at gateway boot ({masked_url})")
@@ -103,14 +103,11 @@ class BaseTaskManager(ABC):
103
103
  if get_task_manager_settings().max_queued_tasks > 0:
104
104
  self._system_gate.release()
105
105
  logger.info(
106
- "Task cleaned up (%d remaining)",
106
+ "Task cleaned up (%d remaining) final_status=%s cancellation_reason=%s",
107
107
  len(self.tasks_sessions),
108
- extra={
109
- "mission_id": mission_id,
110
- "task_id": task_id,
111
- "final_status": final_status,
112
- "cancellation_reason": cancellation_reason,
113
- },
108
+ final_status,
109
+ cancellation_reason,
110
+ extra={"mission_id": mission_id, "task_id": task_id},
114
111
  )
115
112
 
116
113
  async def _validate_task_creation(self, task_id: str, mission_id: str, coro: Coroutine[Any, Any, None]) -> None:
@@ -76,26 +76,22 @@ class LocalTaskManager(BaseTaskManager):
76
76
  self.tasks[task_id] = supervisor_task
77
77
 
78
78
  logger.info(
79
- "Local task created and started: '%s'",
79
+ "Local task created and started: '%s' (total_tasks=%d)",
80
80
  task_id,
81
- extra={
82
- "mission_id": mission_id,
83
- "task_id": task_id,
84
- "total_tasks": len(self.tasks),
85
- },
81
+ len(self.tasks),
82
+ extra={"mission_id": mission_id, "task_id": task_id},
86
83
  )
87
84
 
88
- except Exception as e:
85
+ except Exception:
89
86
  coro.close()
90
87
  # Release the slot if cleanup won't (session never registered).
91
88
  if task_id not in self.tasks_sessions:
92
89
  self._task_slot.release()
93
90
  else:
94
91
  await self._cleanup_task(task_id, mission_id=mission_id)
95
- logger.error(
92
+ logger.exception(
96
93
  "Failed to create local task: '%s'",
97
94
  task_id,
98
- extra={"mission_id": mission_id, "task_id": task_id, "error": str(e)},
99
- exc_info=True,
95
+ extra={"mission_id": mission_id, "task_id": task_id},
100
96
  )
101
97
  raise
@@ -69,17 +69,24 @@ class RedisClient: # noqa: PLR0904
69
69
  )
70
70
 
71
71
  async def verify(self) -> bool:
72
- """Verify Redis is reachable by sending a PING.
72
+ """Verify Redis is reachable by pinging both pools.
73
+
74
+ Pings ``_client`` and ``_blocking_client`` concurrently so the first
75
+ XADD and first XREAD don't each pay DNS+TCP+AUTH on cold pools.
73
76
 
74
77
  Timeout comes from ``RedisPoolSettings.health_check_timeout`` (env
75
78
  ``DIGITALKIN_REDIS_HEALTH_CHECK_TIMEOUT``).
76
79
 
77
80
  Returns:
78
- True if Redis responded, False if unreachable.
81
+ True if both pools responded, False if either is unreachable.
79
82
  """
80
83
  try:
81
84
  timeout = get_redis_settings().pool.health_check_timeout
82
- return await asyncio.wait_for(self._client.ping(), timeout=timeout) # type: ignore[arg-type]
85
+ results = await asyncio.gather(
86
+ asyncio.wait_for(self._client.ping(), timeout=timeout), # type: ignore[arg-type]
87
+ asyncio.wait_for(self._blocking_client.ping(), timeout=timeout), # type: ignore[arg-type]
88
+ )
89
+ return all(results)
83
90
  except Exception:
84
91
  logger.warning("Redis health check failed for %s", GatewayValidator.mask_redis_url(self.url), exc_info=True)
85
92
  return False
@@ -57,26 +57,22 @@ class RemoteTaskManager(BaseTaskManager):
57
57
  coro.close()
58
58
 
59
59
  logger.info(
60
- "Remote task registered: '%s'",
60
+ "Remote task registered: '%s' (total_sessions=%d)",
61
61
  task_id,
62
- extra={
63
- "mission_id": mission_id,
64
- "task_id": task_id,
65
- "total_sessions": len(self.tasks_sessions),
66
- },
62
+ len(self.tasks_sessions),
63
+ extra={"mission_id": mission_id, "task_id": task_id},
67
64
  )
68
65
 
69
- except Exception as e:
66
+ except Exception:
70
67
  coro.close()
71
68
  # Release semaphore if session was never registered (cleanup won't release it)
72
69
  if task_id not in self.tasks_sessions:
73
70
  self._task_slot.release()
74
71
  else:
75
72
  await self._cleanup_task(task_id, mission_id=mission_id)
76
- logger.error(
73
+ logger.exception(
77
74
  "Failed to register remote task: '%s'",
78
75
  task_id,
79
- extra={"mission_id": mission_id, "task_id": task_id, "error": str(e)},
80
- exc_info=True,
76
+ extra={"mission_id": mission_id, "task_id": task_id},
81
77
  )
82
78
  raise
@@ -284,7 +284,7 @@ class TaskSession:
284
284
  return
285
285
  self._cleanup_done = True
286
286
 
287
- logger.debug("Cleanup: draining queue", extra={"task_id": self.task_id, "queue_size": self.queue.qsize()})
287
+ logger.debug("Cleanup: draining queue (queue_size=%d)", self.queue.qsize(), extra=ids)
288
288
  try:
289
289
  while not self.queue.empty():
290
290
  self.queue.get_nowait()
@@ -15,6 +15,7 @@ from google.protobuf import struct_pb2
15
15
  from grpc._cython.cygrpc import UsageError as _GrpcUsageError # noqa: PLC2701
16
16
  from redis.exceptions import RedisError
17
17
 
18
+ from digitalkin.core.exceptions import RedisUnreachableError
18
19
  from digitalkin.core.profiling.step_timer import StepTimer
19
20
  from digitalkin.core.task_manager.redis.proto_streams import ProtoStreamReader
20
21
  from digitalkin.core.task_manager.redis.redis_signal import SharedRedisListener
@@ -131,7 +132,16 @@ class GatewayServicer:
131
132
  return task
132
133
 
133
134
  async def start(self) -> None:
134
- """Start the M2M call-registry TTL sweeper and PSUBSCRIBE the signal listener."""
135
+ """Start the M2M call-registry TTL sweeper and PSUBSCRIBE the signal listener.
136
+
137
+ Pre-warms both Redis pools so the first XADD and first XREAD don't pay
138
+ DNS+TCP+AUTH on cold connections.
139
+
140
+ Raises:
141
+ RedisUnreachableError: Redis ping failed; gateway cannot serve traffic.
142
+ """
143
+ if not await self._redis_client.verify():
144
+ raise RedisUnreachableError(GatewayValidator.mask_redis_url(self._redis_client.url))
135
145
  await self._m2m.start()
136
146
  listener = SharedRedisListener.singleton_or_none()
137
147
  if listener is not None:
@@ -246,11 +246,11 @@ class ModuleServer(BaseServer):
246
246
  advertise_address = get_server_settings().channel.advertise_host or get_server_settings().channel.host
247
247
 
248
248
  logger.info(
249
- "Registering module with registry at %s:%d version=%s",
249
+ "Registering module with registry at %s:%d version=%s module_id=%s",
250
250
  advertise_address,
251
251
  get_server_settings().channel.port,
252
252
  version,
253
- extra={"module_id": module_id},
253
+ module_id,
254
254
  )
255
255
 
256
256
  result = await self.registry.register(
@@ -265,10 +265,10 @@ class ModuleServer(BaseServer):
265
265
  raise RuntimeError(msg)
266
266
 
267
267
  logger.info(
268
- "Module registered successfully at %s:%d",
268
+ "Module registered successfully at %s:%d module_id=%s",
269
269
  advertise_address,
270
270
  get_server_settings().channel.port,
271
- extra={"module_id": result.module_id},
271
+ result.module_id,
272
272
  )
273
273
 
274
274
  async def start_async(self) -> None:
@@ -48,6 +48,14 @@ class GrpcClientWrapper:
48
48
  grpc.StatusCode.DEADLINE_EXCEEDED,
49
49
  }
50
50
 
51
+ # Codes that count toward opening the circuit (service-health failures).
52
+ # Application-level codes (NOT_FOUND, INVALID_ARGUMENT, …) mean the service
53
+ # responded, so they never trip the breaker.
54
+ _CIRCUIT_FAILURE_CODES: ClassVar[set[grpc.StatusCode]] = _RETRYABLE_CODES | {
55
+ grpc.StatusCode.UNKNOWN,
56
+ grpc.StatusCode.RESOURCE_EXHAUSTED,
57
+ }
58
+
51
59
  @staticmethod
52
60
  def _build_channel_credentials(config: ClientConfig) -> grpc.ChannelCredentials | None:
53
61
  """Build SSL channel credentials from config if secure mode.
@@ -242,18 +250,28 @@ class GrpcClientWrapper:
242
250
  response = await rpc_method(request, timeout=timeout)
243
251
  except grpc.RpcError as e:
244
252
  last_error = e
245
- if e.code() not in self._RETRYABLE_CODES or attempt == max_retries:
253
+ if e.code() in self._RETRYABLE_CODES and attempt < max_retries:
254
+ logger.warning(
255
+ "gRPC transient error on %s.%s [%s] (attempt %d/%d), retrying in %.0fms",
256
+ self.service_name,
257
+ query_endpoint,
258
+ e.code().name,
259
+ attempt + 1,
260
+ max_retries + 1,
261
+ backoff_delays[attempt] * 1000,
262
+ )
263
+ continue
264
+ if e.code() in self._CIRCUIT_FAILURE_CODES:
265
+ logger.warning(
266
+ "circuit-breaker tick: %s.%s [%s]",
267
+ self.service_name,
268
+ query_endpoint,
269
+ e.code().name,
270
+ )
246
271
  cb.record_failure()
247
- break
248
- logger.warning(
249
- "gRPC transient error on %s.%s [%s] (attempt %d/%d), retrying in %.0fms",
250
- self.service_name,
251
- query_endpoint,
252
- e.code().name,
253
- attempt + 1,
254
- max_retries + 1,
255
- backoff_delays[attempt] * 1000,
256
- )
272
+ else:
273
+ cb.record_success()
274
+ break
257
275
  else:
258
276
  cb.record_success()
259
277
  return response
@@ -28,7 +28,7 @@ class CostMixin:
28
28
  try:
29
29
  await context.cost.add(name, cost_config_name, quantity)
30
30
  except Exception:
31
- logger.error("Failed to add cost '%s' (config=%s), continuing", name, cost_config_name, exc_info=True)
31
+ logger.exception("Failed to add cost '%s' (config=%s), continuing", name, cost_config_name)
32
32
 
33
33
  @staticmethod
34
34
  async def get_cost(context: ModuleContext, name: str) -> list[CostData]:
@@ -205,11 +205,9 @@ class ToolCache(BaseModel):
205
205
  )
206
206
  self.entries[setup_id] = tool_module_info
207
207
  logger.debug(
208
- "Tool cached",
209
- extra={
210
- "setup_id": setup_id,
211
- "module_id": tool_module_info.module_id,
212
- },
208
+ "Tool cached: module_id=%s",
209
+ tool_module_info.module_id,
210
+ extra={"setup_id": setup_id},
213
211
  )
214
212
 
215
213
  def get(
@@ -560,7 +560,7 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
560
560
  )
561
561
  )
562
562
  except Exception:
563
- logger.exception("Failed to send error callback")
563
+ logger.exception("Failed to send error callback", extra=self.context.session.current_ids())
564
564
  else:
565
565
  self._status = ModuleStatus.STOPPING
566
566
 
@@ -627,7 +627,7 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
627
627
  self._status = ModuleStatus.FAILED
628
628
  short_description = "Error initializing module"
629
629
  error_detail = f"{type(e).__name__}: {e}" if str(e) else type(e).__name__
630
- logger.exception("%s: %s", short_description, error_detail)
630
+ logger.exception("%s: %s", short_description, error_detail, extra=self.context.session.current_ids())
631
631
  await callback(
632
632
  ModuleCodeModel(
633
633
  code="Error",
@@ -645,7 +645,7 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
645
645
  timer.mark("run_lifecycle")
646
646
  except Exception:
647
647
  self._status = ModuleStatus.FAILED
648
- logger.exception("Error during module lifecyle")
648
+ logger.exception("Error during module lifecycle", extra=self.context.session.current_ids())
649
649
  finally:
650
650
  timer.log("module.start", task_id=self.context.session.current_ids().get("job_id", ""))
651
651
  await self.stop()
@@ -691,7 +691,7 @@ class BaseModule( # Module SDK base class requires many public methods # noqa:
691
691
  )
692
692
  except Exception:
693
693
  self._status = ModuleStatus.FAILED
694
- logger.exception("Error stopping module")
694
+ logger.exception("Error stopping module", extra=self.context.session.current_ids())
695
695
 
696
696
  async def _resolve_tools(self, config_setup_data: SetupModelT) -> None:
697
697
  """Resolve tool references and build cache.
@@ -4,6 +4,7 @@ from agentic_mesh_protocol.storage.v1 import data_pb2, storage_service_pb2_grpc
4
4
  from google.protobuf.struct_pb2 import Struct
5
5
  from pydantic import BaseModel
6
6
 
7
+ from digitalkin.grpc_servers.exceptions import CircuitOpenError
7
8
  from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
8
9
  from digitalkin.logger import logger
9
10
  from digitalkin.models.grpc_servers.models import ClientConfig
@@ -21,6 +22,22 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
21
22
 
22
23
  service_name: str = "StorageService"
23
24
 
25
+ @staticmethod
26
+ def _is_circuit_open(error: Exception) -> bool:
27
+ """Whether ``error`` is a fast-fail from an open circuit breaker.
28
+
29
+ An open breaker is an expected, already-logged condition (the
30
+ CLOSED -> OPEN transition is logged once), so per-call rejections are
31
+ logged quietly to avoid flooding logs during an outage window.
32
+
33
+ Args:
34
+ error: The exception raised by ``exec_grpc_query``.
35
+
36
+ Returns:
37
+ True if the error's cause is a ``CircuitOpenError``.
38
+ """
39
+ return isinstance(error.__cause__, CircuitOpenError)
40
+
24
41
  def _build_record_from_proto(self, proto: data_pb2.StorageRecord) -> StorageRecord:
25
42
  """Convert a protobuf StorageRecord message into our Pydantic model.
26
43
 
@@ -83,11 +100,10 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
83
100
  resp = await self.exec_grpc_query("StoreRecord", req)
84
101
  return self._build_record_from_proto(resp.stored_data)
85
102
  except Exception as e:
86
- logger.exception(
87
- "gRPC StoreRecord failed for %s:%s",
88
- record.collection,
89
- record.record_id,
90
- )
103
+ if self._is_circuit_open(e):
104
+ logger.debug("gRPC StoreRecord skipped (circuit open) for %s:%s", record.collection, record.record_id)
105
+ else:
106
+ logger.exception("gRPC StoreRecord failed for %s:%s", record.collection, record.record_id)
91
107
  raise StorageServiceError(str(e)) from e
92
108
 
93
109
  async def _read(self, collection: str, record_id: str) -> StorageRecord | None:
@@ -105,8 +121,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
105
121
  )
106
122
  resp = await self.exec_grpc_query("ReadRecord", req)
107
123
  return self._build_record_from_proto(resp.stored_data)
108
- except Exception:
109
- logger.debug("gRPC ReadRecord failed for %s:%s", collection, record_id)
124
+ except Exception as e:
125
+ if self._is_circuit_open(e):
126
+ logger.debug("gRPC ReadRecord skipped (circuit open) for %s:%s", collection, record_id)
127
+ else:
128
+ logger.info("gRPC ReadRecord failed for %s:%s: %s", collection, record_id, e)
110
129
  return None
111
130
 
112
131
  async def _update(
@@ -137,8 +156,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
137
156
  )
138
157
  resp = await self.exec_grpc_query("UpdateRecord", req)
139
158
  return self._build_record_from_proto(resp.stored_data)
140
- except Exception:
141
- logger.warning("gRPC UpdateRecord failed for %s:%s", collection, record_id)
159
+ except Exception as e:
160
+ if self._is_circuit_open(e):
161
+ logger.debug("gRPC UpdateRecord skipped (circuit open) for %s:%s", collection, record_id)
162
+ else:
163
+ logger.warning("gRPC UpdateRecord failed for %s:%s: %s", collection, record_id, e)
142
164
  return None
143
165
 
144
166
  async def _remove(self, collection: str, record_id: str) -> bool:
@@ -159,12 +181,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
159
181
  record_id=record_id,
160
182
  )
161
183
  await self.exec_grpc_query("RemoveRecord", req)
162
- except Exception:
163
- logger.warning(
164
- "gRPC RemoveRecord failed for %s:%s",
165
- collection,
166
- record_id,
167
- )
184
+ except Exception as e:
185
+ if self._is_circuit_open(e):
186
+ logger.debug("gRPC RemoveRecord skipped (circuit open) for %s:%s", collection, record_id)
187
+ else:
188
+ logger.warning("gRPC RemoveRecord failed for %s:%s: %s", collection, record_id, e)
168
189
  return False
169
190
  return True
170
191
 
@@ -185,8 +206,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
185
206
  )
186
207
  resp = await self.exec_grpc_query("ListRecords", req)
187
208
  return [self._build_record_from_proto(r) for r in resp.records]
188
- except Exception:
189
- logger.warning("gRPC ListRecords failed for %s", collection)
209
+ except Exception as e:
210
+ if self._is_circuit_open(e):
211
+ logger.debug("gRPC ListRecords skipped (circuit open) for %s", collection)
212
+ else:
213
+ logger.warning("gRPC ListRecords failed for %s: %s", collection, e)
190
214
  return []
191
215
 
192
216
  async def _remove_collection(self, collection: str) -> bool:
@@ -204,8 +228,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
204
228
  collection=collection,
205
229
  )
206
230
  await self.exec_grpc_query("RemoveCollection", req)
207
- except Exception:
208
- logger.warning("gRPC RemoveCollection failed for %s", collection)
231
+ except Exception as e:
232
+ if self._is_circuit_open(e):
233
+ logger.debug("gRPC RemoveCollection skipped (circuit open) for %s", collection)
234
+ else:
235
+ logger.warning("gRPC RemoveCollection failed for %s: %s", collection, e)
209
236
  return False
210
237
  return True
211
238
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 1.0.0.dev13
3
+ Version: 1.0.0.dev15
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
@@ -452,7 +452,7 @@ Classifier: Topic :: Software Development :: Libraries
452
452
  Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
- Requires-Dist: ag-ui-protocol>=0.1.18
455
+ Requires-Dist: ag-ui-protocol>=0.1.19
456
456
  Requires-Dist: agentic-mesh-protocol==1.0.0.dev2
457
457
  Requires-Dist: anyio>=4.13.0
458
458
  Requires-Dist: grpcio-health-checking==1.80.0
@@ -1,4 +1,4 @@
1
- ag-ui-protocol>=0.1.18
1
+ ag-ui-protocol>=0.1.19
2
2
  agentic-mesh-protocol==1.0.0.dev2
3
3
  anyio>=4.13.0
4
4
  grpcio-health-checking==1.80.0
@@ -1,16 +0,0 @@
1
- """Exceptions for the DigitalKin core package."""
2
-
3
-
4
- class BackpressureTimeoutError(Exception):
5
- """Producer's XADD throttled past the backpressure timeout.
6
-
7
- Throttled past :data:`GatewayBackpressureSettings.backpressure_timeout_s`.
8
- Caller (typically the module's ``_on_output`` callback) must surface
9
- this as ``stream.error(code=BACKPRESSURE_TIMEOUT)`` via the
10
- ``_emit_fatal_to_redis`` path so the consumer sees a typed sentinel
11
- instead of a silent stall.
12
- """
13
-
14
-
15
- class BulkheadFullError(Exception):
16
- """Raised when a bulkhead semaphore cannot be acquired within timeout."""