digitalkin 1.0.0.dev18__tar.gz → 1.0.0.dev19__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 (231) hide show
  1. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/PKG-INFO +1 -1
  2. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/pyproject.toml +1 -1
  3. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/__version__.py +1 -1
  4. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/module_runner.py +12 -1
  5. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/gateway_servicer.py +102 -17
  6. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +16 -0
  7. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +62 -53
  8. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/grpc_servers/stream_error_codes.py +1 -0
  9. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin.egg-info/PKG-INFO +1 -1
  10. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/LICENSE +0 -0
  11. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/README.md +0 -0
  12. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/__init__.py +0 -0
  13. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/mock/__init__.py +0 -0
  14. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/mock/mock_pb2.py +0 -0
  15. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  16. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/server_async_insecure.py +0 -0
  17. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/server_async_secure.py +0 -0
  18. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/server_sync_insecure.py +0 -0
  19. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/base_server/server_sync_secure.py +0 -0
  20. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/__init__.py +0 -0
  21. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/echo_module.py +0 -0
  22. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/models/__init__.py +0 -0
  23. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/models/input.py +0 -0
  24. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/models/output.py +0 -0
  25. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/models/secret.py +0 -0
  26. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/models/setup.py +0 -0
  27. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/server.py +0 -0
  28. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/triggers/__init__.py +0 -0
  29. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/bench_module/triggers/message_trigger.py +0 -0
  30. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/modules/__init__.py +0 -0
  31. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/modules/archetype_with_tools_module.py +0 -0
  32. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/modules/cpu_intensive_module.py +0 -0
  33. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/modules/dynamic_setup_module.py +0 -0
  34. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/modules/minimal_llm_module.py +0 -0
  35. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/modules/text_transform_module.py +0 -0
  36. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
  37. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
  38. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
  39. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
  40. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
  41. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/monitoring/tests/test_metrics.py +0 -0
  42. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/client.py +0 -0
  43. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/echo_module.py +0 -0
  44. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/models/__init__.py +0 -0
  45. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/models/input.py +0 -0
  46. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/models/output.py +0 -0
  47. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/models/secret.py +0 -0
  48. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/models/setup.py +0 -0
  49. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/server.py +0 -0
  50. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/triggers/__init__.py +0 -0
  51. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/redis_demo/triggers/message_trigger.py +0 -0
  52. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/services/filesystem_module.py +0 -0
  53. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/examples/services/storage_module.py +0 -0
  54. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/setup.cfg +0 -0
  55. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/__init__.py +0 -0
  56. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/community/__init__.py +0 -0
  57. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/community/agno/__init__.py +0 -0
  58. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/community/agno/agno_adapter.py +0 -0
  59. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/community/agno/agui_tools.py +0 -0
  60. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/community/agno/hitl.py +0 -0
  61. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/community/agno/models.py +0 -0
  62. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/__init__.py +0 -0
  63. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/common/__init__.py +0 -0
  64. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/common/factories.py +0 -0
  65. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/exceptions.py +0 -0
  66. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/job_manager/__init__.py +0 -0
  67. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
  68. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
  69. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/profiling/__init__.py +0 -0
  70. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/profiling/step_timer.py +0 -0
  71. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/profiling/task_profiler.py +0 -0
  72. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/resilience/__init__.py +0 -0
  73. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/resilience/bulkhead.py +0 -0
  74. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
  75. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/__init__.py +0 -0
  76. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
  77. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
  78. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
  79. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
  80. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/redis/redis_client.py +0 -0
  81. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
  82. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
  83. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
  84. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
  85. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/task_executor.py +0 -0
  86. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/core/task_manager/task_session.py +0 -0
  87. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/exceptions.py +0 -0
  88. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/__init__.py +0 -0
  89. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  90. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/exceptions.py +0 -0
  91. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
  92. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/interceptors/request_ids.py +0 -0
  93. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
  94. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/module_server.py +0 -0
  95. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/module_servicer.py +0 -0
  96. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
  97. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/stream_session.py +0 -0
  98. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  99. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  100. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
  101. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
  102. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/logger.py +0 -0
  103. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/__init__.py +0 -0
  104. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/agui_mixin.py +0 -0
  105. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/base_mixin.py +0 -0
  106. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/cost_mixin.py +0 -0
  107. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  108. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  109. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/logger_mixin.py +0 -0
  110. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/mixins/storage_mixin.py +0 -0
  111. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/__init__.py +0 -0
  112. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/core/__init__.py +0 -0
  113. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/core/job_manager_models.py +0 -0
  114. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/core/redis.py +0 -0
  115. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/core/task_monitor.py +0 -0
  116. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/events/__init__.py +0 -0
  117. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/events/agent_events.py +0 -0
  118. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  119. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
  120. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
  121. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/grpc_servers/models.py +0 -0
  122. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/grpc_servers/types.py +0 -0
  123. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/__init__.py +0 -0
  124. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/ag_ui.py +0 -0
  125. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/base_types.py +0 -0
  126. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/module.py +0 -0
  127. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/module_context.py +0 -0
  128. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/module_types.py +0 -0
  129. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/request_metadata.py +0 -0
  130. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/select_schema.py +0 -0
  131. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/setup_types.py +0 -0
  132. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/tool_cache.py +0 -0
  133. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/tool_reference.py +0 -0
  134. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/module/utility.py +0 -0
  135. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/services/__init__.py +0 -0
  136. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/services/cost.py +0 -0
  137. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/services/registry.py +0 -0
  138. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/services/services.py +0 -0
  139. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/services/storage.py +0 -0
  140. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/__init__.py +0 -0
  141. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/gateway.py +0 -0
  142. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/grpc_client.py +0 -0
  143. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/log.py +0 -0
  144. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/module.py +0 -0
  145. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/profiling.py +0 -0
  146. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/queue.py +0 -0
  147. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/redis.py +0 -0
  148. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/resilience.py +0 -0
  149. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/server/__init__.py +0 -0
  150. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/server/channel.py +0 -0
  151. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/server/grpc.py +0 -0
  152. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/server/server.py +0 -0
  153. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/server/servicer.py +0 -0
  154. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/task_manager.py +0 -0
  155. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/utils/__init__.py +0 -0
  156. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/settings/utils/channel.py +0 -0
  157. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/utils/__init__.py +0 -0
  158. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
  159. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/__init__.py +0 -0
  160. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/_base_module.py +0 -0
  161. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/archetype_module.py +0 -0
  162. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/tool_module.py +0 -0
  163. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/trigger_handler.py +0 -0
  164. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/triggers/__init__.py +0 -0
  165. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
  166. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
  167. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
  168. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/py.typed +0 -0
  169. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/__init__.py +0 -0
  170. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/base_strategy.py +0 -0
  171. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/communication/__init__.py +0 -0
  172. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/communication/communication_strategy.py +0 -0
  173. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/communication/default_communication.py +0 -0
  174. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/communication/exceptions.py +0 -0
  175. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/communication/grpc_communication.py +0 -0
  176. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/cost/__init__.py +0 -0
  177. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  178. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/cost/default_cost.py +0 -0
  179. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/cost/exceptions.py +0 -0
  180. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/cost/grpc_cost.py +0 -0
  181. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/filesystem/__init__.py +0 -0
  182. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  183. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/filesystem/exceptions.py +0 -0
  184. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  185. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
  186. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/identity/__init__.py +0 -0
  187. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/identity/default_identity.py +0 -0
  188. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  189. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/registry/__init__.py +0 -0
  190. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/registry/default_registry.py +0 -0
  191. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/registry/exceptions.py +0 -0
  192. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/registry/grpc_registry.py +0 -0
  193. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/registry/registry_models.py +0 -0
  194. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/registry/registry_strategy.py +0 -0
  195. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/services_config.py +0 -0
  196. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/services_models.py +0 -0
  197. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/setup/__init__.py +0 -0
  198. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/setup/default_setup.py +0 -0
  199. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/setup/exceptions.py +0 -0
  200. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/setup/grpc_setup.py +0 -0
  201. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  202. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/storage/__init__.py +0 -0
  203. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/storage/default_storage.py +0 -0
  204. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/storage/exceptions.py +0 -0
  205. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/storage/grpc_storage.py +0 -0
  206. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  207. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/task_manager/__init__.py +0 -0
  208. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
  209. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/task_manager/exceptions.py +0 -0
  210. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
  211. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
  212. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/user_profile/__init__.py +0 -0
  213. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  214. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/user_profile/exceptions.py +0 -0
  215. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
  216. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
  217. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/__init__.py +0 -0
  218. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/arg_parser.py +0 -0
  219. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/conditional_schema.py +0 -0
  220. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/development_mode_action.py +0 -0
  221. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/dynamic_schema.py +0 -0
  222. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/exceptions.py +0 -0
  223. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  224. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/package_discover.py +0 -0
  225. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/proto_utils.py +0 -0
  226. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin/utils/schema_splitter.py +0 -0
  227. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin.egg-info/SOURCES.txt +0 -0
  228. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  229. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin.egg-info/requires.txt +0 -0
  230. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/src/digitalkin.egg-info/top_level.txt +0 -0
  231. {digitalkin-1.0.0.dev18 → digitalkin-1.0.0.dev19}/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.dev18
