digitalkin 1.0.0.dev10__tar.gz → 1.0.0.dev12__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 (242) hide show
  1. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/PKG-INFO +1 -1
  2. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/pyproject.toml +4 -1
  3. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/__version__.py +1 -1
  4. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/community/agno/__init__.py +8 -14
  5. digitalkin-1.0.0.dev12/src/digitalkin/community/agno/agui_tools.py +90 -0
  6. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/community/agno/hitl.py +162 -158
  7. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/job_manager/base_job_manager.py +34 -0
  8. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/job_manager/single_job_manager.py +2 -0
  9. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/module_runner.py +5 -6
  10. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/instrumented.py +2 -4
  11. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/redis_client.py +21 -24
  12. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/redis_signal.py +5 -4
  13. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/redis_streams.py +1 -2
  14. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/shadow.py +2 -5
  15. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/task_session.py +1 -0
  16. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/_base_server.py +9 -11
  17. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/gateway_servicer.py +3 -3
  18. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/m2m_call_registry.py +1 -2
  19. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/module_server.py +15 -16
  20. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/module_servicer.py +12 -15
  21. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +1 -1
  22. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +2 -2
  23. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/logger.py +84 -81
  24. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/agui_mixin.py +65 -87
  25. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/events/agent_events.py +28 -28
  26. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/ag_ui.py +33 -33
  27. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/module_context.py +1 -1
  28. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/setup_types.py +20 -21
  29. digitalkin-1.0.0.dev12/src/digitalkin/models/module/tool_cache.py +239 -0
  30. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/tool_reference.py +38 -26
  31. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/utility.py +7 -7
  32. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/utils/dynamic_schema.py +1 -1
  33. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/_base_module.py +9 -13
  34. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/tool_module.py +1 -1
  35. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/base_strategy.py +6 -6
  36. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/communication/communication_strategy.py +1 -1
  37. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/communication/grpc_communication.py +6 -12
  38. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/cost/grpc_cost.py +4 -4
  39. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/filesystem/default_filesystem.py +4 -4
  40. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/registry/grpc_registry.py +22 -49
  41. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/setup/default_setup.py +2 -2
  42. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/setup/grpc_setup.py +5 -9
  43. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/storage/grpc_storage.py +2 -2
  44. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/user_profile/grpc_user_profile.py +2 -2
  45. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/utils/__init__.py +2 -18
  46. digitalkin-1.0.0.dev12/src/digitalkin/utils/conditional_schema.py +197 -0
  47. digitalkin-1.0.0.dev12/src/digitalkin/utils/dynamic_schema.py +303 -0
  48. digitalkin-1.0.0.dev12/src/digitalkin/utils/llm_ready_schema.py +77 -0
  49. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/utils/package_discover.py +3 -3
  50. digitalkin-1.0.0.dev12/src/digitalkin/utils/proto_utils.py +25 -0
  51. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin.egg-info/PKG-INFO +1 -1
  52. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin.egg-info/SOURCES.txt +2 -1
  53. digitalkin-1.0.0.dev12/tests/test_exceptions.py +106 -0
  54. digitalkin-1.0.0.dev10/src/digitalkin/community/agno/agui_tools.py +0 -127
  55. digitalkin-1.0.0.dev10/src/digitalkin/models/module/tool_cache.py +0 -259
  56. digitalkin-1.0.0.dev10/src/digitalkin/utils/conditional_schema.py +0 -212
  57. digitalkin-1.0.0.dev10/src/digitalkin/utils/dynamic_schema.py +0 -435
  58. digitalkin-1.0.0.dev10/src/digitalkin/utils/llm_ready_schema.py +0 -79
  59. digitalkin-1.0.0.dev10/src/digitalkin/utils/proto_utils.py +0 -21
  60. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/LICENSE +0 -0
  61. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/README.md +0 -0
  62. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/__init__.py +0 -0
  63. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/mock/__init__.py +0 -0
  64. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/mock/mock_pb2.py +0 -0
  65. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  66. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/server_async_insecure.py +0 -0
  67. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/server_async_secure.py +0 -0
  68. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/server_sync_insecure.py +0 -0
  69. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/base_server/server_sync_secure.py +0 -0
  70. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/__init__.py +0 -0
  71. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/echo_module.py +0 -0
  72. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/models/__init__.py +0 -0
  73. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/models/input.py +0 -0
  74. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/models/output.py +0 -0
  75. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/models/secret.py +0 -0
  76. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/models/setup.py +0 -0
  77. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/server.py +0 -0
  78. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/triggers/__init__.py +0 -0
  79. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/bench_module/triggers/message_trigger.py +0 -0
  80. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/modules/__init__.py +0 -0
  81. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/modules/archetype_with_tools_module.py +0 -0
  82. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/modules/cpu_intensive_module.py +0 -0
  83. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/modules/dynamic_setup_module.py +0 -0
  84. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/modules/minimal_llm_module.py +0 -0
  85. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/modules/text_transform_module.py +0 -0
  86. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
  87. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
  88. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
  89. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
  90. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
  91. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/monitoring/tests/test_metrics.py +0 -0
  92. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/client.py +0 -0
  93. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/echo_module.py +0 -0
  94. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/models/__init__.py +0 -0
  95. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/models/input.py +0 -0
  96. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/models/output.py +0 -0
  97. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/models/secret.py +0 -0
  98. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/models/setup.py +0 -0
  99. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/server.py +0 -0
  100. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/triggers/__init__.py +0 -0
  101. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/redis_demo/triggers/message_trigger.py +0 -0
  102. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/services/filesystem_module.py +0 -0
  103. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/examples/services/storage_module.py +0 -0
  104. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/setup.cfg +0 -0
  105. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/__init__.py +0 -0
  106. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/community/__init__.py +0 -0
  107. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/community/agno/agno_adapter.py +0 -0
  108. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/community/agno/models.py +0 -0
  109. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/__init__.py +0 -0
  110. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/common/__init__.py +0 -0
  111. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/common/factories.py +0 -0
  112. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/exceptions.py +0 -0
  113. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/job_manager/__init__.py +0 -0
  114. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/profiling/__init__.py +0 -0
  115. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/profiling/step_timer.py +0 -0
  116. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/profiling/task_profiler.py +0 -0
  117. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/resilience/__init__.py +0 -0
  118. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/resilience/bulkhead.py +0 -0
  119. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
  120. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/__init__.py +0 -0
  121. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
  122. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
  123. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
  124. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
  125. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/redis_checkpoint.py +0 -0
  126. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
  127. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
  128. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
  129. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/core/task_manager/task_executor.py +0 -0
  130. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/exceptions.py +0 -0
  131. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/__init__.py +0 -0
  132. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/exceptions.py +0 -0
  133. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
  134. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/interceptors/circuit_breaker_interceptor.py +0 -0
  135. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
  136. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/stream_session.py +0 -0
  137. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  138. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +0 -0
  139. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  140. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
  141. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/__init__.py +0 -0
  142. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/base_mixin.py +0 -0
  143. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/cost_mixin.py +0 -0
  144. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  145. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  146. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/logger_mixin.py +0 -0
  147. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/mixins/storage_mixin.py +0 -0
  148. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/__init__.py +0 -0
  149. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/core/__init__.py +0 -0
  150. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/core/job_manager_models.py +0 -0
  151. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/core/redis.py +0 -0
  152. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/core/task_monitor.py +0 -0
  153. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/events/__init__.py +0 -0
  154. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  155. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
  156. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
  157. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/grpc_servers/models.py +0 -0
  158. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/grpc_servers/stream_error_codes.py +0 -0
  159. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/grpc_servers/types.py +0 -0
  160. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/__init__.py +0 -0
  161. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/base_types.py +0 -0
  162. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/module.py +0 -0
  163. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/module_types.py +0 -0
  164. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/request_metadata.py +0 -0
  165. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/module/select_schema.py +0 -0
  166. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/services/__init__.py +0 -0
  167. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/services/cost.py +0 -0
  168. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/services/registry.py +0 -0
  169. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/services/services.py +0 -0
  170. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/services/storage.py +0 -0
  171. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/__init__.py +0 -0
  172. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/consumer.py +0 -0
  173. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/gateway.py +0 -0
  174. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/grpc_client.py +0 -0
  175. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/log.py +0 -0
  176. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/module.py +0 -0
  177. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/profiling.py +0 -0
  178. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/queue.py +0 -0
  179. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/redis.py +0 -0
  180. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/resilience.py +0 -0
  181. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/server/__init__.py +0 -0
  182. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/server/channel.py +0 -0
  183. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/server/grpc.py +0 -0
  184. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/server/server.py +0 -0
  185. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/server/servicer.py +0 -0
  186. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/task_manager.py +0 -0
  187. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/utils/__init__.py +0 -0
  188. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/settings/utils/channel.py +0 -0
  189. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/models/utils/__init__.py +0 -0
  190. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/__init__.py +0 -0
  191. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/archetype_module.py +0 -0
  192. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/trigger_handler.py +0 -0
  193. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/triggers/__init__.py +0 -0
  194. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
  195. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
  196. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
  197. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/py.typed +0 -0
  198. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/__init__.py +0 -0
  199. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/communication/__init__.py +0 -0
  200. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/communication/default_communication.py +0 -0
  201. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/communication/exceptions.py +0 -0
  202. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/cost/__init__.py +0 -0
  203. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  204. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/cost/default_cost.py +0 -0
  205. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/cost/exceptions.py +0 -0
  206. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/filesystem/__init__.py +0 -0
  207. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/filesystem/exceptions.py +0 -0
  208. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  209. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
  210. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/identity/__init__.py +0 -0
  211. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/identity/default_identity.py +0 -0
  212. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  213. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/registry/__init__.py +0 -0
  214. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/registry/default_registry.py +0 -0
  215. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/registry/exceptions.py +0 -0
  216. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/registry/registry_models.py +0 -0
  217. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/registry/registry_strategy.py +0 -0
  218. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/services_config.py +0 -0
  219. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/services_models.py +0 -0
  220. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/setup/__init__.py +0 -0
  221. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/setup/exceptions.py +0 -0
  222. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  223. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/storage/__init__.py +0 -0
  224. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/storage/default_storage.py +0 -0
  225. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/storage/exceptions.py +0 -0
  226. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  227. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/task_manager/__init__.py +0 -0
  228. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
  229. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/task_manager/exceptions.py +0 -0
  230. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
  231. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
  232. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/user_profile/__init__.py +0 -0
  233. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  234. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/user_profile/exceptions.py +0 -0
  235. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
  236. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/utils/arg_parser.py +0 -0
  237. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/utils/development_mode_action.py +0 -0
  238. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/utils/exceptions.py +0 -0
  239. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin/utils/schema_splitter.py +0 -0
  240. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  241. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/src/digitalkin.egg-info/requires.txt +0 -0
  242. {digitalkin-1.0.0.dev10 → digitalkin-1.0.0.dev12}/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: 1.0.0.dev10
