digitalkin 0.3.1.dev2__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 (143) hide show
  1. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/PKG-INFO +5 -5
  2. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/server_async_insecure.py +6 -5
  3. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/server_async_secure.py +6 -5
  4. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/server_sync_insecure.py +5 -4
  5. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/server_sync_secure.py +5 -4
  6. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/modules/dynamic_setup_module.py +5 -29
  7. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/services/filesystem_module.py +7 -5
  8. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/services/storage_module.py +4 -2
  9. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/pyproject.toml +8 -9
  10. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/__version__.py +1 -1
  11. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/base_job_manager.py +1 -1
  12. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/single_job_manager.py +1 -1
  13. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/taskiq_broker.py +7 -6
  14. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/taskiq_job_manager.py +1 -1
  15. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/surrealdb_repository.py +7 -7
  16. digitalkin-0.3.2.dev2/src/digitalkin/grpc_servers/module_server.py +208 -0
  17. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/module_servicer.py +7 -7
  18. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +22 -6
  19. digitalkin-0.3.2.dev2/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
  20. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/__init__.py +1 -1
  21. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/core/job_manager_models.py +0 -8
  22. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/__init__.py +10 -13
  23. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/module_context.py +21 -12
  24. digitalkin-0.3.2.dev2/src/digitalkin/models/module/utility.py +146 -0
  25. digitalkin-0.3.2.dev2/src/digitalkin/models/services/registry.py +42 -0
  26. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/__init__.py +5 -1
  27. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/_base_module.py +54 -19
  28. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/archetype_module.py +6 -1
  29. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/tool_module.py +6 -1
  30. digitalkin-0.3.2.dev2/src/digitalkin/modules/triggers/__init__.py +12 -0
  31. digitalkin-0.3.2.dev2/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
  32. digitalkin-0.3.2.dev2/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
  33. digitalkin-0.3.2.dev2/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
  34. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/__init__.py +4 -0
  35. digitalkin-0.3.2.dev2/src/digitalkin/services/communication/__init__.py +7 -0
  36. digitalkin-0.3.2.dev2/src/digitalkin/services/communication/communication_strategy.py +76 -0
  37. digitalkin-0.3.2.dev2/src/digitalkin/services/communication/default_communication.py +101 -0
  38. digitalkin-0.3.2.dev2/src/digitalkin/services/communication/grpc_communication.py +223 -0
  39. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/grpc_cost.py +1 -1
  40. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/grpc_filesystem.py +1 -1
  41. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/__init__.py +27 -0
  42. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/default_registry.py +141 -0
  43. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/exceptions.py +47 -0
  44. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/grpc_registry.py +306 -0
  45. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/registry_models.py +43 -0
  46. digitalkin-0.3.2.dev2/src/digitalkin/services/registry/registry_strategy.py +98 -0
  47. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/services_config.py +25 -3
  48. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/services_models.py +5 -1
  49. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/default_setup.py +1 -1
  50. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/grpc_setup.py +1 -1
  51. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/grpc_storage.py +1 -1
  52. digitalkin-0.3.2.dev2/src/digitalkin/services/user_profile/__init__.py +12 -0
  53. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/grpc_user_profile.py +2 -2
  54. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/PKG-INFO +5 -5
  55. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/SOURCES.txt +14 -2
  56. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/requires.txt +4 -4
  57. digitalkin-0.3.1.dev2/src/digitalkin/grpc_servers/module_server.py +0 -267
  58. digitalkin-0.3.1.dev2/src/digitalkin/grpc_servers/registry_server.py +0 -65
  59. digitalkin-0.3.1.dev2/src/digitalkin/grpc_servers/registry_servicer.py +0 -456
  60. digitalkin-0.3.1.dev2/src/digitalkin/services/registry/__init__.py +0 -6
  61. digitalkin-0.3.1.dev2/src/digitalkin/services/registry/default_registry.py +0 -10
  62. digitalkin-0.3.1.dev2/src/digitalkin/services/registry/registry_strategy.py +0 -14
  63. digitalkin-0.3.1.dev2/src/digitalkin/services/user_profile/__init__.py +0 -1
  64. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/LICENSE +0 -0
  65. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/README.md +0 -0
  66. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/__init__.py +0 -0
  67. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/mock/__init__.py +0 -0
  68. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/mock/mock_pb2.py +0 -0
  69. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  70. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/modules/__init__.py +0 -0
  71. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/modules/cpu_intensive_module.py +1 -1
  72. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/modules/minimal_llm_module.py +1 -1
  73. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/examples/modules/text_transform_module.py +1 -1
  74. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/setup.cfg +0 -0
  75. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/__init__.py +0 -0
  76. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/__init__.py +0 -0
  77. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/common/__init__.py +0 -0
  78. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/common/factories.py +0 -0
  79. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/job_manager/__init__.py +0 -0
  80. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/__init__.py +0 -0
  81. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
  82. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
  83. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
  84. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/task_executor.py +0 -0
  85. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/core/task_manager/task_session.py +0 -0
  86. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/__init__.py +0 -0
  87. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  88. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  89. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/exceptions.py +0 -0
  90. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  91. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/logger.py +0 -0
  92. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/__init__.py +0 -0
  93. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/base_mixin.py +0 -0
  94. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/callback_mixin.py +0 -0
  95. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/chat_history_mixin.py +0 -0
  96. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/cost_mixin.py +0 -0
  97. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  98. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  99. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/logger_mixin.py +0 -0
  100. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/mixins/storage_mixin.py +0 -0
  101. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/core/__init__.py +0 -0
  102. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/core/task_monitor.py +0 -0
  103. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  104. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/grpc_servers/models.py +0 -0
  105. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/grpc_servers/types.py +0 -0
  106. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/module.py +0 -0
  107. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/module/module_types.py +0 -0
  108. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/services/__init__.py +0 -0
  109. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/services/cost.py +0 -0
  110. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/models/services/storage.py +0 -0
  111. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/modules/trigger_handler.py +0 -0
  112. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/py.typed +0 -0
  113. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/agent/__init__.py +0 -0
  114. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/agent/agent_strategy.py +0 -0
  115. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/agent/default_agent.py +0 -0
  116. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/base_strategy.py +0 -0
  117. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/__init__.py +0 -0
  118. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  119. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/cost/default_cost.py +0 -0
  120. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/__init__.py +0 -0
  121. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  122. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  123. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/identity/__init__.py +0 -0
  124. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/identity/default_identity.py +0 -0
  125. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  126. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/__init__.py +0 -0
  127. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  128. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/snapshot/__init__.py +0 -0
  129. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/snapshot/default_snapshot.py +0 -0
  130. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/snapshot/snapshot_strategy.py +0 -0
  131. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/__init__.py +0 -0
  132. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/default_storage.py +0 -0
  133. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  134. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  135. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
  136. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/__init__.py +0 -0
  137. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/arg_parser.py +0 -0
  138. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/development_mode_action.py +0 -0
  139. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/dynamic_schema.py +0 -0
  140. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  141. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin/utils/package_discover.py +0 -0
  142. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  143. {digitalkin-0.3.1.dev2 → digitalkin-0.3.2.dev2}/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.1.dev2
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
@@ -452,17 +452,17 @@ 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: digitalkin-proto==0.2.0.dev5
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
- Requires-Dist: surrealdb>=1.0.6
460
+ Requires-Dist: surrealdb>=1.0.7
461
461
  Provides-Extra: taskiq
