digitalkin 0.3.1.dev0__tar.gz → 0.3.1.dev1__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.
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/PKG-INFO +3 -3
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/pyproject.toml +6 -6
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/__version__.py +1 -1
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/taskiq_job_manager.py +1 -2
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/base_task_manager.py +87 -12
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/task_executor.py +103 -27
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/task_session.py +63 -17
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/module_servicer.py +5 -1
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/core/task_monitor.py +17 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin.egg-info/PKG-INFO +3 -3
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin.egg-info/requires.txt +2 -2
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/LICENSE +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/README.md +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/mock/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/mock/mock_pb2.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/server_async_insecure.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/server_async_secure.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/server_sync_insecure.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/server_sync_secure.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/modules/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/modules/cpu_intensive_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/modules/minimal_llm_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/modules/text_transform_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/services/filesystem_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/services/storage_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/setup.cfg +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/common/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/common/factories.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/taskiq_broker.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/surrealdb_repository.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/_base_server.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/module_server.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/registry_server.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/registry_servicer.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/utils/exceptions.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/logger.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/base_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/callback_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/chat_history_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/cost_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/file_history_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/logger_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/mixins/storage_mixin.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/core/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/core/job_manager_models.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/grpc_servers/models.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/grpc_servers/types.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/module/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/module/module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/module/module_context.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/module/module_types.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/services/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/services/cost.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/services/storage.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/modules/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/modules/_base_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/modules/archetype_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/modules/tool_module.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/modules/trigger_handler.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/py.typed +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/agent/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/agent/agent_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/agent/default_agent.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/base_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/cost/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/cost/cost_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/cost/default_cost.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/cost/grpc_cost.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/filesystem/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/identity/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/identity/default_identity.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/identity/identity_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/registry/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/registry/default_registry.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/registry/registry_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/services_config.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/services_models.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/setup/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/setup/default_setup.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/setup/grpc_setup.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/setup/setup_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/snapshot/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/snapshot/default_snapshot.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/snapshot/snapshot_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/default_storage.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/grpc_storage.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/storage_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/user_profile/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/utils/__init__.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/utils/arg_parser.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/utils/development_mode_action.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/utils/llm_ready_schema.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/utils/package_discover.py +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin.egg-info/SOURCES.txt +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin.egg-info/dependency_links.txt +0 -0
- {digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/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.
|
|
3
|
+
Version: 0.3.1.dev1
|
|
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,7 +452,7 @@ 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
|
|
455
|
+
Requires-Dist: digitalkin-proto==0.2.0.dev4
|
|
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
|
|
@@ -460,7 +460,7 @@ Requires-Dist: pydantic>=2.12.4
|
|
|
460
460
|
Requires-Dist: surrealdb>=1.0.6
|
|
461
461
|
Provides-Extra: taskiq
|
|
462
462
|
Requires-Dist: rstream>=0.40.0; extra == "taskiq"
|
|
463
|
-
Requires-Dist: taskiq-aio-pika>=0.
|
|
463
|
+
Requires-Dist: taskiq-aio-pika>=0.5.0; extra == "taskiq"
|
|
464
464
|
Requires-Dist: taskiq-redis>=1.1.2; extra == "taskiq"
|
|
465
465
|
Requires-Dist: taskiq[reload]>=0.11.20; extra == "taskiq"
|
|
466
466
|
Dynamic: license-file
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
keywords = [ "digitalkin", "kin", "agent", "gprc", "sdk" ]
|
|
14
14
|
|
|
15
|
-
version = "0.3.1.
|
|
15
|
+
version = "0.3.1.dev1"
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Development Status :: 3 - Alpha",
|
|
18
18
|
"Intended Audience :: Developers",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
dependencies = [
|
|
31
|
-
"digitalkin-proto
|
|
31
|
+
"digitalkin-proto==0.2.0.dev4",
|
|
32
32
|
"grpcio-health-checking>=1.76.0",
|
|
33
33
|
"grpcio-reflection>=1.76.0",
|
|
34
34
|
"grpcio-status>=1.76.0",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
40
|
taskiq = [
|
|
41
41
|
"rstream>=0.40.0",
|
|
42
|
-
"taskiq-aio-pika>=0.
|
|
42
|
+
"taskiq-aio-pika>=0.5.0",
|
|
43
43
|
"taskiq-redis>=1.1.2",
|
|
44
44
|
"taskiq[reload]>=0.11.20",
|
|
45
45
|
]
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
[dependency-groups]
|
|
57
57
|
dev = [
|
|
58
58
|
"typos>=1.39.2",
|
|
59
|
-
"ruff>=0.14.
|
|
59
|
+
"ruff>=0.14.6",
|
|
60
60
|
"mypy>=1.18.2",
|
|
61
61
|
"pyright>=1.1.407",
|
|
62
|
-
"pre-commit>=4.
|
|
62
|
+
"pre-commit>=4.5.0",
|
|
63
63
|
"bump-my-version>=1.2.4",
|
|
64
64
|
"build>=1.3.0",
|
|
65
65
|
"twine>=6.2.0",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"markdown-exec>=1.12.1",
|
|
84
84
|
"mkdocs>=1.6",
|
|
85
85
|
"mkdocs-coverage>=2.0.0",
|
|
86
|
-
"mkdocs-llmstxt>=0.
|
|
86
|
+
"mkdocs-llmstxt>=0.5.0",
|
|
87
87
|
"mkdocs-redirects>=1.2",
|
|
88
88
|
"mkdocstrings>=0.29",
|
|
89
89
|
"griffe-inherited-docstrings>=1.1.2",
|
|
@@ -140,7 +140,7 @@ class TaskiqJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
|
|
|
140
140
|
services_mode: ServicesMode,
|
|
141
141
|
default_timeout: float = 10.0,
|
|
142
142
|
max_concurrent_tasks: int = 100,
|
|
143
|
-
stream_timeout: float =
|
|
143
|
+
stream_timeout: float = 30.0,
|
|
144
144
|
) -> None:
|
|
145
145
|
"""Initialize the Taskiq job manager.
|
|
146
146
|
|
|
@@ -298,7 +298,6 @@ class TaskiqJobManager(BaseJobManager[InputModelT, OutputModelT, SetupModelT]):
|
|
|
298
298
|
while True:
|
|
299
299
|
try:
|
|
300
300
|
# Block for first item with timeout to allow termination checks
|
|
301
|
-
# Configurable timeout (default 15s) to account for distributed system latencies
|
|
302
301
|
item = await asyncio.wait_for(queue.get(), timeout=self.stream_timeout)
|
|
303
302
|
queue.task_done()
|
|
304
303
|
yield item
|
|
@@ -11,6 +11,7 @@ from typing import Any
|
|
|
11
11
|
from digitalkin.core.task_manager.surrealdb_repository import SurrealDBConnection
|
|
12
12
|
from digitalkin.core.task_manager.task_session import TaskSession
|
|
13
13
|
from digitalkin.logger import logger
|
|
14
|
+
from digitalkin.models.core.task_monitor import CancellationReason
|
|
14
15
|
from digitalkin.modules._base_module import BaseModule
|
|
15
16
|
|
|
16
17
|
|
|
@@ -84,13 +85,32 @@ class BaseTaskManager(ABC):
|
|
|
84
85
|
task_id: The ID of the task to clean up
|
|
85
86
|
mission_id: The ID of the mission associated with the task
|
|
86
87
|
"""
|
|
88
|
+
session = self.tasks_sessions.get(task_id)
|
|
89
|
+
cancellation_reason = session.cancellation_reason.value if session else "no_session"
|
|
90
|
+
final_status = session.status.value if session else "unknown"
|
|
91
|
+
|
|
87
92
|
logger.debug(
|
|
88
|
-
"Cleaning up resources
|
|
93
|
+
"Cleaning up resources",
|
|
94
|
+
extra={
|
|
95
|
+
"mission_id": mission_id,
|
|
96
|
+
"task_id": task_id,
|
|
97
|
+
"final_status": final_status,
|
|
98
|
+
"cancellation_reason": cancellation_reason,
|
|
99
|
+
},
|
|
89
100
|
)
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
|
|
102
|
+
if session:
|
|
92
103
|
await session.cleanup()
|
|
93
104
|
self.tasks_sessions.pop(task_id, None)
|
|
105
|
+
logger.debug(
|
|
106
|
+
"Task session cleanup completed",
|
|
107
|
+
extra={
|
|
108
|
+
"mission_id": mission_id,
|
|
109
|
+
"task_id": task_id,
|
|
110
|
+
"final_status": final_status,
|
|
111
|
+
"cancellation_reason": cancellation_reason,
|
|
112
|
+
},
|
|
113
|
+
)
|
|
94
114
|
|
|
95
115
|
self.tasks.pop(task_id, None)
|
|
96
116
|
|
|
@@ -261,10 +281,21 @@ class BaseTaskManager(ABC):
|
|
|
261
281
|
)
|
|
262
282
|
|
|
263
283
|
except asyncio.TimeoutError:
|
|
284
|
+
# Set timeout as cancellation reason
|
|
285
|
+
if task_id in self.tasks_sessions:
|
|
286
|
+
session = self.tasks_sessions[task_id]
|
|
287
|
+
if session.cancellation_reason == CancellationReason.UNKNOWN:
|
|
288
|
+
session.cancellation_reason = CancellationReason.TIMEOUT
|
|
289
|
+
|
|
264
290
|
logger.warning(
|
|
265
291
|
"Graceful cancellation timed out for task: '%s', forcing cancellation",
|
|
266
292
|
task_id,
|
|
267
|
-
extra={
|
|
293
|
+
extra={
|
|
294
|
+
"mission_id": mission_id,
|
|
295
|
+
"task_id": task_id,
|
|
296
|
+
"timeout": timeout,
|
|
297
|
+
"cancellation_reason": CancellationReason.TIMEOUT.value,
|
|
298
|
+
},
|
|
268
299
|
)
|
|
269
300
|
|
|
270
301
|
# Phase 2: Force cancellation
|
|
@@ -272,8 +303,16 @@ class BaseTaskManager(ABC):
|
|
|
272
303
|
with contextlib.suppress(asyncio.CancelledError):
|
|
273
304
|
await task
|
|
274
305
|
|
|
275
|
-
logger.warning(
|
|
276
|
-
|
|
306
|
+
logger.warning(
|
|
307
|
+
"Task force-cancelled: '%s', reason: %s",
|
|
308
|
+
task_id,
|
|
309
|
+
CancellationReason.TIMEOUT.value,
|
|
310
|
+
extra={
|
|
311
|
+
"mission_id": mission_id,
|
|
312
|
+
"task_id": task_id,
|
|
313
|
+
"cancellation_reason": CancellationReason.TIMEOUT.value,
|
|
314
|
+
},
|
|
315
|
+
)
|
|
277
316
|
return True
|
|
278
317
|
|
|
279
318
|
except Exception as e:
|
|
@@ -283,10 +322,9 @@ class BaseTaskManager(ABC):
|
|
|
283
322
|
extra={"mission_id": mission_id, "task_id": task_id, "error": str(e)},
|
|
284
323
|
exc_info=True,
|
|
285
324
|
)
|
|
286
|
-
await self._cleanup_task(task_id, mission_id)
|
|
287
325
|
return False
|
|
288
|
-
|
|
289
|
-
|
|
326
|
+
finally:
|
|
327
|
+
await self._cleanup_task(task_id, mission_id)
|
|
290
328
|
return True
|
|
291
329
|
|
|
292
330
|
async def clean_session(self, task_id: str, mission_id: str) -> bool:
|
|
@@ -382,7 +420,16 @@ class BaseTaskManager(ABC):
|
|
|
382
420
|
results: dict[str, bool | BaseException] = {}
|
|
383
421
|
for task_id, result in zip(task_ids, results_list):
|
|
384
422
|
if isinstance(result, Exception):
|
|
385
|
-
logger.error(
|
|
423
|
+
logger.error(
|
|
424
|
+
"Exception cancelling task: '%s', error: %s",
|
|
425
|
+
task_id,
|
|
426
|
+
result,
|
|
427
|
+
extra={
|
|
428
|
+
"mission_id": mission_id,
|
|
429
|
+
"task_id": task_id,
|
|
430
|
+
"error": str(result),
|
|
431
|
+
},
|
|
432
|
+
)
|
|
386
433
|
results[task_id] = False
|
|
387
434
|
else:
|
|
388
435
|
results[task_id] = result
|
|
@@ -403,6 +450,21 @@ class BaseTaskManager(ABC):
|
|
|
403
450
|
)
|
|
404
451
|
|
|
405
452
|
self._shutdown_event.set()
|
|
453
|
+
|
|
454
|
+
# Mark all sessions with shutdown reason before cancellation
|
|
455
|
+
for task_id, session in self.tasks_sessions.items():
|
|
456
|
+
if session.cancellation_reason == CancellationReason.UNKNOWN:
|
|
457
|
+
session.cancellation_reason = CancellationReason.SHUTDOWN
|
|
458
|
+
logger.debug(
|
|
459
|
+
"Marking task for shutdown: '%s'",
|
|
460
|
+
task_id,
|
|
461
|
+
extra={
|
|
462
|
+
"mission_id": mission_id,
|
|
463
|
+
"task_id": task_id,
|
|
464
|
+
"cancellation_reason": CancellationReason.SHUTDOWN.value,
|
|
465
|
+
},
|
|
466
|
+
)
|
|
467
|
+
|
|
406
468
|
results = await self.cancel_all_tasks(mission_id, timeout)
|
|
407
469
|
|
|
408
470
|
failed_tasks = [task_id for task_id, success in results.items() if not success]
|
|
@@ -411,13 +473,26 @@ class BaseTaskManager(ABC):
|
|
|
411
473
|
"Failed to cancel %d tasks during shutdown: %s",
|
|
412
474
|
len(failed_tasks),
|
|
413
475
|
failed_tasks,
|
|
414
|
-
extra={
|
|
476
|
+
extra={
|
|
477
|
+
"mission_id": mission_id,
|
|
478
|
+
"failed_tasks": failed_tasks,
|
|
479
|
+
"failed_count": len(failed_tasks),
|
|
480
|
+
"cancellation_reason": CancellationReason.SHUTDOWN.value,
|
|
481
|
+
},
|
|
415
482
|
)
|
|
416
483
|
|
|
417
484
|
# Clean up any remaining sessions (in case cancellation didn't clean them)
|
|
418
485
|
remaining_sessions = list(self.tasks_sessions.keys())
|
|
419
486
|
if remaining_sessions:
|
|
420
|
-
logger.info(
|
|
487
|
+
logger.info(
|
|
488
|
+
"Cleaning up %d remaining task sessions after shutdown",
|
|
489
|
+
len(remaining_sessions),
|
|
490
|
+
extra={
|
|
491
|
+
"mission_id": mission_id,
|
|
492
|
+
"remaining_sessions": remaining_sessions,
|
|
493
|
+
"remaining_count": len(remaining_sessions),
|
|
494
|
+
},
|
|
495
|
+
)
|
|
421
496
|
cleanup_coros = [self._cleanup_task(task_id, mission_id) for task_id in remaining_sessions]
|
|
422
497
|
await asyncio.gather(*cleanup_coros, return_exceptions=True)
|
|
423
498
|
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/task_executor.py
RENAMED
|
@@ -8,7 +8,12 @@ from typing import Any
|
|
|
8
8
|
from digitalkin.core.task_manager.surrealdb_repository import SurrealDBConnection
|
|
9
9
|
from digitalkin.core.task_manager.task_session import TaskSession
|
|
10
10
|
from digitalkin.logger import logger
|
|
11
|
-
from digitalkin.models.core.task_monitor import
|
|
11
|
+
from digitalkin.models.core.task_monitor import (
|
|
12
|
+
CancellationReason,
|
|
13
|
+
SignalMessage,
|
|
14
|
+
SignalType,
|
|
15
|
+
TaskStatus,
|
|
16
|
+
)
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
class TaskExecutor:
|
|
@@ -82,7 +87,7 @@ class TaskExecutor:
|
|
|
82
87
|
finally:
|
|
83
88
|
logger.info("Heartbeat task ended", extra={"mission_id": mission_id, "task_id": task_id})
|
|
84
89
|
|
|
85
|
-
async def supervisor() -> None:
|
|
90
|
+
async def supervisor() -> None: # noqa: C901, PLR0912, PLR0915
|
|
86
91
|
"""Supervise the three concurrent tasks and handle outcomes.
|
|
87
92
|
|
|
88
93
|
Raises:
|
|
@@ -96,6 +101,7 @@ class TaskExecutor:
|
|
|
96
101
|
main_task = None
|
|
97
102
|
hb_task = None
|
|
98
103
|
sig_task = None
|
|
104
|
+
cleanup_reason = CancellationReason.UNKNOWN
|
|
99
105
|
|
|
100
106
|
try:
|
|
101
107
|
main_task = asyncio.create_task(coro, name=f"{task_id}_main")
|
|
@@ -106,12 +112,37 @@ class TaskExecutor:
|
|
|
106
112
|
return_when=asyncio.FIRST_COMPLETED,
|
|
107
113
|
)
|
|
108
114
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
# Determine cleanup reason based on which task completed first
|
|
116
|
+
completed = next(iter(done))
|
|
117
|
+
|
|
118
|
+
if completed is main_task:
|
|
119
|
+
# Main task finished - cleanup is due to success
|
|
120
|
+
cleanup_reason = CancellationReason.SUCCESS_CLEANUP
|
|
121
|
+
elif completed is sig_task or (completed is hb_task and sig_task.done()):
|
|
122
|
+
# Signal task finished - external cancellation
|
|
123
|
+
cleanup_reason = CancellationReason.SIGNAL
|
|
124
|
+
elif completed is hb_task:
|
|
125
|
+
# Heartbeat stopped - failure cleanup
|
|
126
|
+
cleanup_reason = CancellationReason.FAILURE_CLEANUP
|
|
127
|
+
|
|
128
|
+
# Cancel pending tasks with proper reason logging
|
|
129
|
+
if pending:
|
|
130
|
+
pending_names = [t.get_name() for t in pending]
|
|
131
|
+
logger.debug(
|
|
132
|
+
"Cancelling pending tasks: %s, reason: %s",
|
|
133
|
+
pending_names,
|
|
134
|
+
cleanup_reason.value,
|
|
135
|
+
extra={
|
|
136
|
+
"mission_id": mission_id,
|
|
137
|
+
"task_id": task_id,
|
|
138
|
+
"pending_tasks": pending_names,
|
|
139
|
+
"cancellation_reason": cleanup_reason.value,
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
for t in pending:
|
|
143
|
+
t.cancel()
|
|
112
144
|
|
|
113
145
|
# Propagate exception/result from the finished task
|
|
114
|
-
completed = next(iter(done))
|
|
115
146
|
await completed
|
|
116
147
|
|
|
117
148
|
# Determine final status based on which task completed
|
|
@@ -122,50 +153,95 @@ class TaskExecutor:
|
|
|
122
153
|
extra={"mission_id": mission_id, "task_id": task_id},
|
|
123
154
|
)
|
|
124
155
|
elif completed is sig_task or (completed is hb_task and sig_task.done()):
|
|
125
|
-
logger.debug(
|
|
126
|
-
f"Task cancelled due to signal {sig_task=}",
|
|
127
|
-
extra={"mission_id": mission_id, "task_id": task_id},
|
|
128
|
-
)
|
|
129
156
|
session.status = TaskStatus.CANCELLED
|
|
157
|
+
session.cancellation_reason = CancellationReason.SIGNAL
|
|
158
|
+
logger.info(
|
|
159
|
+
"Task cancelled via external signal",
|
|
160
|
+
extra={
|
|
161
|
+
"mission_id": mission_id,
|
|
162
|
+
"task_id": task_id,
|
|
163
|
+
"cancellation_reason": CancellationReason.SIGNAL.value,
|
|
164
|
+
},
|
|
165
|
+
)
|
|
130
166
|
elif completed is hb_task:
|
|
131
167
|
session.status = TaskStatus.FAILED
|
|
168
|
+
session.cancellation_reason = CancellationReason.HEARTBEAT_FAILURE
|
|
132
169
|
logger.error(
|
|
133
|
-
|
|
134
|
-
|
|
170
|
+
"Heartbeat stopped unexpectedly for task: '%s'",
|
|
171
|
+
task_id,
|
|
172
|
+
extra={
|
|
173
|
+
"mission_id": mission_id,
|
|
174
|
+
"task_id": task_id,
|
|
175
|
+
"cancellation_reason": CancellationReason.HEARTBEAT_FAILURE.value,
|
|
176
|
+
},
|
|
135
177
|
)
|
|
136
178
|
msg = f"Heartbeat stopped for {task_id}"
|
|
137
179
|
raise RuntimeError(msg) # noqa: TRY301
|
|
138
180
|
|
|
139
181
|
except asyncio.CancelledError:
|
|
140
182
|
session.status = TaskStatus.CANCELLED
|
|
141
|
-
|
|
183
|
+
# Only set reason if not already set (preserve original reason)
|
|
184
|
+
logger.info(
|
|
185
|
+
"Task cancelled externally: '%s', reason: %s",
|
|
186
|
+
task_id,
|
|
187
|
+
session.cancellation_reason.value,
|
|
188
|
+
extra={
|
|
189
|
+
"mission_id": mission_id,
|
|
190
|
+
"task_id": task_id,
|
|
191
|
+
"cancellation_reason": session.cancellation_reason.value,
|
|
192
|
+
},
|
|
193
|
+
)
|
|
194
|
+
cleanup_reason = CancellationReason.FAILURE_CLEANUP
|
|
142
195
|
raise
|
|
143
196
|
except Exception:
|
|
144
197
|
session.status = TaskStatus.FAILED
|
|
145
|
-
|
|
198
|
+
cleanup_reason = CancellationReason.FAILURE_CLEANUP
|
|
199
|
+
logger.exception(
|
|
200
|
+
"Task failed with exception: '%s'",
|
|
201
|
+
task_id,
|
|
202
|
+
extra={"mission_id": mission_id, "task_id": task_id},
|
|
203
|
+
)
|
|
146
204
|
raise
|
|
147
205
|
finally:
|
|
148
206
|
session.completed_at = datetime.datetime.now(datetime.timezone.utc)
|
|
149
|
-
# Ensure all tasks are cleaned up
|
|
150
|
-
tasks_to_cleanup = [t for t in [main_task, hb_task, sig_task] if t is not None]
|
|
151
|
-
for t in tasks_to_cleanup:
|
|
152
|
-
if not t.done():
|
|
153
|
-
t.cancel()
|
|
207
|
+
# Ensure all tasks are cleaned up with proper reason
|
|
208
|
+
tasks_to_cleanup = [t for t in [main_task, hb_task, sig_task] if t is not None and not t.done()]
|
|
154
209
|
if tasks_to_cleanup:
|
|
210
|
+
cleanup_names = [t.get_name() for t in tasks_to_cleanup]
|
|
211
|
+
logger.debug(
|
|
212
|
+
"Final cleanup of %d remaining tasks: %s, reason: %s",
|
|
213
|
+
len(tasks_to_cleanup),
|
|
214
|
+
cleanup_names,
|
|
215
|
+
cleanup_reason.value,
|
|
216
|
+
extra={
|
|
217
|
+
"mission_id": mission_id,
|
|
218
|
+
"task_id": task_id,
|
|
219
|
+
"cleanup_count": len(tasks_to_cleanup),
|
|
220
|
+
"cleanup_tasks": cleanup_names,
|
|
221
|
+
"cancellation_reason": cleanup_reason.value,
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
for t in tasks_to_cleanup:
|
|
225
|
+
t.cancel()
|
|
155
226
|
await asyncio.gather(*tasks_to_cleanup, return_exceptions=True)
|
|
156
227
|
|
|
228
|
+
duration = (
|
|
229
|
+
(session.completed_at - session.started_at).total_seconds()
|
|
230
|
+
if session.started_at and session.completed_at
|
|
231
|
+
else None
|
|
232
|
+
)
|
|
157
233
|
logger.info(
|
|
158
|
-
"Task execution completed
|
|
159
|
-
|
|
234
|
+
"Task execution completed: '%s', status: %s, reason: %s, duration: %.2fs",
|
|
235
|
+
task_id,
|
|
236
|
+
session.status.value,
|
|
237
|
+
session.cancellation_reason.value if session.status == TaskStatus.CANCELLED else "n/a",
|
|
238
|
+
duration or 0,
|
|
160
239
|
extra={
|
|
161
240
|
"mission_id": mission_id,
|
|
162
241
|
"task_id": task_id,
|
|
163
|
-
"status": session.status,
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
if session.started_at and session.completed_at
|
|
167
|
-
else None
|
|
168
|
-
),
|
|
242
|
+
"status": session.status.value,
|
|
243
|
+
"cancellation_reason": session.cancellation_reason.value,
|
|
244
|
+
"duration": duration,
|
|
169
245
|
},
|
|
170
246
|
)
|
|
171
247
|
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/task_session.py
RENAMED
|
@@ -6,7 +6,13 @@ from collections.abc import AsyncGenerator
|
|
|
6
6
|
|
|
7
7
|
from digitalkin.core.task_manager.surrealdb_repository import SurrealDBConnection
|
|
8
8
|
from digitalkin.logger import logger
|
|
9
|
-
from digitalkin.models.core.task_monitor import
|
|
9
|
+
from digitalkin.models.core.task_monitor import (
|
|
10
|
+
CancellationReason,
|
|
11
|
+
HeartbeatMessage,
|
|
12
|
+
SignalMessage,
|
|
13
|
+
SignalType,
|
|
14
|
+
TaskStatus,
|
|
15
|
+
)
|
|
10
16
|
from digitalkin.modules._base_module import BaseModule
|
|
11
17
|
|
|
12
18
|
|
|
@@ -31,6 +37,7 @@ class TaskSession:
|
|
|
31
37
|
completed_at: datetime.datetime | None
|
|
32
38
|
|
|
33
39
|
is_cancelled: asyncio.Event
|
|
40
|
+
cancellation_reason: CancellationReason
|
|
34
41
|
_paused: asyncio.Event
|
|
35
42
|
_heartbeat_interval: datetime.timedelta
|
|
36
43
|
_last_heartbeat: datetime.datetime
|
|
@@ -62,6 +69,7 @@ class TaskSession:
|
|
|
62
69
|
self.heartbeat_record_id = None
|
|
63
70
|
|
|
64
71
|
self.is_cancelled = asyncio.Event()
|
|
72
|
+
self.cancellation_reason = CancellationReason.UNKNOWN
|
|
65
73
|
self._paused = asyncio.Event()
|
|
66
74
|
self._heartbeat_interval = heartbeat_interval
|
|
67
75
|
|
|
@@ -143,17 +151,26 @@ class TaskSession:
|
|
|
143
151
|
|
|
144
152
|
async def generate_heartbeats(self) -> None:
|
|
145
153
|
"""Periodic heartbeat generator with cancellation support."""
|
|
146
|
-
logger.debug(
|
|
154
|
+
logger.debug(
|
|
155
|
+
"Heartbeat generator started for task: '%s'",
|
|
156
|
+
self.task_id,
|
|
157
|
+
extra={"task_id": self.task_id, "mission_id": self.mission_id},
|
|
158
|
+
)
|
|
147
159
|
while not self.cancelled:
|
|
148
|
-
logger.debug(
|
|
160
|
+
logger.debug(
|
|
161
|
+
"Heartbeat tick for task: '%s', cancelled=%s",
|
|
162
|
+
self.task_id,
|
|
163
|
+
self.cancelled,
|
|
164
|
+
extra={"task_id": self.task_id, "mission_id": self.mission_id},
|
|
165
|
+
)
|
|
149
166
|
success = await self.send_heartbeat()
|
|
150
167
|
if not success:
|
|
151
168
|
logger.error(
|
|
152
169
|
"Heartbeat failed, cancelling task: '%s'",
|
|
153
170
|
self.task_id,
|
|
154
|
-
extra={"task_id": self.task_id},
|
|
171
|
+
extra={"task_id": self.task_id, "mission_id": self.mission_id},
|
|
155
172
|
)
|
|
156
|
-
await self._handle_cancel()
|
|
173
|
+
await self._handle_cancel(CancellationReason.HEARTBEAT_FAILURE)
|
|
157
174
|
break
|
|
158
175
|
await asyncio.sleep(self._heartbeat_interval.total_seconds())
|
|
159
176
|
|
|
@@ -192,7 +209,7 @@ class TaskSession:
|
|
|
192
209
|
continue
|
|
193
210
|
|
|
194
211
|
if signal["action"] == "cancel":
|
|
195
|
-
await self._handle_cancel()
|
|
212
|
+
await self._handle_cancel(CancellationReason.SIGNAL)
|
|
196
213
|
elif signal["action"] == "pause":
|
|
197
214
|
await self._handle_pause()
|
|
198
215
|
elif signal["action"] == "resume":
|
|
@@ -222,26 +239,55 @@ class TaskSession:
|
|
|
222
239
|
extra={"task_id": self.task_id},
|
|
223
240
|
)
|
|
224
241
|
|
|
225
|
-
async def _handle_cancel(self) -> None:
|
|
226
|
-
"""Idempotent cancellation with acknowledgment.
|
|
227
|
-
|
|
242
|
+
async def _handle_cancel(self, reason: CancellationReason = CancellationReason.UNKNOWN) -> None:
|
|
243
|
+
"""Idempotent cancellation with acknowledgment and reason tracking.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
reason: The reason for cancellation (signal, heartbeat failure, cleanup, etc.)
|
|
247
|
+
"""
|
|
228
248
|
if self.is_cancelled.is_set():
|
|
229
249
|
logger.debug(
|
|
230
|
-
"Cancel
|
|
250
|
+
"Cancel ignored - task already cancelled: '%s' (existing reason: %s, new reason: %s)",
|
|
231
251
|
self.task_id,
|
|
232
|
-
|
|
252
|
+
self.cancellation_reason.value,
|
|
253
|
+
reason.value,
|
|
254
|
+
extra={
|
|
255
|
+
"task_id": self.task_id,
|
|
256
|
+
"mission_id": self.mission_id,
|
|
257
|
+
"existing_reason": self.cancellation_reason.value,
|
|
258
|
+
"new_reason": reason.value,
|
|
259
|
+
},
|
|
233
260
|
)
|
|
234
261
|
return
|
|
235
262
|
|
|
236
|
-
|
|
237
|
-
"Cancelling task: '%s'",
|
|
238
|
-
self.task_id,
|
|
239
|
-
extra={"task_id": self.task_id},
|
|
240
|
-
)
|
|
241
|
-
|
|
263
|
+
self.cancellation_reason = reason
|
|
242
264
|
self.status = TaskStatus.CANCELLED
|
|
243
265
|
self.is_cancelled.set()
|
|
244
266
|
|
|
267
|
+
# Log with appropriate level based on reason
|
|
268
|
+
if reason in {CancellationReason.SUCCESS_CLEANUP, CancellationReason.FAILURE_CLEANUP}:
|
|
269
|
+
logger.debug(
|
|
270
|
+
"Task cancelled (cleanup): '%s', reason: %s",
|
|
271
|
+
self.task_id,
|
|
272
|
+
reason.value,
|
|
273
|
+
extra={
|
|
274
|
+
"task_id": self.task_id,
|
|
275
|
+
"mission_id": self.mission_id,
|
|
276
|
+
"cancellation_reason": reason.value,
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
logger.info(
|
|
281
|
+
"Task cancelled: '%s', reason: %s",
|
|
282
|
+
self.task_id,
|
|
283
|
+
reason.value,
|
|
284
|
+
extra={
|
|
285
|
+
"task_id": self.task_id,
|
|
286
|
+
"mission_id": self.mission_id,
|
|
287
|
+
"cancellation_reason": reason.value,
|
|
288
|
+
},
|
|
289
|
+
)
|
|
290
|
+
|
|
245
291
|
# Resume if paused so cancellation can proceed
|
|
246
292
|
if self._paused.is_set():
|
|
247
293
|
self._paused.set()
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/module_servicer.py
RENAMED
|
@@ -220,9 +220,13 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
220
220
|
break
|
|
221
221
|
|
|
222
222
|
if message.get("code", None) is not None and message.get("code") == "__END_OF_STREAM__":
|
|
223
|
-
|
|
223
|
+
logger.info(
|
|
224
|
+
"End of stream via __END_OF_STREAM__",
|
|
225
|
+
extra={"job_id": job_id, "mission_id": request.mission_id},
|
|
226
|
+
)
|
|
224
227
|
break
|
|
225
228
|
|
|
229
|
+
logger.info("Yielding message from job %s: %s", job_id, message)
|
|
226
230
|
proto = json_format.ParseDict(message, struct_pb2.Struct(), ignore_unknown_fields=True)
|
|
227
231
|
yield lifecycle_pb2.StartModuleResponse(success=True, output=proto, job_id=job_id)
|
|
228
232
|
finally:
|
|
@@ -17,6 +17,23 @@ class TaskStatus(Enum):
|
|
|
17
17
|
FAILED = "failed"
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
class CancellationReason(Enum):
|
|
21
|
+
"""Reason for task cancellation - helps distinguish cleanup vs real cancellation."""
|
|
22
|
+
|
|
23
|
+
# Cleanup cancellations (not errors)
|
|
24
|
+
SUCCESS_CLEANUP = "success_cleanup" # Main task completed, cleaning up helper tasks
|
|
25
|
+
FAILURE_CLEANUP = "failure_cleanup" # Main task failed, cleaning up helper tasks
|
|
26
|
+
|
|
27
|
+
# Real cancellations
|
|
28
|
+
SIGNAL = "signal" # External signal requested cancellation
|
|
29
|
+
HEARTBEAT_FAILURE = "heartbeat_failure" # Heartbeat stopped working
|
|
30
|
+
TIMEOUT = "timeout" # Task timed out
|
|
31
|
+
SHUTDOWN = "shutdown" # Manager is shutting down
|
|
32
|
+
|
|
33
|
+
# Unknown/unset
|
|
34
|
+
UNKNOWN = "unknown" # Reason not determined
|
|
35
|
+
|
|
36
|
+
|
|
20
37
|
class SignalType(Enum):
|
|
21
38
|
"""Signal type enumeration."""
|
|
22
39
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: digitalkin
|
|
3
|
-
Version: 0.3.1.
|
|
3
|
+
Version: 0.3.1.dev1
|
|
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,7 +452,7 @@ 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
|
|
455
|
+
Requires-Dist: digitalkin-proto==0.2.0.dev4
|
|
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
|
|
@@ -460,7 +460,7 @@ Requires-Dist: pydantic>=2.12.4
|
|
|
460
460
|
Requires-Dist: surrealdb>=1.0.6
|
|
461
461
|
Provides-Extra: taskiq
|
|
462
462
|
Requires-Dist: rstream>=0.40.0; extra == "taskiq"
|
|
463
|
-
Requires-Dist: taskiq-aio-pika>=0.
|
|
463
|
+
Requires-Dist: taskiq-aio-pika>=0.5.0; extra == "taskiq"
|
|
464
464
|
Requires-Dist: taskiq-redis>=1.1.2; extra == "taskiq"
|
|
465
465
|
Requires-Dist: taskiq[reload]>=0.11.20; extra == "taskiq"
|
|
466
466
|
Dynamic: license-file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
digitalkin-proto
|
|
1
|
+
digitalkin-proto==0.2.0.dev4
|
|
2
2
|
grpcio-health-checking>=1.76.0
|
|
3
3
|
grpcio-reflection>=1.76.0
|
|
4
4
|
grpcio-status>=1.76.0
|
|
@@ -7,6 +7,6 @@ surrealdb>=1.0.6
|
|
|
7
7
|
|
|
8
8
|
[taskiq]
|
|
9
9
|
rstream>=0.40.0
|
|
10
|
-
taskiq-aio-pika>=0.
|
|
10
|
+
taskiq-aio-pika>=0.5.0
|
|
11
11
|
taskiq-redis>=1.1.2
|
|
12
12
|
taskiq[reload]>=0.11.20
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/server_async_insecure.py
RENAMED
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/examples/base_server/server_sync_insecure.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/base_job_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/job_manager/taskiq_broker.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/core/task_manager/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/module_server.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/registry_server.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/registry_servicer.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/utils/__init__.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/grpc_servers/utils/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/core/job_manager_models.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/grpc_servers/__init__.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/grpc_servers/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/module/module_context.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/models/module/module_types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/agent/agent_strategy.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/agent/default_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/cost/cost_strategy.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/cost/default_cost.py
RENAMED
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/filesystem/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/identity/__init__.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/identity/default_identity.py
RENAMED
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/registry/__init__.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/registry/default_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/setup/default_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/setup/setup_strategy.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/snapshot/__init__.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/snapshot/default_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/default_storage.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/grpc_storage.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/storage/storage_strategy.py
RENAMED
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/services/user_profile/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin/utils/development_mode_action.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{digitalkin-0.3.1.dev0 → digitalkin-0.3.1.dev1}/src/digitalkin.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|