digitalkin 0.3.2a2__tar.gz → 0.3.2.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/PKG-INFO +9 -9
  2. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/pyproject.toml +108 -108
  3. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/__version__.py +1 -1
  4. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/single_job_manager.py +36 -78
  5. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/taskiq_job_manager.py +4 -8
  6. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/base_task_manager.py +1 -3
  7. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/surrealdb_repository.py +0 -6
  8. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/task_executor.py +10 -27
  9. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/task_session.py +99 -121
  10. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/module_server.py +43 -26
  11. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/module_servicer.py +12 -114
  12. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +4 -14
  13. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +4 -13
  14. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/core/task_monitor.py +1 -23
  15. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/grpc_servers/models.py +8 -95
  16. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/__init__.py +2 -18
  17. digitalkin-0.3.2.dev2/src/digitalkin/models/module/module_context.py +149 -0
  18. digitalkin-0.3.2.dev2/src/digitalkin/models/module/module_types.py +393 -0
  19. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/utility.py +1 -22
  20. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/services/cost.py +1 -22
  21. digitalkin-0.3.2.dev2/src/digitalkin/models/services/registry.py +42 -0
  22. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/_base_module.py +47 -137
  23. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/triggers/__init__.py +4 -0
  24. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/communication/communication_strategy.py +3 -14
  25. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/communication/default_communication.py +0 -3
  26. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/communication/grpc_communication.py +17 -58
  27. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/cost_strategy.py +14 -36
  28. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/default_cost.py +1 -61
  29. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/grpc_cost.py +1 -97
  30. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/grpc_filesystem.py +1 -8
  31. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/registry/__init__.py +1 -1
  32. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/registry/default_registry.py +1 -22
  33. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/registry/grpc_registry.py +1 -77
  34. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/registry_models.py +43 -0
  35. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/registry/registry_strategy.py +1 -19
  36. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/user_profile_strategy.py +15 -0
  37. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/__init__.py +3 -15
  38. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/dynamic_schema.py +0 -4
  39. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/PKG-INFO +9 -9
  40. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/SOURCES.txt +1 -14
  41. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/requires.txt +2 -2
  42. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/top_level.txt +0 -1
  43. digitalkin-0.3.2a2/examples/modules/archetype_with_tools_module.py +0 -232
  44. digitalkin-0.3.2a2/examples/monitoring/digitalkin_observability/__init__.py +0 -46
  45. digitalkin-0.3.2a2/examples/monitoring/digitalkin_observability/http_server.py +0 -150
  46. digitalkin-0.3.2a2/examples/monitoring/digitalkin_observability/interceptors.py +0 -176
  47. digitalkin-0.3.2a2/examples/monitoring/digitalkin_observability/metrics.py +0 -201
  48. digitalkin-0.3.2a2/examples/monitoring/digitalkin_observability/prometheus.py +0 -137
  49. digitalkin-0.3.2a2/examples/monitoring/tests/test_metrics.py +0 -172
  50. digitalkin-0.3.2a2/src/digitalkin/models/module/base_types.py +0 -61
  51. digitalkin-0.3.2a2/src/digitalkin/models/module/module_context.py +0 -406
  52. digitalkin-0.3.2a2/src/digitalkin/models/module/module_types.py +0 -29
  53. digitalkin-0.3.2a2/src/digitalkin/models/module/setup_types.py +0 -547
  54. digitalkin-0.3.2a2/src/digitalkin/models/module/tool_cache.py +0 -230
  55. digitalkin-0.3.2a2/src/digitalkin/models/module/tool_reference.py +0 -160
  56. digitalkin-0.3.2a2/src/digitalkin/models/services/registry.py +0 -77
  57. digitalkin-0.3.2a2/src/digitalkin/services/registry/registry_models.py +0 -15
  58. digitalkin-0.3.2a2/src/digitalkin/utils/conditional_schema.py +0 -260
  59. digitalkin-0.3.2a2/src/digitalkin/utils/schema_splitter.py +0 -290
  60. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/LICENSE +0 -0
  61. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/README.md +0 -0
  62. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/__init__.py +0 -0
  63. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/mock/__init__.py +0 -0
  64. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/mock/mock_pb2.py +0 -0
  65. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  66. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/server_async_insecure.py +0 -0
  67. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/server_async_secure.py +0 -0
  68. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/server_sync_insecure.py +0 -0
  69. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/base_server/server_sync_secure.py +0 -0
  70. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/modules/__init__.py +0 -0
  71. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/modules/cpu_intensive_module.py +0 -0
  72. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/modules/dynamic_setup_module.py +0 -0
  73. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/modules/minimal_llm_module.py +0 -0
  74. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/modules/text_transform_module.py +0 -0
  75. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/services/filesystem_module.py +0 -0
  76. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/examples/services/storage_module.py +0 -0
  77. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/setup.cfg +0 -0
  78. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/__init__.py +0 -0
  79. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/__init__.py +0 -0
  80. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/common/__init__.py +0 -0
  81. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/common/factories.py +0 -0
  82. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/__init__.py +0 -0
  83. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
  84. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/taskiq_broker.py +0 -0
  85. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/__init__.py +0 -0
  86. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
  87. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
  88. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/__init__.py +0 -0
  89. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  90. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  91. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/exceptions.py +0 -0
  92. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  93. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/logger.py +0 -0
  94. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/__init__.py +0 -0
  95. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/base_mixin.py +0 -0
  96. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/callback_mixin.py +0 -0
  97. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/chat_history_mixin.py +0 -0
  98. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/cost_mixin.py +0 -0
  99. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  100. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  101. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/logger_mixin.py +0 -0
  102. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/storage_mixin.py +0 -0
  103. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/__init__.py +0 -0
  104. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/core/__init__.py +0 -0
  105. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/core/job_manager_models.py +0 -0
  106. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  107. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/grpc_servers/types.py +0 -0
  108. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/module.py +0 -0
  109. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/services/__init__.py +0 -0
  110. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/services/storage.py +0 -0
  111. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/__init__.py +0 -0
  112. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/archetype_module.py +0 -0
  113. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/tool_module.py +0 -0
  114. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/trigger_handler.py +0 -0
  115. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
  116. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
  117. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
  118. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/py.typed +0 -0
  119. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/__init__.py +0 -0
  120. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/agent/__init__.py +0 -0
  121. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/agent/agent_strategy.py +0 -0
  122. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/agent/default_agent.py +0 -0
  123. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/base_strategy.py +0 -0
  124. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/communication/__init__.py +0 -0
  125. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/__init__.py +0 -0
  126. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/__init__.py +0 -0
  127. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  128. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  129. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/identity/__init__.py +0 -0
  130. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/identity/default_identity.py +0 -0
  131. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  132. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/registry/exceptions.py +0 -0
  133. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/services_config.py +0 -0
  134. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/services_models.py +0 -0
  135. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/__init__.py +0 -0
  136. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/default_setup.py +0 -0
  137. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/grpc_setup.py +0 -0
  138. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  139. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/snapshot/__init__.py +0 -0
  140. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/snapshot/default_snapshot.py +0 -0
  141. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/snapshot/snapshot_strategy.py +0 -0
  142. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/__init__.py +0 -0
  143. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/default_storage.py +0 -0
  144. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/grpc_storage.py +0 -0
  145. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  146. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/__init__.py +0 -0
  147. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  148. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
  149. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/arg_parser.py +0 -0
  150. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/development_mode_action.py +0 -0
  151. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  152. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/package_discover.py +0 -0
  153. {digitalkin-0.3.2a2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/dependency_links.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.3.2a2
3
+ Version: 0.3.2.dev2
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
@@ -434,32 +434,32 @@ License: Attribution-NonCommercial-ShareAlike 4.0 International
434
434
  Creative Commons may be contacted at creativecommons.org.
435
435
  https://creativecommons.org/licenses/by-nc-sa/4.0/
436
436
 
437
- Project-URL: Documentation, https://github.com/DigitalKin-ai/digitalkin
438
437
  Project-URL: Homepage, https://github.com/DigitalKin-ai/digitalkin
438
+ Project-URL: Documentation, https://github.com/DigitalKin-ai/digitalkin
439
439
  Project-URL: Issues, https://github.com/DigitalKin-ai/digitalkin/issues
440
- Keywords: agent,digitalkin,gprc,kin,sdk
440
+ Keywords: digitalkin,kin,agent,gprc,sdk
441
441
  Classifier: Development Status :: 3 - Alpha
442
442
  Classifier: Intended Audience :: Developers
443
- Classifier: License :: Other/Proprietary License
444
443
  Classifier: Operating System :: OS Independent
445
- Classifier: Programming Language :: Python :: 3 :: Only
444
+ Classifier: Topic :: Software Development :: Libraries
445
+ Classifier: Programming Language :: Python
446
446
  Classifier: Programming Language :: Python :: 3.10
447
447
  Classifier: Programming Language :: Python :: 3.11
448
448
  Classifier: Programming Language :: Python :: 3.12
449
449
  Classifier: Programming Language :: Python :: 3.13
450
- Classifier: Programming Language :: Python
451
- Classifier: Topic :: Software Development :: Libraries
450
+ Classifier: Programming Language :: Python :: 3 :: Only
451
+ Classifier: License :: Other/Proprietary License
452
452
  Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
- Requires-Dist: agentic-mesh-protocol==0.2.1.dev1
455
+ Requires-Dist: agentic-mesh-protocol==0.2.1.dev0
456
456
  Requires-Dist: grpcio-health-checking>=1.76.0
457
457
  Requires-Dist: grpcio-reflection>=1.76.0
458
458
  Requires-Dist: grpcio-status>=1.76.0
459
459
  Requires-Dist: pydantic>=2.12.5
460
460
  Requires-Dist: surrealdb>=1.0.7
461
461
  Provides-Extra: taskiq
462
- Requires-Dist: rstream>=0.40.1; extra == "taskiq"
462
+ Requires-Dist: rstream>=0.40.0; extra == "taskiq"
463
463
  Requires-Dist: taskiq-aio-pika>=0.5.0; extra == "taskiq"
464
464
  Requires-Dist: taskiq-redis>=1.2.0; extra == "taskiq"
465
465
  Requires-Dist: taskiq[reload]>=0.12.1; extra == "taskiq"
@@ -1,34 +1,34 @@
1
1
  [build-system]
2
- build-backend = "setuptools.build_meta"
3
2
  requires = [ "setuptools >= 75.8.2", "wheel" ]
3
+ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
 
7
- description = "SDK to build kin used in DigitalKin"
8
- license = { file = "LICENSE" }
9
7
  name = "digitalkin"
8
+ description = "SDK to build kin used in DigitalKin"
10
9
  readme = "README.md"
11
10
  requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
12
 
13
- keywords = [ "agent", "digitalkin", "gprc", "kin", "sdk" ]
13
+ keywords = [ "digitalkin", "kin", "agent", "gprc", "sdk" ]
14
14
 
15
+ version = "0.3.2.dev2"
15
16
  classifiers = [
16
17
  "Development Status :: 3 - Alpha",
17
18
  "Intended Audience :: Developers",
18
- "License :: Other/Proprietary License",
19
19
  "Operating System :: OS Independent",
20
- "Programming Language :: Python :: 3 :: Only",
20
+ "Topic :: Software Development :: Libraries",
21
+ "Programming Language :: Python",
21
22
  "Programming Language :: Python :: 3.10",
22
23
  "Programming Language :: Python :: 3.11",
23
24
  "Programming Language :: Python :: 3.12",
24
25
  "Programming Language :: Python :: 3.13",
25
- "Programming Language :: Python",
26
- "Topic :: Software Development :: Libraries",
26
+ "Programming Language :: Python :: 3 :: Only",
27
+ "License :: Other/Proprietary License",
27
28
  ]
28
- version = "0.3.2.a2"
29
29
 
30
30
  dependencies = [
31
- "agentic-mesh-protocol==0.2.1.dev1",
31
+ "agentic-mesh-protocol==0.2.1.dev0",
32
32
  "grpcio-health-checking>=1.76.0",
33
33
  "grpcio-reflection>=1.76.0",
34
34
  "grpcio-status>=1.76.0",
@@ -38,81 +38,81 @@
38
38
 
39
39
  [project.optional-dependencies]
40
40
  taskiq = [
41
- "rstream>=0.40.1",
41
+ "rstream>=0.40.0",
42
42
  "taskiq-aio-pika>=0.5.0",
43
43
  "taskiq-redis>=1.2.0",
44
44
  "taskiq[reload]>=0.12.1",
45
45
  ]
46
46
 
47
47
  [project.urls]
48
- Documentation = "https://github.com/DigitalKin-ai/digitalkin"
49
48
  Homepage = "https://github.com/DigitalKin-ai/digitalkin"
49
+ Documentation = "https://github.com/DigitalKin-ai/digitalkin"
50
50
  Issues = "https://github.com/DigitalKin-ai/digitalkin/issues"
51
51
 
52
52
  [[project.authors]]
53
- email = "contact@digitalkin.ai"
54
53
  name = "DigitalKin.ai"
54
+ email = "contact@digitalkin.ai"
55
55
 
56
56
  [dependency-groups]
57
+ dev = [
58
+ "typos>=1.40.0",
59
+ "ruff>=0.14.8",
60
+ "mypy>=1.19.0",
61
+ "pyright>=1.1.407",
62
+ "pre-commit>=4.5.0",
63
+ "bump-my-version>=1.2.4",
64
+ "build>=1.3.0",
65
+ "twine>=6.2.0",
66
+ "cryptography>=46.0.3",
67
+ ]
57
68
  tests = [
58
69
  "freezegun>=1.5.5",
59
- "grpcio-testing>=1.76.0",
60
70
  "hdrhistogram>=0.10.3",
71
+ "grpcio-testing>=1.76.0",
61
72
  "psutil>=7.1.3",
73
+ "pytest>=9.0.2",
62
74
  "pytest-asyncio>=1.3.0",
63
75
  "pytest-cov>=7.0.0",
64
- "pytest-html==4.2.0",
76
+ "pytest-html==4.1.1",
65
77
  "pytest-json-report==1.5.0",
66
78
  "pytest-timeout>=2.4.0",
67
- "pytest>=9.0.2",
68
- ]
69
- dev = [
70
- "build>=1.3.0",
71
- "bump-my-version>=1.2.5",
72
- "cryptography>=46.0.4",
73
- "mypy>=1.19.1",
74
- "pre-commit>=4.5.1",
75
- "pyright>=1.1.407",
76
- "ruff>=0.14.14",
77
- "twine>=6.2.0",
78
- "typos>=1.42.3",
79
79
  ]
80
80
  docs = [
81
- "griffe-inherited-docstrings>=1.1.2",
81
+ "mike>=2.1.3",
82
82
  "markdown-callouts>=0.4.0",
83
83
  "markdown-exec>=1.12.1",
84
- "mike>=2.1.3",
84
+ "mkdocs>=1.6.1",
85
+ "mkdocs-coverage>=2.0.0",
86
+ "mkdocs-llmstxt>=0.5.0",
87
+ "mkdocs-redirects>=1.2.2",
88
+ "mkdocstrings>=1.0.0",
89
+ "griffe-inherited-docstrings>=1.1.2",
85
90
  "mkdocs-autorefs>=1.4.3",
86
91
  "mkdocs-awesome-pages-plugin>=2.10.1",
87
- "mkdocs-coverage>=2.0.0",
88
92
  "mkdocs-git-committers-plugin-2>=2.5.0",
89
- "mkdocs-git-revision-date-localized-plugin>=1.5.1",
93
+ "mkdocs-git-revision-date-localized-plugin>=1.5.0",
90
94
  "mkdocs-glightbox>=0.5.2",
91
- "mkdocs-include-markdown-plugin>=7.2.1",
95
+ "mkdocs-include-markdown-plugin>=7.2.0",
92
96
  "mkdocs-literate-nav>=0.6.2",
93
- "mkdocs-llmstxt>=0.5.0",
94
97
  "mkdocs-material[imaging]>=9.7.0",
95
98
  "mkdocs-minify-plugin>=0.8.0",
96
- "mkdocs-open-in-new-tab>=1.0.8",
97
- "mkdocs-redirects>=1.2.2",
98
99
  "mkdocs-section-index>=0.3.10",
99
- "mkdocs>=1.6.1",
100
100
  "mkdocstrings-python>=2.0.1",
101
- "mkdocstrings>=1.0.2",
101
+ "mkdocs-open-in-new-tab>=1.0.8",
102
102
  "tomli>=2.3.0",
103
103
  ]
104
104
  [tool.setuptools]
105
105
  package-dir = { "" = "src" }
106
106
 
107
107
  [tool.setuptools.packages.find]
108
- where = [ "examples", "src" ]
109
- [tool.ruff]
108
+ where = [ "src", "examples" ]
110
109
 
111
- indent-width = 4
112
- line-length = 120
113
- show-fixes = true
110
+ [tool.ruff]
114
111
  src = [ "src" ]
115
112
  target-version = "py310"
113
+ line-length = 120
114
+ indent-width = 4
115
+ show-fixes = true
116
116
  unsafe-fixes = true
117
117
  # Enable preview features.
118
118
  exclude = [
@@ -135,59 +135,59 @@
135
135
  "buck-out",
136
136
  "build",
137
137
  "dist",
138
- "docs/*",
139
- "examples/*",
138
+ "node_modules",
139
+ "venv",
140
140
  "factory.py",
141
141
  "generate_certificates.py",
142
- "node_modules",
142
+ "examples/*",
143
143
  "tests/*",
144
- "venv",
144
+ "docs/*",
145
145
  ]
146
146
  preview = true
147
147
 
148
148
  [tool.ruff.lint]
149
149
  select = [
150
- "A", # flake8-builtins
151
150
  "ANN", # flake8-annotations
152
- "ARG", # flake8-unused-arguments
153
151
  "ASYNC", # flake8-async
154
- "C4", # flake8-comprehensions
155
- "C90", # mccabe (complex-structure)
152
+ "S", # flake8-bandit
153
+ "FBT", # flake8-boolean-trap
154
+ "A", # flake8-builtins
156
155
  "COM", # flake8-commas
157
- "D", # pydocstyle
158
- "DOC", # pydoclint
156
+ "C4", # flake8-comprehensions
159
157
  "DTZ", # flake8-datetimez
160
- "E", # pycodestyle-errors
161
158
  "EM", # flake8-errmsg
162
- "ERA", # eradicate
163
- "F", # pyflakes
164
- "FBT", # flake8-boolean-trap
165
- "FLY", # flynt
166
- "FURB", # Refurb
167
- "G", # flake8-logging-format
168
- "I", # isort
169
- "ICN", # flake8-import-conventions
170
159
  "ISC", # flake8-implicit-str-concat
160
+ "ICN", # flake8-import-conventions
161
+ "G", # flake8-logging-format
171
162
  "LOG", # flake8-logging
172
- "N", # pep8-naming
173
- "PERF", # Perflint
174
163
  "PIE", # flake8-pie
175
- "PL", # Pylint
176
- "PL", # Pylint
177
- "PT", # flake8-pytest-style
164
+ "T20", # flake8-print
178
165
  "PYI", # flake8-pyi
166
+ "PT", # flake8-pytest-style
179
167
  "Q", # flake8-quotes
180
- "RET", # flake8-return
181
168
  "RSE", # flake8-raise
182
- "RUF", # Ruff-specific rules
183
- "S", # flake8-bandit
184
- "SIM", # flake8-simplify
169
+ "RET", # flake8-return
185
170
  "SLF", # flake8-self
186
- "T20", # flake8-print
171
+ "SIM", # flake8-simplify
187
172
  "TC", # flake8-type-checking
173
+ "ARG", # flake8-unused-arguments
174
+ "FLY", # flynt
175
+ "C90", # mccabe (complex-structure)
176
+ "PL", # Pylint
188
177
  "TRY", # tryceratops
189
- "UP", # pyupgrade
178
+ "PERF", # Perflint
179
+ "E", # pycodestyle-errors
190
180
  "W", # pycodestyle-warnings
181
+ "F", # pyflakes
182
+ "I", # isort
183
+ "D", # pydocstyle
184
+ "UP", # pyupgrade
185
+ "N", # pep8-naming
186
+ "ERA", # eradicate
187
+ "RUF", # Ruff-specific rules
188
+ "DOC", # pydoclint
189
+ "FURB", # Refurb
190
+ "PL", # Pylint
191
191
  ]
192
192
 
193
193
  ignore = [
@@ -203,66 +203,66 @@
203
203
 
204
204
  [tool.ruff.lint.per-file-ignores]
205
205
  "tests/**/*.py" = [
206
+ "S101",
207
+ "SLF001",
208
+ "PLC2701",
209
+ "PLR6301",
210
+ "TRY002",
211
+ "FBT001",
212
+ "FBT002",
206
213
  "ANN001",
207
- "ANN002",
208
214
  "ANN003",
209
- "ANN201",
210
- "ANN401",
211
- "ARG001",
215
+ "ANN002",
212
216
  "ARG002",
213
- "C901",
214
- "D100",
215
- "D101",
216
- "D102",
217
+ "PLR0914",
218
+ "PLR2004",
219
+ "PLR0915",
220
+ "PLR1702",
221
+ "D417",
222
+ "ARG001",
217
223
  "D103",
224
+ "D102",
225
+ "D101",
218
226
  "D107",
219
- "D417",
227
+ "N802",
220
228
  "DOC201",
221
229
  "DOC501",
222
- "E501",
223
- "ERA001",
224
- "FBT001",
225
- "FBT002",
226
- "G004",
227
- "N802",
228
230
  "PERF203",
229
- "PLC2701",
231
+ "C901",
230
232
  "PLR0911",
233
+ "G004",
234
+ "ERA001",
235
+ "E501",
236
+ "ANN401",
231
237
  "PLR0912",
232
- "PLR0914",
233
- "PLR0915",
234
- "PLR1702",
235
- "PLR2004",
236
- "PLR6301",
237
- "S101",
238
- "SLF001",
239
- "TRY002",
238
+ "ANN201",
239
+ "D100",
240
240
  ]
241
241
 
242
242
  [tool.ruff.lint.pylint]
243
243
  max-args = 10
244
244
 
245
245
  [tool.ruff.format]
246
- docstring-code-format = true
246
+ quote-style = "double"
247
247
  indent-style = "space"
248
248
  line-ending = "auto"
249
- quote-style = "double"
249
+ docstring-code-format = true
250
250
  skip-magic-trailing-comma = false
251
251
 
252
252
  [tool.pyright]
253
- exclude = [ "**/.venv", "**/__pycache__" ]
254
253
  include = [ "src" ]
254
+ exclude = [ "**/.venv", "**/__pycache__" ]
255
255
 
256
256
  [tool.mypy]
257
+ exclude = [ "tests", "examples" ]
257
258
 
258
- exclude = [ "examples", "tests" ]
259
259
  [tool.pytest.ini_options]
260
-
261
- asyncio_mode = "auto"
262
- python_files = "test_*.py"
263
260
  testpaths = [ "tests" ]
264
- log_cli = false
265
- log_level = "ERROR"
261
+ python_files = "test_*.py"
262
+ asyncio_mode = "auto"
263
+
264
+ log_cli = false
265
+ log_level = "ERROR"
266
266
 
267
267
  # Default timeout for tests (in seconds)
268
268
  timeout = 10
@@ -270,10 +270,10 @@
270
270
 
271
271
  # Custom markers
272
272
  markers = [
273
- "edge_case: marks tests for boundary conditions and edge cases",
274
- "grpc: marks tests for gRPC service functionality",
275
273
  "integration: marks tests that require real SurrealDB connection (deselect with '-m \"not integration\"')",
276
- "regression: marks tests for previously fixed bugs",
274
+ "grpc: marks tests for gRPC service functionality",
277
275
  "smoke: marks critical path tests that should always pass",
276
+ "edge_case: marks tests for boundary conditions and edge cases",
278
277
  "validation: marks tests for input validation and schema checking",
278
+ "regression: marks tests for previously fixed bugs",
279
279
  ]
@@ -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.2.a2"
8
+ __version__ = "0.3.2.dev2"
@@ -5,7 +5,7 @@ import datetime
5
5
  import uuid
6
6
  from collections.abc import AsyncGenerator, AsyncIterator
7
7
  from contextlib import asynccontextmanager
8
- from typing import TYPE_CHECKING, Any
8
+ from typing import Any
9
9
 
10
10
  import grpc
11
11
 
@@ -13,13 +13,10 @@ from digitalkin.core.common import ConnectionFactory, ModuleFactory
13
13
  from digitalkin.core.job_manager.base_job_manager import BaseJobManager
14
14
  from digitalkin.core.task_manager.local_task_manager import LocalTaskManager
15
15
  from digitalkin.core.task_manager.task_session import TaskSession
16
-
17
- if TYPE_CHECKING:
18
- from digitalkin.core.task_manager.surrealdb_repository import SurrealDBConnection
19
16
  from digitalkin.logger import logger
20
17
  from digitalkin.models.core.task_monitor import TaskStatus
21
- from digitalkin.models.module.base_types import InputModelT, OutputModelT, SetupModelT
22
18
  from digitalkin.models.module.module import ModuleCodeModel
19
+ from digitalkin.models.module.module_types import InputModelT, OutputModelT, SetupModelT
23
20
  from digitalkin.modules._base_module import BaseModule
24
21
  from digitalkin.services.services_models import ServicesMode
25
22
 
@@ -32,6 +29,10 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
32
29
  to handle their output data.
33
30
  """
34
31
 
32
+ async def start(self) -> None:
33
+ """Start manager."""
34
+ self.channel = await ConnectionFactory.create_surreal_connection("task_manager", datetime.timedelta(seconds=5))
35
+
35
36
  def __init__(
36
37
  self,
37
38
  module_class: type[BaseModule],
@@ -54,11 +55,6 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
54
55
  super().__init__(module_class, services_mode, task_manager)
55
56
 
56
57
  self._lock = asyncio.Lock()
57
- self.channel: SurrealDBConnection | None = None
58
-
59
- async def start(self) -> None:
60
- """Start manager."""
61
- self.channel = await ConnectionFactory.create_surreal_connection("task_manager", datetime.timedelta(seconds=5))
62
58
 
63
59
  async def generate_config_setup_module_response(self, job_id: str) -> SetupModelT | ModuleCodeModel:
64
60
  """Generate a stream consumer for a module's output data.
@@ -90,10 +86,7 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
90
86
  message=f"Module {job_id} did not respond within 30 seconds",
91
87
  )
92
88
  finally:
93
- logger.debug(
94
- "Config setup response retrieved",
95
- extra={"job_id": job_id, "queue_empty": session.queue.empty()},
96
- )
89
+ logger.info(f"{job_id=}: {session.queue.empty()}")
97
90
 
98
91
  async def create_config_setup_instance_job(
99
92
  self,
@@ -117,14 +110,11 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
117
110
  str: The unique identifier (job ID) of the created job.
118
111
 
119
112
  Raises:
120
- RuntimeError: If start() was not called before creating jobs.
121
113
  Exception: If the module fails to start.
122
114
  """
123
115
  job_id = str(uuid.uuid4())
116
+ # TODO: Ensure the job_id is unique.
124
117
  module = ModuleFactory.create_module_instance(self.module_class, job_id, mission_id, setup_id, setup_version_id)
125
- if self.channel is None:
126
- msg = "JobManager.start() must be called before creating jobs"
127
- raise RuntimeError(msg)
128
118
  self.tasks_sessions[job_id] = TaskSession(job_id, mission_id, self.channel, module)
129
119
 
130
120
  try:
@@ -136,7 +126,7 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
136
126
  except Exception:
137
127
  # Remove the module from the manager in case of an error.
138
128
  del self.tasks_sessions[job_id]
139
- logger.exception("Failed to start module", extra={"job_id": job_id})
129
+ logger.exception("Failed to start module %s: %s", job_id)
140
130
  raise
141
131
  else:
142
132
  return job_id
@@ -144,33 +134,13 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
144
134
  async def add_to_queue(self, job_id: str, output_data: OutputModelT | ModuleCodeModel) -> None:
145
135
  """Add output data to the queue for a specific job.
146
136
 
147
- Uses timeout-based backpressure: if the queue is full after 5s,
148
- drops the oldest message to make room for the new one.
149
- Rejects writes after stream is closed to prevent message loss.
137
+ This method is used as a callback to handle output data generated by a module job.
150
138
 
151
139
  Args:
152
140
  job_id: The unique identifier of the job.
153
141
  output_data: The output data produced by the job.
154
142
  """
155
- session = self.tasks_sessions.get(job_id)
156
- if session is None:
157
- logger.warning("Queue write rejected - session not found", extra={"job_id": job_id})
158
- return
159
-
160
- if session.stream_closed:
161
- logger.debug("Queue write rejected - stream closed", extra={"job_id": job_id})
162
- return
163
-
164
- try:
165
- await asyncio.wait_for(session.queue.put(output_data.model_dump()), timeout=5.0)
166
- except asyncio.TimeoutError:
167
- logger.warning("Queue full, dropping oldest message", extra={"job_id": job_id})
168
- try:
169
- session.queue.get_nowait()
170
- session.queue.task_done()
171
- except asyncio.QueueEmpty:
172
- pass
173
- session.queue.put_nowait(output_data.model_dump())
143
+ await self.tasks_sessions[job_id].queue.put(output_data.model_dump())
174
144
 
175
145
  @asynccontextmanager # type: ignore
176
146
  async def generate_stream_consumer(self, job_id: str) -> AsyncIterator[AsyncGenerator[dict[str, Any], None]]: # type: ignore
@@ -207,39 +177,42 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
207
177
  logger.debug("Session: %s with Module %s", job_id, session.module)
208
178
 
209
179
  async def _stream() -> AsyncGenerator[dict[str, Any], Any]:
210
- """Stream output data from the module with bounded blocking.
180
+ """Stream output data from the module with simple blocking pattern.
211
181
 
212
- Uses a 1-second timeout on queue.get() to periodically re-check
213
- termination flags, preventing indefinite hangs when the task crashes
214
- without producing output.
182
+ This implementation uses a simple one-item-at-a-time pattern optimized
183
+ for local execution where we have direct access to session status:
184
+ 1. Block waiting for each item
185
+ 2. Check termination conditions after each item
186
+ 3. Clean shutdown when task completes
187
+
188
+ This pattern provides:
189
+ - Immediate termination when task completes
190
+ - Direct session status monitoring
191
+ - Simple, predictable behavior for local tasks
215
192
 
216
193
  Yields:
217
194
  dict: Output data generated by the module.
218
195
  """
219
196
  while True:
220
- if session.stream_closed or session.is_cancelled.is_set():
221
- logger.debug("Stream ending for job %s (pre-check)", job_id)
222
- break
223
-
224
- try:
225
- msg = await asyncio.wait_for(session.queue.get(), timeout=1.0)
226
- except asyncio.TimeoutError:
227
- continue
228
-
197
+ # Block for next item - if queue is empty but producer not finished yet
198
+ msg = await session.queue.get()
229
199
  try:
230
200
  yield msg
231
201
  finally:
202
+ # Always mark task as done, even if consumer raises exception
232
203
  session.queue.task_done()
233
204
 
205
+ # Check termination conditions after each message
206
+ # This allows immediate shutdown when the task completes
234
207
  if (
235
- session.stream_closed
236
- or session.is_cancelled.is_set()
208
+ session.is_cancelled.is_set()
237
209
  or (session.status is TaskStatus.COMPLETED and session.queue.empty())
238
210
  or session.status is TaskStatus.FAILED
239
211
  ):
240
212
  logger.debug(
241
- "Stream ending for job %s: status=%s, queue_empty=%s",
213
+ "Stream ending for job %s: cancelled=%s, status=%s, queue_empty=%s",
242
214
  job_id,
215
+ session.is_cancelled.is_set(),
243
216
  session.status,
244
217
  session.queue.empty(),
245
218
  )
@@ -286,18 +259,6 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
286
259
  logger.info("Managed task started: '%s'", job_id, extra={"task_id": job_id})
287
260
  return job_id
288
261
 
289
- async def clean_session(self, task_id: str, mission_id: str) -> bool:
290
- """Clean a task's session.
291
-
292
- Args:
293
- task_id: Unique identifier for the task.
294
- mission_id: Mission identifier.
295
-
296
- Returns:
297
- bool: True if the task was successfully cleaned, False otherwise.
298
- """
299
- return await self._task_manager.clean_session(task_id, mission_id)
300
-
301
262
  async def stop_module(self, job_id: str) -> bool:
302
263
  """Stop a running module job.
303
264
 
@@ -310,23 +271,20 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
310
271
  Raises:
311
272
  Exception: If an error occurs while stopping the module.
312
273
  """
313
- logger.info("Stop module requested", extra={"job_id": job_id})
274
+ logger.info(f"STOP required for {job_id=}")
314
275
 
315
276
  async with self._lock:
316
277
  session = self.tasks_sessions.get(job_id)
317
278
 
318
279
  if not session:
319
- logger.warning("Session not found", extra={"job_id": job_id})
280
+ logger.warning(f"session with id: {job_id} not found")
320
281
  return False
321
282
  try:
322
283
  await session.module.stop()
323
284
  await self.cancel_task(job_id, session.mission_id)
324
- logger.debug(
325
- "Module stopped successfully",
326
- extra={"job_id": job_id, "mission_id": session.mission_id},
327
- )
328
- except Exception:
329
- logger.exception("Error stopping module", extra={"job_id": job_id})
285
+ logger.debug(f"session {job_id} ({session.module.name}) stopped successfully")
286
+ except Exception as e:
287
+ logger.error(f"Error while stopping module {job_id}: {e}")
330
288
  raise
331
289
  else:
332
290
  return True
@@ -373,7 +331,7 @@ class SingleJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
373
331
  await asyncio.gather(*stop_tasks, return_exceptions=True)
374
332
 
375
333
  # Close SurrealDB connection after stopping all modules
376
- if self.channel is not None:
334
+ if hasattr(self, "channel"):
377
335
  try:
378
336
  await self.channel.close()
379
337
  logger.info("SingleJobManager: SurrealDB connection closed")