3
+ Version: 1.0.0.dev19
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
@@ -37,7 +37,7 @@
37
37
  "pydantic-settings>=2.14.1",
38
38
  "redis[hiredis]>=7.4.0,<9",
39
39
  ]
40
- version = "1.0.0.dev18"
40
+ version = "1.0.0.dev19"
41
41
 
42
42
  [project.optional-dependencies]
43
43
  performance = [ "uvloop>=0.21" ]
@@ -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.dev18"
8
+ __version__ = "1.0.0.dev19"
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from google.protobuf import json_format, struct_pb2
10
10
  from pydantic import ValidationError
11
+ from redis.exceptions import RedisError
11
12
 
12
13
  from digitalkin.core.exceptions import BackpressureTimeoutError
13
14
  from digitalkin.core.profiling.step_timer import StepTimer
@@ -41,7 +42,7 @@ class ModuleRunner:
41
42
  self._redis_client = redis_client
42
43
  self._servicer = servicer
43
44
 
44
- async def run( # noqa: PLR0914, PLR0915
45
+ async def run( # noqa: C901, PLR0914, PLR0915
45
46
  self,
46
47
  query: struct_pb2.Struct,
47
48
  *,
@@ -215,6 +216,16 @@ class ModuleRunner:
215
216
  except BackpressureTimeoutError as exc:
216
217
  logger.exception("ModuleRunner: backpressure timeout", extra=log_extra)
217
218
  await on_fatal(StreamErrorCode.BACKPRESSURE_TIMEOUT.value, str(exc))
219
+ except RedisError as exc:
220
+ logger.error(
221
+ "[VALIDATE R2] ModuleRunner: Redis unavailable writing output: task_id=%s",
222
+ task_id,
223
+ extra=log_extra,
224
+ ) # TODO(validate): remove after prod validation
225
+ await on_fatal(
226
+ StreamErrorCode.REDIS_UNAVAILABLE.value,
227
+ f"redis unavailable: {type(exc).__name__}: {exc}",
228
+ )
218
229
  except Exception as exc:
219
230
  logger.exception("ModuleRunner: module job failed", extra=log_extra)
220
231
  await on_fatal(
@@ -157,7 +157,7 @@ class GatewayServicer:
157
157
  await self._m2m.stop()
158
158
  await self._registry.shutdown()
159
159
 
160
- async def StartStream( # noqa: PLR0911, PLR0915
160
+ async def StartStream( # noqa: C901, PLR0911, PLR0915
161
161
  self,
162
162
  request: Any,
163
163
  context: grpc.aio.ServicerContext,
@@ -215,14 +215,30 @@ class GatewayServicer:
215
215
  # Durable at-most-once guard: survives session teardown and spans replicas.
216
216
  # A re-issued StartStream for an already-run task is accepted but does NOT
217
217
  # re-dial/re-execute — the caller resumes the existing output via Stream/from_seq.
218
- claim = await self._idempotency.claim(task_id, SharedRedisListener.PROCESS_ID)
218
+ try:
219
+ claim = await self._idempotency.claim(task_id, SharedRedisListener.PROCESS_ID)
220
+ except RedisError:
221
+ logger.warning(
222
+ "[VALIDATE R2] StartStream claim Redis failure → accepted=False",
223
+ extra=log_extra,
224
+ exc_info=True,
225
+ ) # TODO(validate): remove after prod validation
226
+ return gateway_pb2.StartStreamResponse(accepted=False, task_id=task_id)
219
227
  timer.mark("idempotency_claim")
220
228
  if claim is not ClaimResult.CLAIMED:
221
229
  # Re-issued StartStream for an already-claimed task. The prior dial-back
222
230
  # is dead (the dedup branch above returned if its session were still live),
223
231
  # so re-dial in resume mode to re-attach the restarted consumer from its
224
232
  # cursor — as long as the durable stream is still retained.
225
- stream_len = await self._redis_client.xlen(f"task:{task_id}:stream")
233
+ try:
234
+ stream_len = await self._redis_client.xlen(f"task:{task_id}:stream")
235
+ except RedisError:
236
+ logger.warning(
237
+ "[VALIDATE R2] StartStream xlen Redis failure → accepted=False",
238
+ extra=log_extra,
239
+ exc_info=True,
240
+ ) # TODO(validate): remove after prod validation
241
+ return gateway_pb2.StartStreamResponse(accepted=False, task_id=task_id)
226
242
  if stream_len == 0:
227
243
  logger.info(
228
244
  "[VALIDATE IDEM1] no live stream to resume (TTL expired) task_id=%s (%s)",
@@ -274,7 +290,8 @@ class GatewayServicer:
274
290
  if not accepted:
275
291
  logger.warning("Session rejected (capacity)", extra=log_extra)
276
292
  # Release the claim so this task can be retried once capacity frees up.
277
- await self._idempotency.release(task_id)
293
+ with contextlib.suppress(RedisError):
294
+ await self._idempotency.release(task_id)
278
295
  return gateway_pb2.StartStreamResponse(accepted=False, task_id=task_id)
279
296
 
280
297
  # Seed stream.start so the consumer's first XREAD finds data immediately.
@@ -289,10 +306,22 @@ class GatewayServicer:
289
306
  },
290
307
  })
291
308
  timer.mark("build_start_info")
292
- await self._redis_client.xadd(
293
- f"task:{task_id}:stream",
294
- {"pb": start_info.SerializeToString(), "seq": "0"},
295
- )
309
+ try:
310
+ await self._redis_client.xadd(
311
+ f"task:{task_id}:stream",
312
+ {"pb": start_info.SerializeToString(), "seq": "0"},
313
+ )
314
+ except RedisError:
315
+ logger.warning(
316
+ "[VALIDATE R2] StartStream xadd(stream.start) Redis failure → accepted=False",
317
+ extra=log_extra,
318
+ exc_info=True,
319
+ ) # TODO(validate): remove after prod validation
320
+ # Claimed + registered but the stream couldn't be seeded: undo both so a retry re-runs.
321
+ with contextlib.suppress(RedisError):
322
+ await self._idempotency.release(task_id)
323
+ await self._registry.unregister(task_id)
324
+ return gateway_pb2.StartStreamResponse(accepted=False, task_id=task_id)
296
325
  timer.mark("xadd_stream_start")
297
326
 
298
327
  logger.info("→ Dial-back scheduled to consumer %s", client_address, extra=log_extra)
@@ -424,7 +453,14 @@ class GatewayServicer:
424
453
 
425
454
  # Late client: session finished but data still in Redis.
426
455
  if session is None:
427
- stream_len = await self._redis_client.xlen(f"task:{task_id}:stream")
456
+ try:
457
+ stream_len = await self._redis_client.xlen(f"task:{task_id}:stream")
458
+ except RedisError:
459
+ async for out in self._fatal_close(
460
+ task_id, StreamErrorCode.REDIS_UNAVAILABLE.value, "redis unavailable"
461
+ ):
462
+ yield out
463
+ return
428
464
  if stream_len > 0:
429
465
  async for resp in self._consume_guarded(task_id, from_seq):
430
466
  yield resp
@@ -436,10 +472,17 @@ class GatewayServicer:
436
472
  # First message's data is the query.
437
473
  input_key = f"task:{task_id}:input"
438
474
  if first_msg.data and len(first_msg.data.fields) > 0:
439
- await self._redis_client.xadd(
440
- input_key,
441
- {"pb": first_msg.data.SerializeToString()},
442
- )
475
+ try:
476
+ await self._redis_client.xadd(
477
+ input_key,
478
+ {"pb": first_msg.data.SerializeToString()},
479
+ )
480
+ except RedisError:
481
+ async for out in self._fatal_close(
482
+ task_id, StreamErrorCode.REDIS_UNAVAILABLE.value, "redis unavailable"
483
+ ):
484
+ yield out
485
+ return
443
486
 
444
487
  upstream_task = self._spawn(
445
488
  self._read_peer_upstream(request_iterator, task_id, session),
@@ -703,6 +746,21 @@ class GatewayServicer:
703
746
  )
704
747
  yield self._sentinel(0, task_id, "stream.end")
705
748
  return
749
+ except RedisError:
750
+ logger.warning(
751
+ "[VALIDATE R2] stream read Redis failure — closing stream: task_id=%s",
752
+ task_id,
753
+ ) # TODO(validate): remove after prod validation
754
+ yield self._sentinel(
755
+ 0,
756
+ task_id,
757
+ "stream.error",
758
+ code=StreamErrorCode.REDIS_UNAVAILABLE.value,
759
+ message="redis unavailable during stream read",
760
+ fatal=True,
761
+ )
762
+ yield self._sentinel(0, task_id, "stream.end")
763
+ return
706
764
  yield resp
707
765
 
708
766
  async def _dial_consumer( # noqa: C901, PLR0912, PLR0914, PLR0915
@@ -869,6 +927,25 @@ class GatewayServicer:
869
927
  f"dial-back idle timeout: no module output in {idle_timeout:.0f}s",
870
928
  )
871
929
  return
930
+ except RedisError:
931
+ logger.warning(
932
+ "[VALIDATE R2] dial-back stream read Redis failure — closing BiDi: task_id=%s",
933
+ task_id,
934
+ extra=log_extra,
935
+ ) # TODO(validate): remove after prod validation
936
+ for sc in (
937
+ self._sentinel(
938
+ 0,
939
+ task_id,
940
+ "stream.error",
941
+ code=StreamErrorCode.REDIS_UNAVAILABLE.value,
942
+ message="redis unavailable during stream read",
943
+ fatal=True,
944
+ ),
945
+ self._sentinel(0, task_id, "stream.end"),
946
+ ):
947
+ yield gateway_pb2.StreamServer(task_id=task_id, seq=sc.from_seq, data=sc.data)
948
+ return
872
949
  yield gateway_pb2.StreamServer(
873
950
  task_id=task_id,
874
951
  seq=cli_msg.from_seq,
@@ -968,10 +1045,18 @@ class GatewayServicer:
968
1045
  first = False
969
1046
  continue
970
1047
  # Follow-up multi-turn input → task's input stream.
971
- await self._redis_client.xadd(
972
- f"task:{task_id}:input",
973
- {"pb": upstream.data.SerializeToString()},
974
- )
1048
+ try:
1049
+ await self._redis_client.xadd(
1050
+ f"task:{task_id}:input",
1051
+ {"pb": upstream.data.SerializeToString()},
1052
+ )
1053
+ except RedisError:
1054
+ logger.warning(
1055
+ "[VALIDATE R2] dropped follow-up input (redis unavailable): task_id=%s data=%s",
1056
+ task_id,
1057
+ str(upstream.data)[:512],
1058
+ extra=log_extra,
1059
+ ) # TODO(validate): remove after prod validation
975
1060
  except grpc.aio.AioRpcError as exc:
976
1061
  code_name = exc.code().name
977
1062
  details = exc.details() or ""
@@ -134,6 +134,22 @@ class CircuitBreaker:
134
134
  self._failure_count,
135
135
  )
136
136
 
137
+ def release_probe(self) -> bool:
138
+ """Release a half-open probe slot without recording an outcome.
139
+
140
+ Called when an in-flight probe is abandoned (e.g. the caller was
141
+ cancelled) so the breaker cannot wedge with the lock held. A cancelled
142
+ probe is neither success nor failure — the slot simply frees for the
143
+ next caller. No-op unless a probe lock is currently held.
144
+
145
+ Returns:
146
+ True if a held probe lock was released; False if none was held.
147
+ """
148
+ if self._state == CBState.HALF_OPEN and self._half_open_lock:
149
+ self._half_open_lock = False
150
+ return True
151
+ return False
152
+
137
153
  def reset(self) -> None:
138
154
  """Force reset to CLOSED state."""
139
155
  self._state = CBState.CLOSED
@@ -224,7 +224,7 @@ class GrpcClientWrapper:
224
224
  cls._stub_cache.clear()
225
225
  CircuitBreaker.clear_all()
226
226
 
227
- async def exec_grpc_query( # noqa: C901, PLR0914
227
+ async def exec_grpc_query( # noqa: C901, PLR0912, PLR0914, PLR0915
228
228
  self,
229
229
  query_endpoint: str,
230
230
  request: Any,
@@ -284,57 +284,66 @@ class GrpcClientWrapper:
284
284
  backoff_delays = tuple(backoff_base_ms / 1000 * (2**i) for i in range(max_retries))
285
285
  last_error: grpc.RpcError | None = None
286
286
 
287
- for attempt in range(max_retries + 1):
288
- if attempt > 0:
289
- await asyncio.sleep(backoff_delays[attempt - 1])
290
-
291
- try:
292
- response = await rpc_method(request, timeout=eff_timeout)
293
- except grpc.RpcError as e:
294
- last_error = e
295
- if e.code() in self._RETRYABLE_CODES and attempt < max_retries:
296
- logger.warning(
297
- "gRPC transient error on %s.%s [%s] (attempt %d/%d), retrying in %.0fms",
298
- self.service_name,
299
- query_endpoint,
300
- e.code().name,
301
- attempt + 1,
302
- max_retries + 1,
303
- backoff_delays[attempt] * 1000,
304
- )
305
- continue
306
- if e.code() in self._CIRCUIT_FAILURE_CODES:
307
- logger.warning(
308
- "circuit-breaker tick: %s.%s [%s]",
309
- self.service_name,
310
- query_endpoint,
311
- e.code().name,
312
- )
313
- cb.record_failure()
287
+ try:
288
+ for attempt in range(max_retries + 1):
289
+ if attempt > 0:
290
+ await asyncio.sleep(backoff_delays[attempt - 1])
291
+
292
+ try:
293
+ response = await rpc_method(request, timeout=eff_timeout)
294
+ except grpc.RpcError as e:
295
+ last_error = e
296
+ if e.code() in self._RETRYABLE_CODES and attempt < max_retries:
297
+ logger.warning(
298
+ "gRPC transient error on %s.%s [%s] (attempt %d/%d), retrying in %.0fms",
299
+ self.service_name,
300
+ query_endpoint,
301
+ e.code().name,
302
+ attempt + 1,
303
+ max_retries + 1,
304
+ backoff_delays[attempt] * 1000,
305
+ )
306
+ continue
307
+ if e.code() in self._CIRCUIT_FAILURE_CODES:
308
+ logger.warning(
309
+ "circuit-breaker tick: %s.%s [%s]",
310
+ self.service_name,
311
+ query_endpoint,
312
+ e.code().name,
313
+ )
314
+ cb.record_failure()
315
+ else:
316
+ cb.record_success()
317
+ break
314
318
  else:
315
319
  cb.record_success()
316
- break
317
- else:
318
- cb.record_success()
319
- return response
320
-
321
- if last_error is None:
322
- msg = f"[gRPC-client:{self.service_name}.{query_endpoint}] Retry loop exited without response or error"
323
- raise ServerError(msg)
324
- status_code = last_error.code().name
325
- details = last_error.details()
326
- retried = last_error.code() in self._RETRYABLE_CODES
327
- suffix = f" (after {max_retries + 1} attempts)" if retried else ""
328
-
329
- log_level = logging.DEBUG if last_error.code() == grpc.StatusCode.NOT_FOUND else logging.ERROR
330
- logger.log(
331
- log_level,
332
- "gRPC call failed: %s.%s [%s] %s (%s)",
333
- self.service_name,
334
- query_endpoint,
335
- status_code,
336
- details,
337
- type(request).__name__,
338
- )
339
- error_msg = f"[gRPC-client:{self.service_name}.{query_endpoint}] [{status_code}] {details}{suffix}"
340
- raise ServerError(error_msg) from last_error
320
+ return response
321
+
322
+ if last_error is None:
323
+ msg = f"[gRPC-client:{self.service_name}.{query_endpoint}] Retry loop exited without response or error"
324
+ raise ServerError(msg)
325
+ status_code = last_error.code().name
326
+ details = last_error.details()
327
+ retried = last_error.code() in self._RETRYABLE_CODES
328
+ suffix = f" (after {max_retries + 1} attempts)" if retried else ""
329
+
330
+ log_level = logging.DEBUG if last_error.code() == grpc.StatusCode.NOT_FOUND else logging.ERROR
331
+ logger.log(
332
+ log_level,
333
+ "gRPC call failed: %s.%s [%s] %s (%s)",
334
+ self.service_name,
335
+ query_endpoint,
336
+ status_code,
337
+ details,
338
+ type(request).__name__,
339
+ )
340
+ error_msg = f"[gRPC-client:{self.service_name}.{query_endpoint}] [{status_code}] {details}{suffix}"
341
+ raise ServerError(error_msg) from last_error
342
+ finally:
343
+ # Free a half-open probe the loop never resolved (e.g. CancelledError mid-call or backoff).
344
+ if cb.release_probe():
345
+ logger.info(
346
+ "[VALIDATE R1] released abandoned circuit probe: %s.%s",
347
+ self.service_name,
348
+ query_endpoint,
349
+ ) # TODO(validate): remove after prod validation
@@ -19,6 +19,7 @@ class StreamErrorCode(str, Enum):
19
19
  DIAL_BACK_NO_QUERY = "DIAL_BACK_NO_QUERY"
20
20
  DIAL_BACK_IDLE_TIMEOUT = "DIAL_BACK_IDLE_TIMEOUT"
21
21
  STREAM_IDLE_TIMEOUT = "STREAM_IDLE_TIMEOUT"
22
+ REDIS_UNAVAILABLE = "REDIS_UNAVAILABLE"
22
23
  MODULE_RUNTIME_ERROR = "MODULE_RUNTIME_ERROR"
23
24
  INPUT_VALIDATION_ERROR = "INPUT_VALIDATION_ERROR"
24
25
  BACKPRESSURE_TIMEOUT = "BACKPRESSURE_TIMEOUT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 1.0.0.dev18
3
+ Version: 1.0.0.dev19
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