digitalkin 0.3.5.dev12__tar.gz → 0.3.5.dev13__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 (173) hide show
  1. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/PKG-INFO +3 -3
  2. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/modules/archetype_with_tools_module.py +3 -2
  3. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/pyproject.toml +12 -12
  4. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/__version__.py +1 -1
  5. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/community/agno/agno_adapter.py +12 -25
  6. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/job_manager/base_job_manager.py +6 -0
  7. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/job_manager/single_job_manager.py +1 -4
  8. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/job_manager/taskiq_broker.py +211 -13
  9. digitalkin-0.3.5.dev13/src/digitalkin/core/job_manager/taskiq_job_manager.py +659 -0
  10. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/task_manager/task_session.py +2 -0
  11. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/_base_server.py +56 -48
  12. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/module_server.py +37 -12
  13. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/module_servicer.py +42 -3
  14. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +35 -10
  15. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/__init__.py +0 -4
  16. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/agui_mixin.py +3 -3
  17. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/base_mixin.py +2 -2
  18. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/callback_mixin.py +19 -5
  19. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/chat_history_mixin.py +14 -8
  20. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/grpc_servers/models.py +2 -138
  21. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/__init__.py +0 -2
  22. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/module_context.py +2 -18
  23. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/setup_types.py +124 -13
  24. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/tool_cache.py +51 -45
  25. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/tool_reference.py +17 -9
  26. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/__init__.py +1 -0
  27. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/server/__init__.py +1 -0
  28. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/server/channel.py +36 -0
  29. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/server/grpc.py +99 -0
  30. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/server/server.py +47 -0
  31. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/utils/__init__.py +1 -0
  32. digitalkin-0.3.5.dev13/src/digitalkin/models/settings/utils/channel.py +148 -0
  33. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/_base_module.py +38 -15
  34. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +1 -1
  35. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +2 -2
  36. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +1 -1
  37. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/registry/registry_strategy.py +12 -0
  38. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/conditional_schema.py +2 -3
  39. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin.egg-info/PKG-INFO +3 -3
  40. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin.egg-info/SOURCES.txt +7 -0
  41. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin.egg-info/requires.txt +2 -2
  42. digitalkin-0.3.5.dev12/src/digitalkin/core/job_manager/taskiq_job_manager.py +0 -477
  43. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/LICENSE +0 -0
  44. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/README.md +0 -0
  45. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/__init__.py +0 -0
  46. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/mock/__init__.py +0 -0
  47. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/mock/mock_pb2.py +0 -0
  48. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  49. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/server_async_insecure.py +0 -0
  50. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/server_async_secure.py +0 -0
  51. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/server_sync_insecure.py +0 -0
  52. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/base_server/server_sync_secure.py +0 -0
  53. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/modules/__init__.py +0 -0
  54. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/modules/cpu_intensive_module.py +0 -0
  55. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/modules/dynamic_setup_module.py +0 -0
  56. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/modules/minimal_llm_module.py +0 -0
  57. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/modules/text_transform_module.py +0 -0
  58. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
  59. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
  60. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
  61. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
  62. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
  63. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/monitoring/tests/test_metrics.py +0 -0
  64. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/services/filesystem_module.py +0 -0
  65. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/examples/services/storage_module.py +0 -0
  66. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/setup.cfg +0 -0
  67. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/__init__.py +0 -0
  68. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/community/__init__.py +0 -0
  69. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/community/agno/__init__.py +0 -0
  70. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/__init__.py +0 -0
  71. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/common/__init__.py +0 -0
  72. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/common/factories.py +0 -0
  73. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/job_manager/__init__.py +0 -0
  74. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/profiling/__init__.py +0 -0
  75. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/profiling/asyncio_monitor.py +0 -0
  76. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/profiling/task_profiler.py +0 -0
  77. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/task_manager/__init__.py +0 -0
  78. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
  79. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
  80. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
  81. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/core/task_manager/task_executor.py +0 -0
  82. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/__init__.py +0 -0
  83. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  84. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/utils/exceptions.py +0 -0
  85. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  86. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
  87. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/logger.py +0 -0
  88. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/cost_mixin.py +0 -0
  89. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  90. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  91. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/logger_mixin.py +0 -0
  92. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/mixins/storage_mixin.py +0 -0
  93. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/__init__.py +0 -0
  94. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/core/__init__.py +0 -0
  95. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/core/job_manager_models.py +0 -0
  96. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/core/task_monitor.py +0 -0
  97. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/events/__init__.py +0 -0
  98. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/events/agent_events.py +0 -0
  99. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  100. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/grpc_servers/types.py +0 -0
  101. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/ag_ui.py +0 -0
  102. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/base_types.py +0 -0
  103. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/module.py +0 -0
  104. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/module_types.py +0 -0
  105. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/request_metadata.py +0 -0
  106. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/select_schema.py +0 -0
  107. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/module/utility.py +0 -0
  108. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/services/__init__.py +0 -0
  109. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/services/cost.py +0 -0
  110. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/services/registry.py +0 -0
  111. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/models/services/storage.py +0 -0
  112. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/__init__.py +0 -0
  113. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/archetype_module.py +0 -0
  114. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/tool_module.py +0 -0
  115. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/trigger_handler.py +0 -0
  116. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/modules/triggers/__init__.py +0 -0
  117. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/py.typed +0 -0
  118. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/__init__.py +0 -0
  119. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/agent/__init__.py +0 -0
  120. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/agent/agent_strategy.py +0 -0
  121. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/agent/default_agent.py +0 -0
  122. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/base_strategy.py +0 -0
  123. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/communication/__init__.py +0 -0
  124. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/communication/communication_strategy.py +0 -0
  125. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/communication/default_communication.py +0 -0
  126. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/communication/grpc_communication.py +0 -0
  127. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/cost/__init__.py +0 -0
  128. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  129. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/cost/default_cost.py +0 -0
  130. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/cost/grpc_cost.py +0 -0
  131. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/filesystem/__init__.py +0 -0
  132. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  133. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  134. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
  135. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/identity/__init__.py +0 -0
  136. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/identity/default_identity.py +0 -0
  137. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  138. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/registry/__init__.py +0 -0
  139. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/registry/default_registry.py +0 -0
  140. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/registry/exceptions.py +0 -0
  141. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/registry/grpc_registry.py +0 -0
  142. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/registry/registry_models.py +0 -0
  143. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/services_config.py +0 -0
  144. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/services_models.py +0 -0
  145. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/setup/__init__.py +0 -0
  146. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/setup/default_setup.py +0 -0
  147. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/setup/grpc_setup.py +0 -0
  148. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  149. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/snapshot/__init__.py +0 -0
  150. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/snapshot/default_snapshot.py +0 -0
  151. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/snapshot/snapshot_strategy.py +0 -0
  152. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/storage/__init__.py +0 -0
  153. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/storage/default_storage.py +0 -0
  154. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/storage/grpc_storage.py +0 -0
  155. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  156. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/task_manager/__init__.py +0 -0
  157. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
  158. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/task_manager/grpc_task_manager.py +0 -0
  159. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
  160. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/user_profile/__init__.py +0 -0
  161. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  162. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
  163. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
  164. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/__init__.py +0 -0
  165. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/arg_parser.py +0 -0
  166. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/development_mode_action.py +0 -0
  167. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/dynamic_schema.py +0 -0
  168. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  169. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/package_discover.py +0 -0
  170. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/proto_utils.py +0 -0
  171. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin/utils/schema_splitter.py +0 -0
  172. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  173. {digitalkin-0.3.5.dev12 → digitalkin-0.3.5.dev13}/src/digitalkin.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.3.5.dev12