462
462
  Requires-Dist: rstream>=0.40.0; extra == "taskiq"
463
463
  Requires-Dist: taskiq-aio-pika>=0.5.0; extra == "taskiq"
464
- Requires-Dist: taskiq-redis>=1.1.2; extra == "taskiq"
465
- Requires-Dist: taskiq[reload]>=0.12.0; extra == "taskiq"
464
+ Requires-Dist: taskiq-redis>=1.2.0; extra == "taskiq"
465
+ Requires-Dist: taskiq[reload]>=0.12.1; extra == "taskiq"
466
466
  Dynamic: license-file
467
467
 
468
468
  # DigitalKin Python SDK
@@ -9,8 +9,9 @@ from pathlib import Path
9
9
  # Add parent directory to path to enable imports
10
10
  sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
11
11
 
12
- from digitalkin.grpc_servers._base_server import BaseServer
13
12
  from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
13
+
14
+ from digitalkin.grpc_servers._base_server import BaseServer
14
15
  from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
15
16
  from examples.base_server.mock.mock_pb2_grpc import (
16
17
  Greeter,
@@ -30,7 +31,7 @@ class AsyncGreeterImpl(Greeter):
30
31
 
31
32
  async def SayHello(self, request, context): # noqa: N802
32
33
  """Asynchronous implementation of SayHello method."""
33
- logger.info(f"Received request object: {request}")
34
+ logger.info("Received request object: %s", request)
34
35
  logger.info(f"Request attributes: {vars(request)}")
35
36
  logger.info(f"Received request with name: {request.name}")
36
37
 
@@ -40,7 +41,7 @@ class AsyncGreeterImpl(Greeter):
40
41
  name = "unknown"
41
42
  # Check context metadata
42
43
  for key, value in context.invocation_metadata():
43
- logger.info(f"Metadata: {key}={value}")
44
+ logger.info("Metadata: %s=%s", key, value)
44
45
  if key.lower() == "name":
45
46
  name = value
46
47
 
@@ -97,7 +98,7 @@ async def main_async() -> int:
97
98
  # as the KeyboardInterrupt usually breaks out of asyncio.run()
98
99
  logger.info("Server stopping due to keyboard interrupt...")
99
100
  except Exception as e:
100
- logger.exception(f"Error running server: {e}")
101
+ logger.exception("Error running server: %s", e)
101
102
  return 1
102
103
  finally:
103
104
  # Clean up resources if server was started
@@ -116,7 +117,7 @@ def main():
116
117
  logger.info("Server stopped by keyboard interrupt")
117
118
  return 0 # Clean exit
118
119
  except Exception as e:
119
- logger.exception(f"Fatal error: {e}")
120
+ logger.exception("Fatal error: %s", e)
120
121
  return 1
121
122
 
122
123
 
@@ -9,13 +9,14 @@ from pathlib import Path
9
9
  # Add parent directory to path to enable imports
10
10
  sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
11
11
 
12
- from digitalkin.grpc_servers._base_server import BaseServer
13
12
  from digitalkin.grpc_servers.utils.models import (
14
13
  SecurityMode,
15
14
  ServerConfig,
16
15
  ServerCredentials,
17
16
  ServerMode,
18
17
  )
18
+
19
+ from digitalkin.grpc_servers._base_server import BaseServer
19
20
  from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
20
21
  from examples.base_server.mock.mock_pb2_grpc import (
21
22
  Greeter,
@@ -35,7 +36,7 @@ class AsyncGreeterImpl(Greeter):
35
36
 
36
37
  async def SayHello(self, request, context): # noqa: N802
37
38
  """Asynchronous implementation of SayHello method."""
38
- logger.info(f"Received request object: {request}")
39
+ logger.info("Received request object: %s", request)
39
40
  logger.info(f"Request attributes: {vars(request)}")
40
41
  logger.info(f"Received request with name: {request.name}")
41
42
 
@@ -45,7 +46,7 @@ class AsyncGreeterImpl(Greeter):
45
46
  name = "unknown"
46
47
  # Check context metadata
47
48
  for key, value in context.invocation_metadata():
48
- logger.info(f"Metadata: {key}={value}")
49
+ logger.info("Metadata: %s=%s", key, value)
49
50
  if key.lower() == "name":
50
51
  name = value
51
52
 
@@ -115,7 +116,7 @@ async def main_async() -> int:
115
116
  # as the KeyboardInterrupt usually breaks out of asyncio.run()
116
117
  logger.info("Server stopping due to keyboard interrupt...")
117
118
  except Exception as e:
118
- logger.exception(f"Error running server: {e}")
119
+ logger.exception("Error running server: %s", e)
119
120
  return 1
120
121
  finally:
121
122
  # Clean up resources if server was started
@@ -134,7 +135,7 @@ def main():
134
135
  logger.info("Server stopped by keyboard interrupt")
135
136
  return 0 # Clean exit
136
137
  except Exception as e:
137
- logger.exception(f"Fatal error: {e}")
138
+ logger.exception("Fatal error: %s", e)
138
139
  return 1
139
140
 
140
141
 
@@ -8,8 +8,9 @@ from pathlib import Path
8
8
  # Add parent directory to path to enable imports
9
9
  sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
10
10
 
11
- from digitalkin.grpc_servers._base_server import BaseServer
12
11
  from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
12
+
13
+ from digitalkin.grpc_servers._base_server import BaseServer
13
14
  from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
14
15
  from examples.base_server.mock.mock_pb2_grpc import (
15
16
  Greeter,
@@ -29,7 +30,7 @@ class SyncGreeterServicer(Greeter):
29
30
 
30
31
  def SayHello(self, request, context): # noqa: N802
31
32
  """Implementation of SayHello method."""
32
- logger.info(f"Received request object: {request}")
33
+ logger.info("Received request object: %s", request)
33
34
  logger.info(f"Request attributes: {vars(request)}")
34
35
  logger.info(f"Received request with name: {request.name}")
35
36
 
@@ -39,7 +40,7 @@ class SyncGreeterServicer(Greeter):
39
40
  name = "unknown"
40
41
  # Check context metadata
41
42
  for key, value in context.invocation_metadata():
42
- logger.info(f"Metadata: {key}={value}")
43
+ logger.info("Metadata: %s=%s", key, value)
43
44
  if key.lower() == "name":
44
45
  name = value
45
46
 
@@ -92,7 +93,7 @@ def main() -> int:
92
93
  server.stop()
93
94
 
94
95
  except Exception as e:
95
- logger.exception(f"Error running server: {e}")
96
+ logger.exception("Error running server: %s", e)
96
97
  return 1
97
98
 
98
99
  return 0
@@ -8,13 +8,14 @@ from pathlib import Path
8
8
  # Add parent directory to path to enable imports
9
9
  sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
10
10
 
11
- from digitalkin.grpc_servers._base_server import BaseServer
12
11
  from digitalkin.grpc_servers.utils.models import (
13
12
  SecurityMode,
14
13
  ServerConfig,
15
14
  ServerCredentials,
16
15
  ServerMode,
17
16
  )
17
+
18
+ from digitalkin.grpc_servers._base_server import BaseServer
18
19
  from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
19
20
  from examples.base_server.mock.mock_pb2_grpc import (
20
21
  Greeter,
@@ -34,7 +35,7 @@ class SyncGreeterServicer(Greeter):
34
35
 
35
36
  def SayHello(self, request, context): # noqa: N802
36
37
  """Implementation of SayHello method."""
37
- logger.info(f"Received request object: {request}")
38
+ logger.info("Received request object: %s", request)
38
39
  logger.info(f"Request attributes: {vars(request)}")
39
40
  logger.info(f"Received request with name: {request.name}")
40
41
 
@@ -44,7 +45,7 @@ class SyncGreeterServicer(Greeter):
44
45
  name = "unknown"
45
46
  # Check context metadata
46
47
  for key, value in context.invocation_metadata():
47
- logger.info(f"Metadata: {key}={value}")
48
+ logger.info("Metadata: %s=%s", key, value)
48
49
  if key.lower() == "name":
49
50
  name = value
50
51
 
@@ -111,7 +112,7 @@ def main() -> int:
111
112
  server.stop()
112
113
 
113
114
  except Exception as e:
114
- logger.exception(f"Error running server: {e}")
115
+ logger.exception("Error running server: %s", e)
115
116
  return 1
116
117
 
117
118
  return 0
@@ -209,8 +209,6 @@ class DynamicModuleOutput(DataModel[MessageOutputTrigger]):
209
209
  class DynamicModuleSecret(BaseModel):
210
210
  """Secret model (empty for this example)."""
211
211
 
212
- pass
213
-
214
212
 
215
213
  # =============================================================================
216
214
  # Module Implementation
@@ -285,13 +283,7 @@ class DynamicSetupModule(
285
283
 
286
284
  async def demonstrate_dynamic_schema() -> None:
287
285
  """Demonstrate the dynamic schema functionality."""
288
- print("=" * 60)
289
- print("Dynamic Schema Demonstration")
290
- print("=" * 60)
291
-
292
286
  # 1. Show schema WITHOUT force (dynamic fields not resolved)
293
- print("\n1. Schema without force=True (fetchers NOT called):")
294
- print("-" * 40)
295
287
 
296
288
  model_no_force = await DynamicAgentSetup.get_clean_model(
297
289
  config_fields=True,
@@ -302,13 +294,10 @@ async def demonstrate_dynamic_schema() -> None:
302
294
 
303
295
  # Check if enum is present
304
296
  model_name_schema = schema_no_force.get("properties", {}).get("model_name", {})
305
- print(f"model_name has enum: {'enum' in model_name_schema}")
306
297
  if "enum" in model_name_schema:
307
- print(f" enum values: {model_name_schema['enum']}")
298
+ pass
308
299
 
309
300
  # 2. Show schema WITH force (dynamic fields resolved)
310
- print("\n2. Schema with force=True (fetchers called):")
311
- print("-" * 40)
312
301
 
313
302
  model_with_force = await DynamicAgentSetup.get_clean_model(
314
303
  config_fields=True,
@@ -319,43 +308,30 @@ async def demonstrate_dynamic_schema() -> None:
319
308
 
320
309
  # Check enum values after force
321
310
  model_name_schema = schema_with_force.get("properties", {}).get("model_name", {})
322
- print(f"model_name has enum: {'enum' in model_name_schema}")
323
311
  if "enum" in model_name_schema:
324
- print(f" enum values: {model_name_schema['enum']}")
312
+ pass
325
313
 
326
314
  language_schema = schema_with_force.get("properties", {}).get("language", {})
327
- print(f"language has enum: {'enum' in language_schema}")
328
315
  if "enum" in language_schema:
329
- print(f" enum values: {language_schema['enum']}")
316
+ pass
330
317
 
331
318
  # 3. Show that static json_schema_extra is preserved
332
- print("\n3. Static json_schema_extra preserved:")
333
- print("-" * 40)
334
- print(f"model_name ui:widget: {model_name_schema.get('ui:widget', 'NOT FOUND')}")
335
319
 
336
320
  # 4. Show field filtering
337
- print("\n4. Field filtering demonstration:")
338
- print("-" * 40)
339
321
 
340
322
  # Config fields only (hidden excluded)
341
- config_model = await DynamicAgentSetup.get_clean_model(
323
+ await DynamicAgentSetup.get_clean_model(
342
324
  config_fields=True,
343
325
  hidden_fields=False,
344
326
  force=False,
345
327
  )
346
- print(f"Config fields (hidden=False): {list(config_model.model_fields.keys())}")
347
328
 
348
329
  # All fields including hidden
349
- all_model = await DynamicAgentSetup.get_clean_model(
330
+ await DynamicAgentSetup.get_clean_model(
350
331
  config_fields=True,
351
332
  hidden_fields=True,
352
333
  force=False,
353
334
  )
354
- print(f"All fields (hidden=True): {list(all_model.model_fields.keys())}")
355
-
356
- print("\n" + "=" * 60)
357
- print("Demonstration complete!")
358
- print("=" * 60)
359
335
 
360
336
 
361
337
  if __name__ == "__main__":
@@ -3,16 +3,16 @@
3
3
  import asyncio
4
4
  import datetime
5
5
  from collections.abc import Callable
6
- from typing import TYPE_CHECKING, Any
6
+ from typing import Any
7
7
 
8
8
  from pydantic import BaseModel, Field
9
9
 
10
10
  from digitalkin.logger import logger
11
11
  from digitalkin.models.module import ModuleStatus
12
12
  from digitalkin.modules.archetype_module import ArchetypeModule
13
+ from digitalkin.services.filesystem.filesystem_strategy import FileFilter, UploadFileData
13
14
  from digitalkin.services.services_config import ServicesConfig
14
15
  from digitalkin.services.services_models import ServicesMode
15
- from digitalkin.services.filesystem.filesystem_strategy import FilesystemRecord, FileFilter, UploadFileData
16
16
 
17
17
 
18
18
  class ExampleInput(BaseModel):
@@ -62,7 +62,10 @@ class ExampleModule(ArchetypeModule[ExampleInput, ExampleOutput, ExampleSetup, E
62
62
 
63
63
  # Define services_config_params with default values
64
64
  services_config_strategies = {}
65
- services_config_params = {"cost": {"config": {}}, "storage": {"config": {}}} # Filesystem has no config but it's enabled
65
+ services_config_params = {
66
+ "cost": {"config": {}},
67
+ "storage": {"config": {}},
68
+ } # Filesystem has no config but it's enabled
66
69
 
67
70
  def __init__(self, job_id: str, mission_id: str, setup_version_id: str) -> None:
68
71
  """Initialize the example module.
@@ -128,7 +131,6 @@ class ExampleModule(ArchetypeModule[ExampleInput, ExampleOutput, ExampleSetup, E
128
131
  callback(record.model_dump())
129
132
  # Call the callback with the output data
130
133
 
131
-
132
134
  # Wait a bit to simulate processing time
133
135
  await asyncio.sleep(1)
134
136
 
@@ -173,7 +175,7 @@ async def test_module() -> None:
173
175
 
174
176
  # Check the storage
175
177
  if module.status == ModuleStatus.STOPPED:
176
- files, nb_results = module.filesystem.get_files(
178
+ files, _nb_results = module.filesystem.get_files(
177
179
  filters=FileFilter(name="example_output.txt", context="test-mission-123"),
178
180
  )
179
181
  for file in files:
@@ -64,7 +64,7 @@ class ExampleModule(ArchetypeModule[ExampleInput, ExampleOutput, ExampleSetup, E
64
64
 
65
65
  # Define services_config_params with default values
66
66
  services_config_strategies = {}
67
- services_config_params = {"storage": {"config": {"example": ExampleOutput}},"cost": {"config":{}}}
67
+ services_config_params = {"storage": {"config": {"example": ExampleOutput}}, "cost": {"config": {}}}
68
68
 
69
69
  def __init__(self, job_id: str, mission_id: str, setup_version_id: str) -> None:
70
70
  """Initialize the example module.
@@ -184,7 +184,9 @@ async def test_module() -> None:
184
184
  def test_storage_directly() -> None:
185
185
  """Test the storage service directly."""
186
186
  # Initialize storage service
187
- storage = ServicesConfig().storage(mission_id="test-mission",setup_version_id="test-setup-123", config={"example": ExampleStorage})
187
+ storage = ServicesConfig().storage(
188
+ mission_id="test-mission", setup_version_id="test-setup-123", config={"example": ExampleStorage}
189
+ )
188
190
 
189
191
  # Create a test record
190
192
  storage.store("example", "test_table", {"test_key": "test_value"}, "OUTPUT")
@@ -12,7 +12,7 @@
12
12
 
13
13
  keywords = [ "digitalkin", "kin", "agent", "gprc", "sdk" ]
14
14
 
15
- version = "0.3.1.dev2"
15
+ version = "0.3.2.dev2"
16
16
  classifiers = [
17
17
  "Development Status :: 3 - Alpha",
18
18
  "Intended Audience :: Developers",
@@ -28,20 +28,20 @@
28
28
  ]
29
29
 
30
30
  dependencies = [
31
- "digitalkin-proto==0.2.0.dev5",
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",
35
35
  "pydantic>=2.12.5",
36
- "surrealdb>=1.0.6",
36
+ "surrealdb>=1.0.7",
37
37
  ]
38
38
 
39
39
  [project.optional-dependencies]
40
40
  taskiq = [
41
41
  "rstream>=0.40.0",
42
42
  "taskiq-aio-pika>=0.5.0",
43
- "taskiq-redis>=1.1.2",
44
- "taskiq[reload]>=0.12.0",
43
+ "taskiq-redis>=1.2.0",
44
+ "taskiq[reload]>=0.12.1",
45
45
  ]
46
46
 
47
47
  [project.urls]
@@ -56,7 +56,7 @@
56
56
  [dependency-groups]
57
57
  dev = [
58
58
  "typos>=1.40.0",
59
- "ruff>=0.14.7",
59
+ "ruff>=0.14.8",
60
60
  "mypy>=1.19.0",
61
61
  "pyright>=1.1.407",
62
62
  "pre-commit>=4.5.0",
@@ -70,7 +70,7 @@
70
70
  "hdrhistogram>=0.10.3",
71
71
  "grpcio-testing>=1.76.0",
72
72
  "psutil>=7.1.3",
73
- "pytest>=9.0.1",
73
+ "pytest>=9.0.2",
74
74
  "pytest-asyncio>=1.3.0",
75
75
  "pytest-cov>=7.0.0",
76
76
  "pytest-html==4.1.1",
@@ -97,11 +97,10 @@
97
97
  "mkdocs-material[imaging]>=9.7.0",
98
98
  "mkdocs-minify-plugin>=0.8.0",
99
99
  "mkdocs-section-index>=0.3.10",
100
- "mkdocstrings-python>=2.0.0",
100
+ "mkdocstrings-python>=2.0.1",
101
101
  "mkdocs-open-in-new-tab>=1.0.8",
102
102
  "tomli>=2.3.0",
103
103
  ]
104
-
105
104
  [tool.setuptools]
106
105
  package-dir = { "" = "src" }
107
106
 
@@ -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.1.dev2"
8
+ __version__ = "0.3.2.dev2"
@@ -8,8 +8,8 @@ from typing import Any, Generic
8
8
  from digitalkin.core.task_manager.base_task_manager import BaseTaskManager
9
9
  from digitalkin.core.task_manager.task_session import TaskSession
10
10
  from digitalkin.models.core.task_monitor import TaskStatus
11
- from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
12
11
  from digitalkin.models.module.module import ModuleCodeModel
12
+ from digitalkin.models.module.module_types import InputModelT, OutputModelT, SetupModelT
13
13
  from digitalkin.modules._base_module import BaseModule
14
14
  from digitalkin.services.services_config import ServicesConfig
15
15
  from digitalkin.services.services_models import ServicesMode
@@ -15,8 +15,8 @@ from digitalkin.core.task_manager.local_task_manager import LocalTaskManager
15
15
  from digitalkin.core.task_manager.task_session import TaskSession
16
16
  from digitalkin.logger import logger
17
17
  from digitalkin.models.core.task_monitor import TaskStatus
18
- from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
19
18
  from digitalkin.models.module.module import ModuleCodeModel
19
+ from digitalkin.models.module.module_types import InputModelT, OutputModelT, SetupModelT
20
20
  from digitalkin.modules._base_module import BaseModule
21
21
  from digitalkin.services.services_models import ServicesMode
22
22
 
@@ -21,8 +21,9 @@ from digitalkin.core.job_manager.base_job_manager import BaseJobManager
21
21
  from digitalkin.core.task_manager.task_executor import TaskExecutor
22
22
  from digitalkin.core.task_manager.task_session import TaskSession
23
23
  from digitalkin.logger import logger
24
- from digitalkin.models.core.job_manager_models import StreamCodeModel
25
- from digitalkin.models.module.module_types import OutputModelT
24
+ from digitalkin.models.module.module import ModuleCodeModel
25
+ from digitalkin.models.module.module_types import DataModel, OutputModelT
26
+ from digitalkin.models.module.utility import EndOfStreamOutput
26
27
  from digitalkin.modules._base_module import BaseModule
27
28
  from digitalkin.services.services_config import ServicesConfig
28
29
  from digitalkin.services.services_models import ServicesMode
@@ -141,7 +142,7 @@ async def cleanup_global_resources() -> None:
141
142
  logger.warning("Failed to shutdown Taskiq broker: %s", e)
142
143
 
143
144
 
144
- async def send_message_to_stream(job_id: str, output_data: OutputModelT) -> None: # type: ignore
145
+ async def send_message_to_stream(job_id: str, output_data: OutputModelT | ModuleCodeModel) -> None: # type: ignore[type-var]
145
146
  """Callback define to add a message frame to the Rstream.
146
147
 
147
148
  Args:
@@ -186,7 +187,7 @@ async def run_start_module(
186
187
  module_class.discover()
187
188
 
188
189
  job_id = context.message.task_id
189
- callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id)
190
+ callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id) # type: ignore[type-var]
190
191
  module = ModuleFactory.create_module_instance(module_class, job_id, mission_id, setup_id, setup_version_id)
191
192
 
192
193
  channel = None
@@ -201,7 +202,7 @@ async def run_start_module(
201
202
  # Create a proper done callback that handles errors
202
203
  async def send_end_of_stream(_: Any) -> None: # noqa: ANN401
203
204
  try:
204
- await callback(StreamCodeModel(code="__END_OF_STREAM__"))
205
+ await callback(DataModel(root=EndOfStreamOutput()))
205
206
  except Exception as e:
206
207
  logger.error("Error sending end of stream: %s", e, exc_info=True)
207
208
 
@@ -272,7 +273,7 @@ async def run_config_module(
272
273
  logger.debug("Services config: %s | Module config: %s", services_config, module_class.services_config)
273
274
 
274
275
  job_id = context.message.task_id
275
- callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id)
276
+ callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id) # type: ignore[type-var]
276
277
  module = ModuleFactory.create_module_instance(module_class, job_id, mission_id, setup_id, setup_version_id)
277
278
 
278
279
  # Override environment variables temporarily to use manager's SurrealDB
@@ -24,7 +24,7 @@ from digitalkin.core.job_manager.taskiq_broker import STREAM, STREAM_RETENTION,
24
24
  from digitalkin.core.task_manager.remote_task_manager import RemoteTaskManager
25
25
  from digitalkin.logger import logger
26
26
  from digitalkin.models.core.task_monitor import TaskStatus
27
- from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
27
+ from digitalkin.models.module.module_types import InputModelT, OutputModelT, SetupModelT
28
28
  from digitalkin.modules._base_module import BaseModule
29
29
  from digitalkin.services.services_models import ServicesMode
30
30
 
@@ -4,7 +4,7 @@ import asyncio
4
4
  import datetime
5
5
  import os
6
6
  from collections.abc import AsyncGenerator
7
- from typing import Any, Generic, TypeVar
7
+ from typing import Any, Generic, TypeVar, cast
8
8
  from uuid import UUID
9
9
 
10
10
  from surrealdb import AsyncHttpSurrealConnection, AsyncSurreal, AsyncWsSurrealConnection, RecordID
@@ -91,7 +91,7 @@ class SurrealDBConnection(Generic[TSurreal]):
91
91
  logger.debug("Connecting to SurrealDB at %s", self.url)
92
92
  self.db = AsyncSurreal(self.url) # type: ignore
93
93
  await self.db.signin({"username": self.username, "password": self.password})
94
- await self.db.use(self.namespace, self.database)
94
+ await self.db.use(self.namespace, self.database) # type: ignore[arg-type]
95
95
  logger.debug("Successfully connected to SurrealDB")
96
96
 
97
97
  async def close(self) -> None:
@@ -112,7 +112,7 @@ class SurrealDBConnection(Generic[TSurreal]):
112
112
  # Process results and track failures
113
113
  failed_queries = []
114
114
  for live_id, result in zip(live_query_ids, results):
115
- if isinstance(result, (ConnectionError, TimeoutError, Exception)):
115
+ if isinstance(result, ConnectionError | TimeoutError | Exception):
116
116
  failed_queries.append((live_id, str(result)))
117
117
  else:
118
118
  self._live_queries.discard(live_id)
@@ -146,7 +146,7 @@ class SurrealDBConnection(Generic[TSurreal]):
146
146
  logger.debug("Creating record in %s with data: %s", table_name, data)
147
147
  result = await self.db.create(table_name, data)
148
148
  logger.debug("create result: %s", result)
149
- return result
149
+ return cast("list[dict[str, Any]] | dict[str, Any]", result)
150
150
 
151
151
  async def merge(
152
152
  self,
@@ -170,7 +170,7 @@ class SurrealDBConnection(Generic[TSurreal]):
170
170
  logger.debug("Updating record in %s with data: %s", record_id, data)
171
171
  result = await self.db.merge(record_id, data)
172
172
  logger.debug("update result: %s", result)
173
- return result
173
+ return cast("list[dict[str, Any]] | dict[str, Any]", result)
174
174
 
175
175
  async def update(
176
176
  self,
@@ -194,7 +194,7 @@ class SurrealDBConnection(Generic[TSurreal]):
194
194
  logger.debug("Updating record in %s with data: %s", record_id, data)
195
195
  result = await self.db.update(record_id, data)
196
196
  logger.debug("update result: %s", result)
197
- return result
197
+ return cast("list[dict[str, Any]] | dict[str, Any]", result)
198
198
 
199
199
  async def execute_query(self, query: str, params: dict[str, Any] | None = None) -> list[dict[str, Any]]:
200
200
  """Execute a custom SurrealQL query.
@@ -209,7 +209,7 @@ class SurrealDBConnection(Generic[TSurreal]):
209
209
  logger.debug("execute_query: %s with params: %s", query, params)
210
210
  result = await self.db.query(query, params or {})
211
211
  logger.debug("execute_query result: %s", result)
212
- return [result] if isinstance(result, dict) else result
212
+ return cast("list[dict[str, Any]]", [result] if isinstance(result, dict) else result)
213
213
 
214
214
  async def select_by_task_id(self, table: str, value: str) -> dict[str, Any]:
215
215
  """Fetch a record from a table by a unique field.