3
+ Version: 1.0.0.dev12
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,<8",
39
39
  ]
40
- version = "1.0.0.dev10"
40
+ version = "1.0.0.dev12"
41
41
 
42
42
  [project.optional-dependencies]
43
43
  performance = [ "uvloop>=0.21" ]
@@ -274,6 +274,8 @@
274
274
  [tool.mypy]
275
275
  exclude = [ "examples", "tests", "scripts" ]
276
276
  ignore_missing_imports = true
277
+ warn_unused_ignores = true
278
+ warn_redundant_casts = true
277
279
 
278
280
  [tool.pytest.ini_options]
279
281
 
@@ -294,6 +296,7 @@
294
296
  "contract: marks gRPC proto contract verification tests",
295
297
  "e2e: marks end-to-end tests requiring full Docker Compose stack",
296
298
  "edge_case: marks tests for boundary conditions and edge cases",
299
+ "flaky: marks known-flaky tests for the quarantine plugin (tests/fixtures/flakiness.py)",
297
300
  "grpc: marks tests for gRPC service functionality",
298
301
  "idempotency: marks tests for retry and duplicate request handling",
299
302
  "integration: marks tests that require external service connections (deselect with '-m \"not integration\"')",
@@ -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.dev10"
8
+ __version__ = "1.0.0.dev12"
@@ -4,28 +4,24 @@ Adapters, converters, and HITL helpers for building DigitalKin modules
4
4
  on top of the Agno agent framework. Exports:
5
5
 
6
6
  - :class:`AgnoStreamAdapter` — Agno streaming events → DigitalKin events.
7
- - :func:`agui_tool_to_external_function` / :func:`make_tools_factory`
8
- register AG-UI client-side (frontend) tools as Agno external Functions.
7
+ - :class:`AguiTools` register AG-UI client-side (frontend) tools as Agno
8
+ external Functions.
9
9
  - :class:`AgnoHitlRunner`, :class:`PausedRunStore`, :class:`PauseInfo`,
10
10
  :class:`PausedRunRecord`, :data:`HITL_STORAGE_CONFIG`,
11
- :func:`emit_awaiting_tool_result` — human-in-the-loop (HITL) runner
12
- that persists a paused Agno run via the module's
11
+ :class:`HitlEvents` — human-in-the-loop (HITL) runner that persists a
12
+ paused Agno run via the module's
13
13
  :class:`~digitalkin.services.storage.StorageStrategy` and resumes it
14
14
  when the front replies with a ``ToolMessage``.
15
15
  """
16
16
 
17
17
  from digitalkin.community.agno.agno_adapter import AgnoStreamAdapter
18
- from digitalkin.community.agno.agui_tools import (
19
- agui_tool_to_external_function,
20
- make_tools_factory,
21
- )
18
+ from digitalkin.community.agno.agui_tools import AguiTools
22
19
  from digitalkin.community.agno.hitl import (
23
20
  HITL_STORAGE_CONFIG,
24
21
  AgnoHitlRunner,
22
+ HitlEvents,
25
23
  PausedRunRecord,
26
24
  PausedRunStore,
27
- emit_awaiting_tool_result,
28
- emit_messages_snapshot,
29
25
  )
30
26
  from digitalkin.community.agno.models import PauseInfo
31
27
 
@@ -33,11 +29,9 @@ __all__ = [
33
29
  "HITL_STORAGE_CONFIG",
34
30
  "AgnoHitlRunner",
35
31
  "AgnoStreamAdapter",
32
+ "AguiTools",
33
+ "HitlEvents",
36
34
  "PauseInfo",
37
35
  "PausedRunRecord",
38
36
  "PausedRunStore",
39
- "agui_tool_to_external_function",
40
- "emit_awaiting_tool_result",
41
- "emit_messages_snapshot",
42
- "make_tools_factory",
43
37
  ]
@@ -0,0 +1,90 @@
1
+ """AG-UI frontend tools → Agno external Functions.
2
+
3
+ The AG-UI protocol lets the client declare its own tools in
4
+ ``RunAgentInput.tools``, meant to be executed on the frontend rather than
5
+ by the agent process. :class:`AguiTools` exposes them to an Agno agent as
6
+ :class:`~agno.tools.function.Function` objects marked
7
+ ``external_execution=True``: when the LLM "calls" one, Agno pauses the run
8
+ instead of executing an entrypoint, letting the caller stream the tool-call
9
+ events to the front and resume later.
10
+
11
+ See ``examples/`` and the :class:`~digitalkin.community.agno.AgnoHitlRunner`
12
+ docstring for end-to-end usage.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Callable
21
+
22
+ from ag_ui.core.types import Tool as AgUiTool
23
+ from agno.run.base import RunContext
24
+ from agno.tools.function import Function
25
+
26
+
27
+ class AguiTools:
28
+ """Convert AG-UI frontend tool declarations into Agno external Functions."""
29
+
30
+ @staticmethod
31
+ def _unreachable_entrypoint(**_: Any) -> None:
32
+ """Placeholder — never invoked because ``external_execution=True`` pauses the run."""
33
+
34
+ @staticmethod
35
+ def agui_tool_to_external_function(tool: AgUiTool) -> Function:
36
+ """Wrap an AG-UI tool definition as an Agno external ``Function``.
37
+
38
+ The resulting :class:`Function` carries the AG-UI schema as-is and is
39
+ marked ``external_execution=True`` so Agno emits the tool-call events
40
+ but skips the entrypoint and pauses the run when the LLM invokes it.
41
+
42
+ Args:
43
+ tool: An :class:`ag_ui.core.types.Tool` from ``RunAgentInput.tools``.
44
+
45
+ Returns:
46
+ An :class:`agno.tools.function.Function` ready to plug into an agent.
47
+ """
48
+ from agno.tools.function import Function # pyright: ignore[reportMissingImports]
49
+
50
+ parameters = tool.parameters or {"type": "object", "properties": {}, "required": []}
51
+ return Function(
52
+ name=tool.name,
53
+ description=tool.description,
54
+ parameters=parameters,
55
+ entrypoint=AguiTools._unreachable_entrypoint,
56
+ external_execution=True,
57
+ skip_entrypoint_processing=True,
58
+ )
59
+
60
+ @staticmethod
61
+ def make_tools_factory(
62
+ base_tools: list[Any],
63
+ dependency_key: str = "agui_tools",
64
+ ) -> Callable[[RunContext], list[Any]]:
65
+ """Build an Agno ``tools`` factory merging base tools with per-run AG-UI tools.
66
+
67
+ The returned callable is the value passed to ``Agent(tools=...)``. On
68
+ every run Agno resolves it with the current ``RunContext``; the factory
69
+ reads ``run_context.dependencies[dependency_key]`` (the per-run AG-UI
70
+ tool list), converts them to external Functions, and concatenates them
71
+ with ``base_tools``.
72
+
73
+ Args:
74
+ base_tools: Toolkits / Functions always available, passed through.
75
+ dependency_key: Key in ``run_context.dependencies`` for the per-run
76
+ AG-UI tool list. Defaults to ``"agui_tools"``.
77
+
78
+ Returns:
79
+ A callable for ``Agent(tools=...)``. Set ``cache_callables=False``
80
+ so it is re-invoked every run.
81
+ """
82
+
83
+ def factory(run_context: RunContext | None = None) -> list[Any]:
84
+ if run_context is None:
85
+ return list(base_tools)
86
+ deps = getattr(run_context, "dependencies", None) or {}
87
+ agui_tools: list[AgUiTool] = deps.get(dependency_key) or []
88
+ return [*base_tools, *[AguiTools.agui_tool_to_external_function(t) for t in agui_tools]]
89
+
90
+ return factory
@@ -124,169 +124,173 @@ class PausedRunStore:
124
124
  await self._storage.remove(collection=self.COLLECTION, record_id=thread_id)