3
+ Version: 0.3.5.dev13
4
4
  Summary: SDK to build kin used in DigitalKin
5
5
  Author-email: "DigitalKin.ai" <contact@digitalkin.ai>
6
6
  License: Attribution-NonCommercial-ShareAlike 4.0 International
@@ -454,7 +454,7 @@ Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
455
  Requires-Dist: ag-ui-protocol>=0.1.14
456
456
  Requires-Dist: agentic-mesh-protocol==0.2.3
457
- Requires-Dist: anyio==4.12.1
457
+ Requires-Dist: anyio==4.13.0
458
458
  Requires-Dist: grpcio-health-checking==1.78.0
459
459
  Requires-Dist: grpcio-reflection==1.78.0
460
460
  Requires-Dist: grpcio-status==1.78.0
@@ -463,7 +463,7 @@ Provides-Extra: profiling
463
463
  Requires-Dist: asyncio-inspector==0.1.0; extra == "profiling"
464
464
  Requires-Dist: pyinstrument==5.1.2; extra == "profiling"
465
465
  Requires-Dist: viztracer==1.1.1; extra == "profiling"
466
- Requires-Dist: yappi==1.7.3; extra == "profiling"
466
+ Requires-Dist: yappi==1.7.6; extra == "profiling"
467
467
  Provides-Extra: taskiq
468
468
  Requires-Dist: rstream==1.0.0; extra == "taskiq"
469
469
  Requires-Dist: taskiq-aio-pika==0.6.0; extra == "taskiq"
@@ -5,10 +5,11 @@ from typing import Any, ClassVar, Literal
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
8
- from digitalkin.models.grpc_servers.models import ClientConfig, SecurityMode, ServerMode
8
+ from digitalkin.models.grpc_servers.models import ClientConfig
9
9
  from digitalkin.models.module.module_context import ModuleContext
10
10
  from digitalkin.models.module.setup_types import SetupModel
11
11
  from digitalkin.models.module.tool_reference import ToolReference
12
+ from digitalkin.models.settings.utils.channel import ControlFlow, SecurityMode
12
13
  from digitalkin.modules._base_module import BaseModule # noqa: PLC2701
13
14
  from digitalkin.services.services_models import ServicesStrategy
14
15
 
@@ -98,7 +99,7 @@ class ArchetypeSecret(BaseModel):
98
99
  client_config = ClientConfig(
99
100
  host="[::]",
100
101
  port=50152,
101
- mode=ServerMode.ASYNC,
102
+ mode=ControlFlow.ASYNC,
102
103
  security=SecurityMode.INSECURE,
103
104
  credentials=None,
104
105
  )
@@ -29,20 +29,20 @@
29
29
  dependencies = [
30
30
  "ag-ui-protocol>=0.1.14",
31
31
  "agentic-mesh-protocol==0.2.3",
32
- "anyio==4.12.1",
32
+ "anyio==4.13.0",
33
33
  "grpcio-health-checking==1.78.0",
34
34
  "grpcio-reflection==1.78.0",
35
35
  "grpcio-status==1.78.0",
36
36
  "pydantic==2.12.5",
37
- ]
38
- version = "0.3.5.dev12"
37
+ ]
38
+ version = "0.3.5.dev13"
39
39
 
40
40
  [project.optional-dependencies]
41
41
  profiling = [
42
42
  "asyncio-inspector==0.1.0",
43
43
  "pyinstrument==5.1.2",
44
44
  "viztracer==1.1.1",
45
- "yappi==1.7.3",
45
+ "yappi==1.7.6",
46
46
  ]
47
47
  taskiq = [
48
48
  "rstream==1.0.0",
@@ -61,13 +61,13 @@
61
61
 
62
62
  [dependency-groups]
63
63
  dev = [
64
- "build==1.4.0",
64
+ "build==1.4.2",
65
65
  "bump-my-version==1.2.7",
66
- "cryptography==46.0.5",
66
+ "cryptography==46.0.6",
67
67
  "mypy==1.19.1",
68
68
  "pre-commit==4.5.1",
69
69
  "pyright==1.1.408",
70
- "ruff==0.15.5",
70
+ "ruff==0.15.8",
71
71
  "twine==6.2.0",
72
72
  "types-grpcio-health-checking==1.0.0.20250506",
73
73
  "types-grpcio-reflection==1.0.0.20250506",
@@ -87,17 +87,17 @@
87
87
  "mkdocs-git-revision-date-localized-plugin==1.5.1",
88
88
  "mkdocs-glightbox==0.5.2",
89
89
  "mkdocs-include-markdown-plugin==7.2.1",
90
- "mkdocs-literate-nav==0.6.2",
90
+ "mkdocs-literate-nav==0.6.3",
91
91
  "mkdocs-llmstxt==0.5.0",
92
- "mkdocs-material[imaging]==9.7.5",
92
+ "mkdocs-material[imaging]==9.7.6",
93
93
  "mkdocs-minify-plugin==0.8.0",
94
94
  "mkdocs-open-in-new-tab==1.0.8",
95
95
  "mkdocs-redirects==1.2.2",
96
- "mkdocs-section-index==0.3.10",
96
+ "mkdocs-section-index==0.3.11",
97
97
  "mkdocs==1.6.1",
98
98
  "mkdocstrings-python==2.0.3",
99
99
  "mkdocstrings==1.0.3",
100
- "tomli==2.4.0",
100
+ "tomli==2.4.1",
101
101
  ]
102
102
  tests = [
103
103
  "freezegun==1.5.5",
@@ -105,7 +105,7 @@
105
105
  "hdrhistogram==0.10.3",
106
106
  "psutil==7.2.2",
107
107
  "pytest-asyncio==1.3.0",
108
- "pytest-cov==7.0.0",
108
+ "pytest-cov==7.1.0",
109
109
  "pytest-html==4.2.0",
110
110
  "pytest-json-report==1.5.0",
111
111
  "pytest-timeout==2.4.0",
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("digitalkin")
7
7
  except PackageNotFoundError:
8
- __version__ = "0.3.5.dev12"
8
+ __version__ = "0.3.5.dev13"
@@ -86,7 +86,7 @@ class AgnoStreamAdapter:
86
86
 
87
87
  event_type = agno_event.event
88
88
 
89
- logger.info("[DK STREAM-DEBUG => agno_adapter] Converting Agno event type: %s", event_type)
89
+ logger.debug("Converting Agno event: %s", event_type)
90
90
 
91
91
  timestamp = getattr(agno_event, "timestamp", None)
92
92
 
@@ -97,7 +97,7 @@ class AgnoStreamAdapter:
97
97
 
98
98
  # Skip duplicate RunStarted for the same run_id
99
99
  if run_id and run_id == self._active_run_id:
100
- logger.info("[DK STREAM-DEBUG => agno_adapter] Skipping duplicate RunStarted for run_id=%s", run_id)
100
+ logger.debug("Skipping duplicate RunStarted for run_id=%s", run_id)
101
101
  return []
102
102
 
103
103
  self._active_run_id = run_id
@@ -119,7 +119,7 @@ class AgnoStreamAdapter:
119
119
 
120
120
  # Skip if this run was already completed and not reopened
121
121
  if run_id and run_id in self._completed_run_ids and run_id != self._active_run_id:
122
- logger.info("[DK STREAM-DEBUG => agno_adapter] Skipping duplicate RunCompleted for run_id=%s", run_id)
122
+ logger.debug("Skipping duplicate RunCompleted for run_id=%s", run_id)
123
123
  return []
124
124
 
125
125
  events: list[BaseAgentRunEvent] = []
@@ -169,10 +169,7 @@ class AgnoStreamAdapter:
169
169
 
170
170
  self._current_reasoning_id = str(uuid.uuid4())
171
171
  self._reasoning_active = True
172
- logger.info(
173
- "[DK STREAM-DEBUG => agno_adapter] Reasoning started (explicit), id=%s",
174
- self._current_reasoning_id,
175
- )
172
+ logger.debug("Reasoning started, id=%s", self._current_reasoning_id)
176
173
  events.append(
177
174
  ReasoningStartedEvent(
178
175
  event=AgentRunEvent.REASONING_STARTED,
@@ -208,7 +205,7 @@ class AgnoStreamAdapter:
208
205
  ]
209
206
 
210
207
  if event_type == RunEvent.reasoning_completed:
211
- logger.info("[DK STREAM-DEBUG => agno_adapter] Reasoning completed (explicit)")
208
+ logger.debug("Reasoning completed")
212
209
  return self._close_reasoning(timestamp)
213
210
 
214
211
  # ── Tool Call Events ─────────────────────────────────────────────
@@ -218,7 +215,7 @@ class AgnoStreamAdapter:
218
215
 
219
216
  # Close both reasoning and content before tool calls
220
217
  if self._reasoning_active:
221
- logger.info("[DK STREAM-DEBUG => agno_adapter] Reasoning auto-completed (tool call started)")
218
+ logger.debug("Reasoning auto-completed (tool call started)")
222
219
  events.extend(self._close_reasoning(timestamp))
223
220
  if self._content_active:
224
221
  events.extend(self._close_content(timestamp))
@@ -275,10 +272,7 @@ class AgnoStreamAdapter:
275
272
 
276
273
  # Skip if already closed by ToolCallCompleted (Agno emits both for the same tool_call_id)
277
274
  if tool_call_id and tool_call_id in self._closed_tool_call_ids:
278
- logger.info(
279
- "[DK STREAM-DEBUG => agno_adapter] Skipping duplicate ToolCallError for already-closed tool %s",
280
- tool_call_id,
281
- )
275
+ logger.debug("Skipping duplicate ToolCallError for tool %s", tool_call_id)
282
276
  return []
283
277
 
284
278
  tool_info = None
@@ -315,10 +309,10 @@ class AgnoStreamAdapter:
315
309
  """
316
310
  events: list[BaseAgentRunEvent] = []
317
311
  if self._content_active:
318
- logger.info("[DK STREAM-DEBUG => agno_adapter] Flushing: closing active content")
312
+ logger.debug("Flushing active content sequence")
319
313
  events.extend(self._close_content(None))
320
314
  if self._reasoning_active:
321
- logger.info("[DK STREAM-DEBUG => agno_adapter] Flushing: closing active reasoning")
315
+ logger.debug("Flushing active reasoning sequence")
322
316
  events.extend(self._close_reasoning(None))
323
317
  return events
324
318
 
@@ -394,7 +388,7 @@ class AgnoStreamAdapter:
394
388
 
395
389
  # Edge case: neither reasoning_content nor content
396
390
  if reasoning_content is None and content is None:
397
- logger.debug("[DK STREAM-DEBUG => agno_adapter] run_content with no content, skipping")
391
+ logger.debug("run_content with no content, skipping")
398
392
 
399
393
  return events
400
394
 
@@ -420,10 +414,7 @@ class AgnoStreamAdapter:
420
414
  # Auto-open reasoning on first chunk
421
415
  if not self._reasoning_active:
422
416
  self._current_reasoning_id = str(uuid.uuid4())
423
- logger.info(
424
- "[DK STREAM-DEBUG => agno_adapter] Reasoning auto-started from run_content, id=%s",
425
- self._current_reasoning_id,
426
- )
417
+ logger.debug("Reasoning auto-started, id=%s", self._current_reasoning_id)
427
418
  events.append(
428
419
  ReasoningStartedEvent(
429
420
  event=AgentRunEvent.REASONING_STARTED,
@@ -434,10 +425,6 @@ class AgnoStreamAdapter:
434
425
  )
435
426
  self._reasoning_active = True
436
427
 
437
- logger.info(
438
- "[DK STREAM-DEBUG => agno_adapter] Reasoning content delta (len=%d)",
439
- len(reasoning_content),
440
- )
441
428
  events.append(
442
429
  ReasoningContentDeltaEvent(
443
430
  event=AgentRunEvent.REASONING_CONTENT_DELTA,
@@ -466,7 +453,7 @@ class AgnoStreamAdapter:
466
453
  # Non-empty string → text data
467
454
  # Close reasoning if transitioning from reasoning to content
468
455
  if self._reasoning_active:
469
- logger.info("[DK STREAM-DEBUG => agno_adapter] Reasoning auto-completed (text content arrived)")
456
+ logger.debug("Reasoning auto-completed (text content arrived)")
470
457
  events.extend(self._close_reasoning(timestamp))
471
458
 
472
459
  # Auto-open text message on first chunk
@@ -130,6 +130,12 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
130
130
  required for the job manager to function.
131
131
  """
132
132
 
133
+ async def stop(self) -> None:
134
+ """Stop the job manager and clean up resources.
135
+
136
+ Default no-op. Subclasses with external connections override.
137
+ """
138
+
133
139
  @staticmethod
134
140
  async def job_specific_callback(
135
141
  callback: Callable[[str, DataModel | ModuleCodeModel], Coroutine[Any, Any, None]],
@@ -285,10 +285,7 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
285
285
  if session.cancelled:
286
286
  break
287
287
 
288
- try:
289
- yield _stream()
290
- finally:
291
- session.close_stream()
288
+ yield _stream()
292
289
 
293
290
  async def create_module_instance_job(
294
291
  self,
@@ -1,18 +1,20 @@
1
1
  """Taskiq broker & RSTREAM producer for the job manager."""
2
2
 
3
3
  import asyncio
4
- import json
5
4
  import logging
6
5
  import os
7
6
  import pickle
7
+ import ssl
8
8
  from typing import Any
9
9
 
10
10
  from rstream import Producer
11
11
  from rstream.exceptions import PreconditionFailed
12
12
  from taskiq import Context, TaskiqDepends, TaskiqMessage
13
13
  from taskiq.abc.formatter import TaskiqFormatter
14
+ from taskiq.abc.middleware import TaskiqMiddleware
14
15
  from taskiq.compat import model_validate
15
16
  from taskiq.message import BrokerMessage
17
+ from taskiq.result import TaskiqResult
16
18
  from taskiq_aio_pika import AioPikaBroker
17
19
 
18
20
  from digitalkin.core.common import ModuleFactory
@@ -61,27 +63,79 @@ class PickleFormatter(TaskiqFormatter):
61
63
  def loads(self, message: bytes) -> TaskiqMessage: # Required by TaskiqFormatter interface # noqa: PLR6301
62
64
  """Recreate Python object from bytes.
63
65
 
66
+ Non-pickle messages (e.g. raw JSON left in the queue by other producers)
67
+ are logged and converted to a no-op ``TaskiqMessage`` so that Taskiq
68
+ acknowledges (consumes) them instead of nack-ing and re-delivering in a loop.
69
+
64
70
  Args:
65
71
  message: Broker message from bytes.
66
72
 
67
73
  Returns:
68
74
  message with TaskIQ format
69
75
  """
70
- json_str = pickle.loads( # noqa: S301
71
- message
72
- ) # Pickle: required for Taskiq deserialization (internal broker messages only)
76
+ try:
77
+ json_str = pickle.loads( # noqa: S301
78
+ message
79
+ ) # Pickle: required for Taskiq deserialization (internal broker messages only)
80
+ except Exception as e:
81
+ logger.warning(
82
+ "Discarding non-pickle message (size=%d, preview=%r): %s",
83
+ len(message),
84
+ message[:80],
85
+ e,
86
+ )
87
+ # Return a no-op message that Taskiq will ack and discard
88
+ # (no task named "__discarded__" exists, so Taskiq logs a warning and moves on)
89
+ return TaskiqMessage(
90
+ task_id="__discarded__",
91
+ task_name="__discarded__",
92
+ labels={"_discarded": "true"},
93
+ args=[],
94
+ kwargs={},
95
+ )
73
96
  return model_validate(TaskiqMessage, json_str)
74
97
 
75
98
 
99
+ def _rstream_ssl_context() -> ssl.SSLContext | None:
100
+ """Create SSL context for RStream if TLS is enabled via RABBITMQ_RSTREAM_SSL=true.
101
+
102
+ Returns:
103
+ SSL context if TLS is enabled, None otherwise.
104
+ """
105
+ if os.environ.get("RABBITMQ_RSTREAM_SSL", "").lower() not in {"true", "1", "yes"}:
106
+ return None
107
+ ctx = ssl.create_default_context()
108
+ # Allow self-signed certs in staging if RABBITMQ_RSTREAM_SSL_VERIFY=false
109
+ if os.environ.get("RABBITMQ_RSTREAM_SSL_VERIFY", "true").lower() in {"false", "0", "no"}:
110
+ ctx.check_hostname = False
111
+ ctx.verify_mode = ssl.CERT_NONE
112
+ return ctx
113
+
114
+
76
115
  class TaskiqBrokerConfig:
77
116
  """Configuration and lifecycle management for Taskiq broker and RStream producer."""
78
117
 
79
118
  STREAM = "taskiq_data"
80
119
  STREAM_RETENTION = 200_000
81
120
 
121
+ @staticmethod
122
+ async def _on_producer_closed(reason: Any) -> None:
123
+ """Log RStream producer connection closure for diagnostics.
124
+
125
+ Args:
126
+ reason: Connection close reason from rstream.
127
+ """
128
+ logger.error("RStream producer connection closed: %s", reason)
129
+
82
130
  @staticmethod
83
131
  def define_producer() -> Producer:
84
- """Create RStream producer from environment variables.
132
+ """Create RStream producer with tuned settings for sustained throughput.
133
+
134
+ Tuning:
135
+ - ``default_batch_publishing_delay``: Flush batches every 100ms (default 3s)
136
+ for lower streaming latency during long-running tasks.
137
+ - ``default_context_switch_value``: Yield to the event loop every 100 messages
138
+ (default 1000) to keep concurrent coroutines responsive under heavy output.
85
139
 
86
140
  Returns:
87
141
  Producer connected to RabbitMQ.
@@ -91,12 +145,22 @@ class TaskiqBrokerConfig:
91
145
  username = os.environ.get("RABBITMQ_RSTREAM_USERNAME", "guest")
92
146
  password = os.environ.get("RABBITMQ_RSTREAM_PASSWORD", "guest")
93
147
 
94
- logger.info("Connection to RabbitMQ: %s:%s.", host, port)
95
- return Producer(host=host, port=int(port), username=username, password=password)
148
+ logger.info("RStream producer connecting to %s:%s", host, port)
149
+ return Producer(
150
+ host=host,
151
+ port=int(port),
152
+ username=username,
153
+ password=password,
154
+ ssl_context=_rstream_ssl_context(),
155
+ default_batch_publishing_delay=float(os.environ.get("DIGITALKIN_RSTREAM_BATCH_DELAY", "0.1")),
156
+ default_context_switch_value=int(os.environ.get("DIGITALKIN_RSTREAM_CONTEXT_SWITCH", "100")),
157
+ connection_name="digitalkin_producer",
158
+ on_close_handler=TaskiqBrokerConfig._on_producer_closed,
159
+ )
96
160
 
97
161
  @staticmethod
98
162
  def define_broker() -> AioPikaBroker:
99
- """Create AioPikaBroker from environment variables.
163
+ """Create AioPikaBroker with tuned QoS for worker prefetch control.
100
164
 
101
165
  Returns:
102
166
  Broker connected to RabbitMQ with custom formatter.
@@ -105,12 +169,19 @@ class TaskiqBrokerConfig:
105
169
  port = os.environ.get("RABBITMQ_BROKER_PORT", "5672")
106
170
  username = os.environ.get("RABBITMQ_BROKER_USERNAME", "guest")
107
171
  password = os.environ.get("RABBITMQ_BROKER_PASSWORD", "guest")
172
+ scheme = os.environ.get("RABBITMQ_BROKER_SCHEME", "amqp")
108
173
 
109
174
  broker = AioPikaBroker(
110
- f"amqp://{username}:{password}@{host}:{port}",
175
+ f"{scheme}://{username}:{password}@{host}:{port}",
176
+ qos=int(os.environ.get("DIGITALKIN_TASKIQ_PREFETCH", "10")),
111
177
  startup=[TaskiqBrokerConfig.init_rstream],
112
178
  )
113
179
  broker.formatter = PickleFormatter()
180
+ redis_url = os.environ.get("DIGITALKIN_TASKIQ_RESULT_BACKEND_URL")
181
+ if redis_url:
182
+ from taskiq_redis import RedisAsyncResultBackend
183
+
184
+ broker.with_result_backend(RedisAsyncResultBackend(redis_url))
114
185
  return broker
115
186
 
116
187
  @staticmethod
@@ -147,17 +218,78 @@ class TaskiqBrokerConfig:
147
218
  async def send_message_to_stream(job_id: str, output_data: DataModel | ModuleCodeModel) -> None:
148
219
  """Add a message frame to the RStream.
149
220
 
221
+ Uses Pydantic's Rust-based model_dump_json() and direct string embedding
222
+ to avoid the overhead of model_dump() → dict → json.dumps() → encode().
223
+
150
224
  Args:
151
225
  job_id: ID of the job that sent the message.
152
226
  output_data: Message body as a OutputModelT or error / stream_code.
153
227
  """
154
- body = json.dumps({"job_id": job_id, "output_data": output_data.model_dump(mode="json")}).encode("utf-8")
228
+ # job_id is always a UUID (hex + hyphens), safe to embed without escaping
229
+ output_json = output_data.model_dump_json()
230
+ body = f'{{"job_id":"{job_id}","output_data":{output_json}}}'.encode()
155
231
  await RSTREAM_PRODUCER.send(stream=TaskiqBrokerConfig.STREAM, message=body)
156
232
 
157
233
 
234
+ class TaskiqLifecycleMiddleware(TaskiqMiddleware):
235
+ """Lifecycle middleware for structured logging and safety-net EndOfStreamOutput."""
236
+
237
+ async def pre_execute(self, message: TaskiqMessage) -> TaskiqMessage: # noqa: PLR6301
238
+ """Log task start.
239
+
240
+ Returns:
241
+ The unmodified message.
242
+ """
243
+ logger.info("Taskiq task starting: %s (task_name=%s)", message.task_id, message.task_name)
244
+ return message
245
+
246
+ async def post_execute(self, message: TaskiqMessage, result: TaskiqResult) -> None: # noqa: PLR6301
247
+ """Log task completion."""
248
+ log_fn = logger.info if not result.is_err else logger.error
249
+ log_fn(
250
+ "Taskiq task finished: %s (task_name=%s, is_err=%s, exec_time=%.3fs)",
251
+ message.task_id,
252
+ message.task_name,
253
+ result.is_err,
254
+ result.execution_time,
255
+ )
256
+
257
+ async def on_error( # noqa: PLR6301
258
+ self,
259
+ message: TaskiqMessage,
260
+ result: TaskiqResult, # noqa: ARG002
261
+ exception: BaseException,
262
+ ) -> None:
263
+ """Safety net: send EndOfStreamOutput if worker task failed to."""
264
+ logger.error("Taskiq task error: %s (task_name=%s, error=%s)", message.task_id, message.task_name, exception)
265
+ try:
266
+ await TaskiqBrokerConfig.send_message_to_stream(
267
+ message.task_id,
268
+ ModuleCodeModel(code="WorkerCrash", short_description="Middleware safety net", message=str(exception)),
269
+ )
270
+ await TaskiqBrokerConfig.send_message_to_stream(
271
+ message.task_id,
272
+ DataModel(root=EndOfStreamOutput()),
273
+ )
274
+ except Exception:
275
+ logger.exception("Middleware safety net failed for %s", message.task_id)
276
+
277
+
158
278
  # Module-level globals required by Taskiq framework (decorator needs broker at import time)
159
279
  RSTREAM_PRODUCER = TaskiqBrokerConfig.define_producer()
160
280
  TASKIQ_BROKER = TaskiqBrokerConfig.define_broker()
281
+ TASKIQ_BROKER.add_middlewares(TaskiqLifecycleMiddleware())
282
+
283
+
284
+ @TASKIQ_BROKER.task(task_name="__discarded__")
285
+ async def _discarded_message() -> None: # noqa: RUF029
286
+ """No-op sink for poison messages consumed by PickleFormatter.
287
+
288
+ Taskiq's receiver early-returns without acking when a task name is unknown,
289
+ so we register this dummy task to ensure the message is executed (no-op),
290
+ acked, and removed from the queue.
291
+ """
292
+ logger.debug("Poison message acknowledged and discarded")
161
293
 
162
294
 
163
295
  @TASKIQ_BROKER.task
@@ -170,6 +302,7 @@ async def run_start_module(
170
302
  input_data: dict,
171
303
  setup_data: dict,
172
304
  request_metadata: dict[str, str] | None = None,
305
+ registry_config: dict[str, Any] | None = None,
173
306
  context: Context = TaskiqDepends(),
174
307
  ) -> None:
175
308
  """TaskIQ task allowing a module to compute in the background asynchronously.
@@ -183,9 +316,17 @@ async def run_start_module(
183
316
  input_data: dict,
184
317
  setup_data: dict,
185
318
  request_metadata: gRPC request metadata (headers) to forward to the module.
319
+ registry_config: Registry config (client_config) forwarded from the main process.
186
320
  context: Allow TaskIQ context access
187
321
  """
188
322
  logger.info("Starting module with services_mode: %s", services_mode)
323
+
324
+ # Restore registry config lost during pickle (worker re-imports class without runtime mutations)
325
+ if registry_config is not None:
326
+ if "services_config_params" not in module_class.__dict__:
327
+ module_class.services_config_params = dict(module_class.services_config_params)
328
+ module_class.services_config_params["registry"] = registry_config
329
+
189
330
  services_config = ServicesConfig(
190
331
  services_config_strategies=module_class.services_config_strategies,
191
332
  services_config_params=module_class.services_config_params,
@@ -219,6 +360,17 @@ async def run_start_module(
219
360
  setup_model = await module_class.create_setup_model(setup_data)
220
361
  except Exception as e:
221
362
  logger.error("Failed to reconstruct models for job %s: %s", job_id, e, exc_info=True)
363
+ try:
364
+ await callback(
365
+ ModuleCodeModel(
366
+ code="ValidationError",
367
+ short_description="Model reconstruction failed",
368
+ message=str(e),
369
+ )
370
+ )
371
+ await callback(DataModel(root=EndOfStreamOutput()))
372
+ except Exception:
373
+ logger.exception("Failed to send error to stream for job %s", job_id)
222
374
  raise
223
375
 
224
376
  supervisor_task = await executor.execute_task(
@@ -236,8 +388,19 @@ async def run_start_module(
236
388
  # Wait for the supervisor task to complete
237
389
  await supervisor_task
238
390
  logger.info("Module task %s completed", job_id)
239
- except Exception:
391
+ except Exception as e:
240
392
  logger.exception("Error running module %s", job_id)
393
+ try:
394
+ await callback(
395
+ ModuleCodeModel(
396
+ code="WorkerError",
397
+ short_description="Worker execution failed",
398
+ message=str(e),
399
+ )
400
+ )
401
+ await callback(DataModel(root=EndOfStreamOutput()))
402
+ except Exception:
403
+ logger.exception("Failed to send error to stream for job %s", job_id)
241
404
  raise
242
405
  finally:
243
406
  # Cleanup via module context
@@ -256,6 +419,7 @@ async def run_config_module(
256
419
  services_mode: ServicesMode,
257
420
  config_setup_data: dict,
258
421
  request_metadata: dict[str, str] | None = None,
422
+ registry_config: dict[str, Any] | None = None,
259
423
  context: Context = TaskiqDepends(),
260
424
  ) -> None:
261
425
  """TaskIQ task allowing a module to compute in the background asynchronously.
@@ -268,9 +432,17 @@ async def run_config_module(
268
432
  services_mode: ServicesMode,
269
433
  config_setup_data: dict,
270
434
  request_metadata: gRPC request metadata (headers) to forward to the module.
435
+ registry_config: Registry config (client_config) forwarded from the main process.
271
436
  context: Allow TaskIQ context access
272
437
  """
273
438
  logger.info("Starting config module with services_mode: %s", services_mode)
439
+
440
+ # Restore registry config lost during pickle (worker re-imports class without runtime mutations)
441
+ if registry_config is not None:
442
+ if "services_config_params" not in module_class.__dict__:
443
+ module_class.services_config_params = dict(module_class.services_config_params)
444
+ module_class.services_config_params["registry"] = registry_config
445
+
274
446
  services_config = ServicesConfig(
275
447
  services_config_strategies=module_class.services_config_strategies,
276
448
  services_config_params=module_class.services_config_params,
@@ -293,7 +465,22 @@ async def run_config_module(
293
465
  session = TaskSession(job_id, mission_id, module)
294
466
 
295
467
  # Create and run the config setup task with TaskExecutor
296
- setup_model = module_class.create_config_setup_model(config_setup_data)
468
+ try:
469
+ setup_model = module_class.create_config_setup_model(config_setup_data)
470
+ except Exception as e:
471
+ logger.error("Failed to reconstruct config setup model for job %s: %s", job_id, e, exc_info=True)
472
+ try:
473
+ await callback(
474
+ ModuleCodeModel(
475
+ code="ValidationError",
476
+ short_description="Config setup model reconstruction failed",
477
+ message=str(e),
478
+ )
479
+ )
480
+ await callback(DataModel(root=EndOfStreamOutput()))
481
+ except Exception:
482
+ logger.exception("Failed to send error to stream for job %s", job_id)
483
+ raise
297
484
 
298
485
  supervisor_task = await executor.execute_task(
299
486
  task_id=job_id,
@@ -305,8 +492,19 @@ async def run_config_module(
305
492
  # Wait for the supervisor task to complete
306
493
  await supervisor_task
307
494
  logger.info("Config module task %s completed", job_id)
308
- except Exception:
495
+ except Exception as e:
309
496
  logger.exception("Error running config module %s", job_id)
497
+ try:
498
+ await callback(
499
+ ModuleCodeModel(
500
+ code="WorkerError",
501
+ short_description="Config worker execution failed",
502
+ message=str(e),
503
+ )
504
+ )
505
+ await callback(DataModel(root=EndOfStreamOutput()))
506
+ except Exception:
507
+ logger.exception("Failed to send error to stream for job %s", job_id)
310
508
  raise
311
509
  finally:
312
510
  # Cleanup via module context