125
125
 
126
126
 
127
- def _agno_messages_to_agui(agno_messages: list[Any]) -> list[AgUiMessage]:
128
- """Convert Agno messages into AG-UI messages.
129
-
130
- Drops system/developer/reasoning; reshapes assistant ``tool_calls``
131
- into AG-UI :class:`~ag_ui.core.types.ToolCall` objects.
132
-
133
- Args:
134
- agno_messages: Value of ``RunOutput.messages`` at pause time.
135
-
136
- Returns:
137
- AG-UI :class:`~ag_ui.core.types.Message` instances.
138
- """
139
- from ag_ui.core.types import (
140
- AssistantMessage as AgUiAssistantMessage,
141
- )
142
- from ag_ui.core.types import (
143
- FunctionCall as AgUiFunctionCall,
144
- )
145
- from ag_ui.core.types import (
146
- ToolCall as AgUiToolCall,
147
- )
148
- from ag_ui.core.types import (
149
- ToolMessage as AgUiToolMessage,
150
- )
151
- from ag_ui.core.types import (
152
- UserMessage as AgUiUserMessage,
153
- )
154
-
155
- result: list[AgUiMessage] = []
156
- for msg in agno_messages or []:
157
- role = getattr(msg, "role", None)
158
- msg_id = getattr(msg, "id", None) or ""
159
- content = getattr(msg, "content", None)
160
- if isinstance(content, list):
161
- content = " ".join(str(part) for part in content if part is not None)
162
-
163
- if role == "user":
164
- result.append(AgUiUserMessage(id=msg_id, role="user", content=content or ""))
165
- elif role == "assistant":
166
- raw_calls = getattr(msg, "tool_calls", None) or []
167
- agui_tool_calls: list[AgUiToolCall] = []
168
- for tc in raw_calls:
169
- tc_id = tc.get("id") if isinstance(tc, dict) else getattr(tc, "id", None)
170
- func = tc.get("function") if isinstance(tc, dict) else getattr(tc, "function", None)
171
- if not tc_id or func is None:
172
- continue
173
- func_name = func.get("name") if isinstance(func, dict) else getattr(func, "name", None)
174
- func_args = func.get("arguments") if isinstance(func, dict) else getattr(func, "arguments", None)
175
- if not isinstance(func_args, str):
176
- func_args = json.dumps(func_args) if func_args is not None else "{}"
177
- agui_tool_calls.append(
178
- AgUiToolCall(
179
- id=tc_id,
180
- type="function",
181
- function=AgUiFunctionCall(name=func_name or "", arguments=func_args),
127
+ class HitlEvents:
128
+ """AG-UI message conversion and event emission for the HITL flow."""
129
+
130
+ @staticmethod
131
+ def agno_messages_to_agui(agno_messages: list[Any]) -> list[AgUiMessage]:
132
+ """Convert Agno messages into AG-UI messages.
133
+
134
+ Drops system/developer/reasoning; reshapes assistant ``tool_calls``
135
+ into AG-UI :class:`~ag_ui.core.types.ToolCall` objects.
136
+
137
+ Args:
138
+ agno_messages: Value of ``RunOutput.messages`` at pause time.
139
+
140
+ Returns:
141
+ AG-UI :class:`~ag_ui.core.types.Message` instances.
142
+ """
143
+ from ag_ui.core.types import (
144
+ AssistantMessage as AgUiAssistantMessage,
145
+ )
146
+ from ag_ui.core.types import (
147
+ FunctionCall as AgUiFunctionCall,
148
+ )
149
+ from ag_ui.core.types import (
150
+ ToolCall as AgUiToolCall,
151
+ )
152
+ from ag_ui.core.types import (
153
+ ToolMessage as AgUiToolMessage,
154
+ )
155
+ from ag_ui.core.types import (
156
+ UserMessage as AgUiUserMessage,
157
+ )
158
+
159
+ result: list[AgUiMessage] = []
160
+ for msg in agno_messages or []:
161
+ role = getattr(msg, "role", None)
162
+ msg_id = getattr(msg, "id", None) or ""
163
+ content = getattr(msg, "content", None)
164
+ if isinstance(content, list):
165
+ content = " ".join(str(part) for part in content if part is not None)
166
+
167
+ if role == "user":
168
+ result.append(AgUiUserMessage(id=msg_id, role="user", content=content or ""))
169
+ elif role == "assistant":
170
+ raw_calls = getattr(msg, "tool_calls", None) or []
171
+ agui_tool_calls: list[AgUiToolCall] = []
172
+ for tc in raw_calls:
173
+ tc_id = tc.get("id") if isinstance(tc, dict) else getattr(tc, "id", None)
174
+ func = tc.get("function") if isinstance(tc, dict) else getattr(tc, "function", None)
175
+ if not tc_id or func is None:
176
+ continue
177
+ func_name = func.get("name") if isinstance(func, dict) else getattr(func, "name", None)
178
+ func_args = func.get("arguments") if isinstance(func, dict) else getattr(func, "arguments", None)
179
+ if not isinstance(func_args, str):
180
+ func_args = json.dumps(func_args) if func_args is not None else "{}"
181
+ agui_tool_calls.append(
182
+ AgUiToolCall(
183
+ id=tc_id,
184
+ type="function",
185
+ function=AgUiFunctionCall(name=func_name or "", arguments=func_args),
186
+ )
187
+ )
188
+ result.append(
189
+ AgUiAssistantMessage(
190
+ id=msg_id,
191
+ role="assistant",
192
+ content=content if isinstance(content, str) else None,
193
+ tool_calls=agui_tool_calls or None,
182
194
  )
183
195
  )
184
- result.append(
185
- AgUiAssistantMessage(
186
- id=msg_id,
187
- role="assistant",
188
- content=content if isinstance(content, str) else None,
189
- tool_calls=agui_tool_calls or None,
196
+ elif role == "tool":
197
+ tool_call_id = getattr(msg, "tool_call_id", None)
198
+ if not tool_call_id:
199
+ continue
200
+ result.append(
201
+ AgUiToolMessage(
202
+ id=msg_id,
203
+ role="tool",
204
+ tool_call_id=tool_call_id,
205
+ content=content if isinstance(content, str) else "",
206
+ )
190
207
  )
208
+ return result
209
+
210
+ @staticmethod
211
+ async def emit_messages_snapshot(
212
+ context: ModuleContext,
213
+ messages: list[AgUiMessage],
214
+ ) -> None:
215
+ """Emit an AG-UI ``MessagesSnapshot`` event.
216
+
217
+ Typically called just before :func:`emit_awaiting_tool_result` on a
218
+ paused run so the front has an authoritative view of the conversation
219
+ (including the assistant message carrying the frontend ``tool_calls``,
220
+ which cannot be reconstructed from the streamed tool-call events alone).
221
+
222
+ Args:
223
+ context: Current module context.
224
+ messages: List of AG-UI messages, typically produced by
225
+ :func:`agno_messages_to_agui` from ``RunOutput.messages``.
226
+ """
227
+ if not messages:
228
+ return
229
+
230
+ from ag_ui.core.events import MessagesSnapshotEvent as AgUiMessagesSnapshotEvent
231
+
232
+ from digitalkin.models.module.ag_ui import (
233
+ AgUiMessagesSnapshotOutput,
234
+ AgUiOutput,
235
+ )
236
+
237
+ output = AgUiOutput(
238
+ root=AgUiMessagesSnapshotOutput(
239
+ event=AgUiMessagesSnapshotEvent(messages=messages),
191
240
  )
192
- elif role == "tool":
193
- tool_call_id = getattr(msg, "tool_call_id", None)
194
- if not tool_call_id:
195
- continue
196
- result.append(
197
- AgUiToolMessage(
198
- id=msg_id,
199
- role="tool",
200
- tool_call_id=tool_call_id,
201
- content=content if isinstance(content, str) else "",
241
+ )
242
+ await context.callbacks.send_message(output)
243
+ logger.info("emit_messages_snapshot: sent %d message(s)", len(messages))
244
+
245
+ @staticmethod
246
+ async def emit_awaiting_tool_result(
247
+ context: ModuleContext,
248
+ *,
249
+ thread_id: str,
250
+ run_id: str,
251
+ pending_tool_call_ids: list[str],
252
+ ) -> None:
253
+ """Emit an AG-UI ``RunFinished`` with ``status="awaiting_tool_result"``.
254
+
255
+ This is the protocol signal telling the front "the run paused on a
256
+ client-side tool; execute it and reply with a ``ToolMessage``". It
257
+ goes out via ``context.callbacks.send_message`` (bypassing the
258
+ standard :class:`~digitalkin.mixins.agui_mixin.AgUiMixin` event
259
+ mapping, which has no notion of an "awaiting" status).
260
+
261
+ Args:
262
+ context: Current module context.
263
+ thread_id: AG-UI thread identifier.
264
+ run_id: Run identifier to echo back in the finished event.
265
+ pending_tool_call_ids: The ``tool_call_id`` values the front must
266
+ execute and resolve — echoed in ``result.pending_tool_call_ids``
267
+ so the front can match them.
268
+ """
269
+ from ag_ui.core.events import RunFinishedEvent as AgUiRunFinishedEvent
270
+
271
+ from digitalkin.models.module.ag_ui import (
272
+ AgUiOutput,
273
+ AgUiRunFinishedOutput,
274
+ )
275
+
276
+ output = AgUiOutput(
277
+ root=AgUiRunFinishedOutput(
278
+ event=AgUiRunFinishedEvent(
279
+ thread_id=thread_id,
280
+ run_id=run_id,
281
+ result={
282
+ "status": _AWAITING_STATUS,
283
+ "pending_tool_call_ids": pending_tool_call_ids,
284
+ },
202
285
  )
203
286
  )
204
- return result
205
-
206
-
207
- async def emit_messages_snapshot(
208
- context: ModuleContext,
209
- messages: list[AgUiMessage],
210
- ) -> None:
211
- """Emit an AG-UI ``MessagesSnapshot`` event.
212
-
213
- Typically called just before :func:`emit_awaiting_tool_result` on a
214
- paused run so the front has an authoritative view of the conversation
215
- (including the assistant message carrying the frontend ``tool_calls``,
216
- which cannot be reconstructed from the streamed tool-call events alone).
217
-
218
- Args:
219
- context: Current module context.
220
- messages: List of AG-UI messages, typically produced by
221
- :func:`_agno_messages_to_agui` from ``RunOutput.messages``.
222
- """
223
- if not messages:
224
- return
225
-
226
- from ag_ui.core.events import MessagesSnapshotEvent as AgUiMessagesSnapshotEvent
227
-
228
- from digitalkin.models.module.ag_ui import (
229
- AgUiMessagesSnapshotOutput,
230
- AgUiOutput,
231
- )
232
-
233
- output = AgUiOutput(
234
- root=AgUiMessagesSnapshotOutput(
235
- event=AgUiMessagesSnapshotEvent(messages=messages),
236
287
  )
237
- )
238
- await context.callbacks.send_message(output)
239
- logger.info("emit_messages_snapshot: sent %d message(s)", len(messages))
240
-
241
-
242
- async def emit_awaiting_tool_result(
243
- context: ModuleContext,
244
- *,
245
- thread_id: str,
246
- run_id: str,
247
- pending_tool_call_ids: list[str],
248
- ) -> None:
249
- """Emit an AG-UI ``RunFinished`` with ``status="awaiting_tool_result"``.
250
-
251
- This is the protocol signal telling the front "the run paused on a
252
- client-side tool; execute it and reply with a ``ToolMessage``". It
253
- goes out via ``context.callbacks.send_message`` (bypassing the
254
- standard :class:`~digitalkin.mixins.agui_mixin.AgUiMixin` event
255
- mapping, which has no notion of an "awaiting" status).
256
-
257
- Args:
258
- context: Current module context.
259
- thread_id: AG-UI thread identifier.
260
- run_id: Run identifier to echo back in the finished event.
261
- pending_tool_call_ids: The ``tool_call_id`` values the front must
262
- execute and resolve — echoed in ``result.pending_tool_call_ids``
263
- so the front can match them.
264
- """
265
- from ag_ui.core.events import RunFinishedEvent as AgUiRunFinishedEvent
266
-
267
- from digitalkin.models.module.ag_ui import (
268
- AgUiOutput,
269
- AgUiRunFinishedOutput,
270
- )
271
-
272
- output = AgUiOutput(
273
- root=AgUiRunFinishedOutput(
274
- event=AgUiRunFinishedEvent(
275
- thread_id=thread_id,
276
- run_id=run_id,
277
- result={
278
- "status": _AWAITING_STATUS,
279
- "pending_tool_call_ids": pending_tool_call_ids,
280
- },
281
- )
288
+ await context.callbacks.send_message(output)
289
+ logger.info(
290
+ "emit_awaiting_tool_result: thread_id=%s pending=%s",
291
+ thread_id,
292
+ pending_tool_call_ids,
282
293
  )
283
- )
284
- await context.callbacks.send_message(output)
285
- logger.info(
286
- "emit_awaiting_tool_result: thread_id=%s pending=%s",
287
- thread_id,
288
- pending_tool_call_ids,
289
- )
290
294
 
291
295
 
292
296
  class AgnoHitlRunner:
@@ -559,7 +563,7 @@ class AgnoHitlRunner:
559
563
  resumed, pause_info = await self.try_resume(input_data=input_data, send=send)
560
564
  if resumed:
561
565
  if pause_info is not None and context is not None:
562
- await emit_awaiting_tool_result(
566
+ await HitlEvents.emit_awaiting_tool_result(
563
567
  context,
564
568
  thread_id=pause_info.thread_id,
565
569
  run_id=pause_info.run_id,
@@ -586,7 +590,7 @@ class AgnoHitlRunner:
586
590
  images=images,
587
591
  )
588
592
  if pause_info is not None and context is not None:
589
- await emit_awaiting_tool_result(
593
+ await HitlEvents.emit_awaiting_tool_result(
590
594
  context,
591
595
  thread_id=pause_info.thread_id,
592
596
  run_id=pause_info.run_id,
@@ -625,7 +629,7 @@ class AgnoHitlRunner:
625
629
  if adapter.is_paused and final_run_output is not None and getattr(final_run_output, "is_paused", False):
626
630
  pause_info = await self._store.save(run_output=final_run_output, thread_id=thread_id)
627
631
  # Attach AG-UI-shaped messages so the front can materialise the tool_call.
628
- pause_info.new_messages = _agno_messages_to_agui(final_run_output.messages or [])
632
+ pause_info.new_messages = HitlEvents.agno_messages_to_agui(final_run_output.messages or [])
629
633
  return pause_info
630
634
 
631
635
  return None
@@ -202,3 +202,37 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
202
202
  Returns:
203
203
  dict[str, dict[str, Any]]: A dictionary containing information about all modules and their statuses.
204
204
  """
205
+
206
+ @abc.abstractmethod
207
+ async def preload_instance(
208
+ self,
209
+ setup_data: SetupModelT,
210
+ mission_id: str,
211
+ setup_id: str,
212
+ setup_version_id: str,
213
+ request_metadata: dict[str, str] | None = None,
214
+ job_id: str | None = None,
215
+ tool_cache: Any = None,
216
+ callback: Callable | None = None,
217
+ ) -> tuple[Any, str, Callable]:
218
+ """Build a module instance and run its idempotent ``prepare()``.
219
+
220
+ Returns:
221
+ Tuple of (prepared module instance, job_id, output callback).
222
+ """
223
+
224
+ @abc.abstractmethod
225
+ async def run_instance(
226
+ self,
227
+ module: Any,
228
+ job_id: str,
229
+ mission_id: str,
230
+ input_data: InputModelT,
231
+ setup_data: SetupModelT,
232
+ callback: Callable,
233
+ ) -> str:
234
+ """Run a pre-prepared module instance (from ``preload_instance``) with input.
235
+
236
+ Returns:
237
+ The job_id of the scheduled run.
238
+ """
@@ -273,6 +273,8 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
273
273
  timer.mark("redis_task_manager")
274
274
 
275
275
  if callback is None:
276
+ if self._stream_writers is None: # safety when __init__ was bypassed (e.g. object.__new__)
277
+ self._stream_writers = {}
276
278
  self._stream_writers[job_id] = RedisStreamWriter(job_id, self._redis_client)
277
279
  callback = await self.job_specific_callback(self.add_to_queue, job_id)
278
280
  timer.mark("default_callback")
@@ -14,9 +14,7 @@ from digitalkin.core.profiling.step_timer import StepTimer
14
14
  from digitalkin.core.profiling.task_profiler import TaskProfiler
15
15
  from digitalkin.logger import logger
16
16
  from digitalkin.models.grpc_servers.stream_error_codes import StreamErrorCode
17
- from digitalkin.models.settings.profiling import ProfilerMode, ProfilingSettings
18
-
19
- _PROFILING = ProfilingSettings()
17
+ from digitalkin.models.settings.profiling import ProfilerMode, get_profiling_settings
20
18
 
21
19
  if TYPE_CHECKING:
22
20
  from collections.abc import Awaitable, Callable
@@ -65,12 +63,13 @@ class ModuleRunner:
65
63
  timer = StepTimer()
66
64
 
67
65
  # Construct profiler outside try so finally can always stop it.
66
+ profiling = get_profiling_settings()
68
67
  profiler_mode = (
69
- ProfilerMode(_PROFILING.profiler)
70
- if _PROFILING.profiler in {p.value for p in ProfilerMode}
68
+ ProfilerMode(profiling.profiler)
69
+ if profiling.profiler in {p.value for p in ProfilerMode}
71
70
  else ProfilerMode.NONE
72
71
  )
73
- profiler = TaskProfiler(task_id=task_id, mode=profiler_mode, output_dir=_PROFILING.profile_output_dir)
72
+ profiler = TaskProfiler(task_id=task_id, mode=profiler_mode, output_dir=profiling.profile_output_dir)
74
73
 
75
74
  try:
76
75
  timer.mark("entry")
@@ -15,8 +15,6 @@ if TYPE_CHECKING:
15
15
 
16
16
  from digitalkin.logger import logger
17
17
 
18
- _MIN_KEY_PARTS_FOR_REDACTION = 2
19
-
20
18
 
21
19
  class InstrumentedRedisClient: # noqa: PLR0904
22
20
  """Observability wrapper around RedisClient.
@@ -52,8 +50,8 @@ class InstrumentedRedisClient: # noqa: PLR0904
52
50
  parts = key.split(":")
53
51
  if len(parts) <= 1:
54
52
  return key
55
- if len(parts) > _MIN_KEY_PARTS_FOR_REDACTION:
56
- return ":".join(parts[0:1] + ["*"] * (len(parts) - _MIN_KEY_PARTS_FOR_REDACTION) + parts[-1:])
53
+ if len(parts) > 2: # noqa: PLR2004
54
+ return ":".join(parts[0:1] + ["*"] * (len(parts) - 2) + parts[-1:])
57
55
  return f"{parts[0]}:*"
58
56
 
59
57
  async def _execute(self, command: str, key: str, coro: Any) -> Any: