digitalkin 0.2.25rc1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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/__version__.py +1 -1
- digitalkin/grpc_servers/_base_server.py +1 -1
- digitalkin/grpc_servers/module_server.py +26 -42
- digitalkin/grpc_servers/module_servicer.py +30 -24
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +3 -3
- digitalkin/grpc_servers/utils/models.py +1 -1
- digitalkin/logger.py +60 -23
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +108 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +99 -0
- digitalkin/mixins/filesystem_mixin.py +47 -0
- digitalkin/mixins/logger_mixin.py +59 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/module/__init__.py +2 -0
- digitalkin/models/module/module.py +9 -1
- digitalkin/models/module/module_context.py +90 -6
- digitalkin/models/module/module_types.py +5 -5
- digitalkin/models/module/task_monitor.py +51 -0
- digitalkin/models/services/__init__.py +9 -0
- digitalkin/models/services/storage.py +39 -5
- digitalkin/modules/_base_module.py +105 -74
- digitalkin/modules/job_manager/base_job_manager.py +12 -8
- digitalkin/modules/job_manager/single_job_manager.py +84 -78
- digitalkin/modules/job_manager/surrealdb_repository.py +225 -0
- digitalkin/modules/job_manager/task_manager.py +391 -0
- digitalkin/modules/job_manager/task_session.py +276 -0
- digitalkin/modules/job_manager/taskiq_job_manager.py +2 -2
- digitalkin/modules/tool_module.py +10 -2
- digitalkin/modules/trigger_handler.py +7 -6
- digitalkin/services/cost/__init__.py +9 -2
- digitalkin/services/storage/grpc_storage.py +1 -1
- {digitalkin-0.2.25rc1.dist-info → digitalkin-0.3.0.dist-info}/METADATA +18 -18
- {digitalkin-0.2.25rc1.dist-info → digitalkin-0.3.0.dist-info}/RECORD +39 -26
- {digitalkin-0.2.25rc1.dist-info → digitalkin-0.3.0.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.25rc1.dist-info → digitalkin-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.2.25rc1.dist-info → digitalkin-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
"""BaseModule is the abstract base for all modules in the DigitalKin SDK."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import contextlib
|
|
5
4
|
import json
|
|
6
5
|
from abc import ABC, abstractmethod
|
|
7
6
|
from collections.abc import Callable, Coroutine
|
|
8
7
|
from typing import Any, ClassVar, Generic
|
|
9
8
|
|
|
10
|
-
from pydantic import BaseModel
|
|
11
|
-
|
|
12
9
|
from digitalkin.logger import logger
|
|
13
10
|
from digitalkin.models.module import (
|
|
14
11
|
InputModelT,
|
|
@@ -17,28 +14,14 @@ from digitalkin.models.module import (
|
|
|
17
14
|
SecretModelT,
|
|
18
15
|
SetupModelT,
|
|
19
16
|
)
|
|
17
|
+
from digitalkin.models.module.module import ModuleCodeModel
|
|
20
18
|
from digitalkin.models.module.module_context import ModuleContext
|
|
21
19
|
from digitalkin.modules.trigger_handler import TriggerHandler
|
|
22
|
-
from digitalkin.services.agent.agent_strategy import AgentStrategy
|
|
23
|
-
from digitalkin.services.cost.cost_strategy import CostStrategy
|
|
24
|
-
from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
|
|
25
|
-
from digitalkin.services.identity.identity_strategy import IdentityStrategy
|
|
26
|
-
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
27
20
|
from digitalkin.services.services_config import ServicesConfig, ServicesStrategy
|
|
28
|
-
from digitalkin.services.snapshot.snapshot_strategy import SnapshotStrategy
|
|
29
|
-
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
30
21
|
from digitalkin.utils.llm_ready_schema import llm_ready_schema
|
|
31
22
|
from digitalkin.utils.package_discover import ModuleDiscoverer
|
|
32
23
|
|
|
33
24
|
|
|
34
|
-
class ModuleCodeModel(BaseModel):
|
|
35
|
-
"""typed error/code model."""
|
|
36
|
-
|
|
37
|
-
code: str
|
|
38
|
-
message: str
|
|
39
|
-
short_description: str
|
|
40
|
-
|
|
41
|
-
|
|
42
25
|
class BaseModule( # noqa: PLR0904
|
|
43
26
|
ABC,
|
|
44
27
|
Generic[
|
|
@@ -67,33 +50,35 @@ class BaseModule( # noqa: PLR0904
|
|
|
67
50
|
services_config_params: ClassVar[dict[str, dict[str, Any | None] | None]]
|
|
68
51
|
services_config: ServicesConfig
|
|
69
52
|
|
|
70
|
-
# services list
|
|
71
|
-
agent: AgentStrategy
|
|
72
|
-
cost: CostStrategy
|
|
73
|
-
filesystem: FilesystemStrategy
|
|
74
|
-
identity: IdentityStrategy
|
|
75
|
-
registry: RegistryStrategy
|
|
76
|
-
snapshot: SnapshotStrategy
|
|
77
|
-
storage: StorageStrategy
|
|
78
|
-
|
|
79
53
|
# runtime params
|
|
80
54
|
job_id: str
|
|
81
55
|
mission_id: str
|
|
82
56
|
setup_id: str
|
|
83
57
|
setup_version_id: str
|
|
84
|
-
_status: ModuleStatus
|
|
85
|
-
_task: asyncio.Task | None
|
|
86
58
|
|
|
87
|
-
def _init_strategies(self) ->
|
|
88
|
-
"""Initialize the services configuration.
|
|
89
|
-
|
|
90
|
-
|
|
59
|
+
def _init_strategies(self) -> dict[str, Any]:
|
|
60
|
+
"""Initialize the services configuration.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
dict of services with name: Strategy
|
|
64
|
+
agent: AgentStrategy
|
|
65
|
+
cost: CostStrategy
|
|
66
|
+
filesystem: FilesystemStrategy
|
|
67
|
+
identity: IdentityStrategy
|
|
68
|
+
registry: RegistryStrategy
|
|
69
|
+
snapshot: SnapshotStrategy
|
|
70
|
+
storage: StorageStrategy
|
|
71
|
+
"""
|
|
72
|
+
logger.debug("Service initialisation: %s", self.services_config_strategies.keys())
|
|
73
|
+
return {
|
|
74
|
+
service_name: self.services_config.init_strategy(
|
|
91
75
|
service_name,
|
|
92
76
|
self.mission_id,
|
|
93
77
|
self.setup_id,
|
|
94
78
|
self.setup_version_id,
|
|
95
79
|
)
|
|
96
|
-
|
|
80
|
+
for service_name in self.services_config.valid_strategy_names()
|
|
81
|
+
}
|
|
97
82
|
|
|
98
83
|
def __init__(
|
|
99
84
|
self,
|
|
@@ -110,15 +95,16 @@ class BaseModule( # noqa: PLR0904
|
|
|
110
95
|
# SetupVersion reference needed for the precise Kin scope as the cost
|
|
111
96
|
self.setup_version_id: str = setup_version_id
|
|
112
97
|
self._status = ModuleStatus.CREATED
|
|
113
|
-
self._task: asyncio.Task | None = None
|
|
114
|
-
# Initialize services configuration
|
|
115
|
-
self._init_strategies()
|
|
116
98
|
|
|
117
99
|
# Initialize minimum context
|
|
118
100
|
self.context = ModuleContext(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
# Initialize services configuration
|
|
102
|
+
**self._init_strategies(),
|
|
103
|
+
session={
|
|
104
|
+
"mission_id": mission_id,
|
|
105
|
+
"setup_version_id": setup_version_id,
|
|
106
|
+
"job_id": job_id,
|
|
107
|
+
},
|
|
122
108
|
)
|
|
123
109
|
|
|
124
110
|
@property
|
|
@@ -307,7 +293,11 @@ class BaseModule( # noqa: PLR0904
|
|
|
307
293
|
"""
|
|
308
294
|
return cls.triggers_discoverer.register_trigger(handler_cls)
|
|
309
295
|
|
|
310
|
-
async def run_config_setup(
|
|
296
|
+
async def run_config_setup( # noqa: PLR6301
|
|
297
|
+
self,
|
|
298
|
+
context: ModuleContext, # noqa: ARG002
|
|
299
|
+
config_setup_data: SetupModelT,
|
|
300
|
+
) -> SetupModelT:
|
|
311
301
|
"""Run config setup the module.
|
|
312
302
|
|
|
313
303
|
The config setup is used to initialize the setup with configuration data.
|
|
@@ -323,7 +313,7 @@ class BaseModule( # noqa: PLR0904
|
|
|
323
313
|
return config_setup_data
|
|
324
314
|
|
|
325
315
|
@abstractmethod
|
|
326
|
-
async def initialize(self, setup_data: SetupModelT) -> None:
|
|
316
|
+
async def initialize(self, context: ModuleContext, setup_data: SetupModelT) -> None:
|
|
327
317
|
"""Initialize the module."""
|
|
328
318
|
raise NotImplementedError
|
|
329
319
|
|
|
@@ -331,7 +321,6 @@ class BaseModule( # noqa: PLR0904
|
|
|
331
321
|
self,
|
|
332
322
|
input_data: InputModelT,
|
|
333
323
|
setup_data: SetupModelT,
|
|
334
|
-
callback: Callable[[OutputModelT], Coroutine[Any, Any, None]],
|
|
335
324
|
) -> None:
|
|
336
325
|
"""Run the module with the given input and setup data.
|
|
337
326
|
|
|
@@ -360,7 +349,6 @@ class BaseModule( # noqa: PLR0904
|
|
|
360
349
|
await handler_instance.handle(
|
|
361
350
|
input_instance.root,
|
|
362
351
|
setup_data,
|
|
363
|
-
callback,
|
|
364
352
|
self.context,
|
|
365
353
|
)
|
|
366
354
|
|
|
@@ -373,7 +361,6 @@ class BaseModule( # noqa: PLR0904
|
|
|
373
361
|
self,
|
|
374
362
|
input_data: InputModelT,
|
|
375
363
|
setup_data: SetupModelT,
|
|
376
|
-
callback: Callable[[OutputModelT], Coroutine[Any, Any, None]],
|
|
377
364
|
) -> None:
|
|
378
365
|
"""Run the module lifecycle.
|
|
379
366
|
|
|
@@ -381,19 +368,53 @@ class BaseModule( # noqa: PLR0904
|
|
|
381
368
|
asyncio.CancelledError: If the module is cancelled
|
|
382
369
|
"""
|
|
383
370
|
try:
|
|
384
|
-
logger.info(
|
|
385
|
-
|
|
386
|
-
|
|
371
|
+
logger.info(
|
|
372
|
+
"Starting module %s",
|
|
373
|
+
self.name,
|
|
374
|
+
extra={
|
|
375
|
+
"mission_id": self.mission_id,
|
|
376
|
+
"setup_id": self.setup_id,
|
|
377
|
+
"setup_version_id": self.setup_version_id,
|
|
378
|
+
"job_id": self.job_id,
|
|
379
|
+
},
|
|
380
|
+
)
|
|
381
|
+
await self.run(input_data, setup_data)
|
|
382
|
+
logger.info(
|
|
383
|
+
"Module %s finished",
|
|
384
|
+
self.name,
|
|
385
|
+
extra={
|
|
386
|
+
"mission_id": self.mission_id,
|
|
387
|
+
"setup_id": self.setup_id,
|
|
388
|
+
"setup_version_id": self.setup_version_id,
|
|
389
|
+
"job_id": self.job_id,
|
|
390
|
+
},
|
|
391
|
+
)
|
|
387
392
|
except asyncio.CancelledError:
|
|
388
393
|
self._status = ModuleStatus.CANCELLED
|
|
389
|
-
logger.error(
|
|
394
|
+
logger.error(
|
|
395
|
+
"Module %s cancelled",
|
|
396
|
+
self.name,
|
|
397
|
+
extra={
|
|
398
|
+
"mission_id": self.mission_id,
|
|
399
|
+
"setup_id": self.setup_id,
|
|
400
|
+
"setup_version_id": self.setup_version_id,
|
|
401
|
+
"job_id": self.job_id,
|
|
402
|
+
},
|
|
403
|
+
)
|
|
390
404
|
except Exception:
|
|
391
405
|
self._status = ModuleStatus.FAILED
|
|
392
|
-
logger.exception(
|
|
406
|
+
logger.exception(
|
|
407
|
+
"Error inside module %s",
|
|
408
|
+
self.name,
|
|
409
|
+
extra={
|
|
410
|
+
"mission_id": self.mission_id,
|
|
411
|
+
"setup_id": self.setup_id,
|
|
412
|
+
"setup_version_id": self.setup_version_id,
|
|
413
|
+
"job_id": self.job_id,
|
|
414
|
+
},
|
|
415
|
+
)
|
|
393
416
|
else:
|
|
394
417
|
self._status = ModuleStatus.STOPPING
|
|
395
|
-
finally:
|
|
396
|
-
await self.stop()
|
|
397
418
|
|
|
398
419
|
async def start(
|
|
399
420
|
self,
|
|
@@ -404,15 +425,17 @@ class BaseModule( # noqa: PLR0904
|
|
|
404
425
|
) -> None:
|
|
405
426
|
"""Start the module."""
|
|
406
427
|
try:
|
|
407
|
-
logger
|
|
408
|
-
|
|
428
|
+
self.context.callbacks.logger = logger
|
|
429
|
+
self.context.callbacks.send_message = callback
|
|
430
|
+
logger.info(f"Inititalize module {self.job_id}")
|
|
431
|
+
await self.initialize(self.context, setup_data)
|
|
409
432
|
except Exception as e:
|
|
410
433
|
self._status = ModuleStatus.FAILED
|
|
411
434
|
short_description = "Error initializing module"
|
|
412
435
|
logger.exception("%s: %s", short_description, e)
|
|
413
436
|
await callback(
|
|
414
437
|
ModuleCodeModel(
|
|
415
|
-
code=
|
|
438
|
+
code="Error",
|
|
416
439
|
short_description=short_description,
|
|
417
440
|
message=str(e),
|
|
418
441
|
)
|
|
@@ -425,32 +448,22 @@ class BaseModule( # noqa: PLR0904
|
|
|
425
448
|
try:
|
|
426
449
|
logger.debug("Init the discovered input handlers.")
|
|
427
450
|
self.triggers_discoverer.init_handlers(self.context)
|
|
428
|
-
logger.debug("Run lifecycle")
|
|
429
|
-
self.
|
|
430
|
-
self._task = asyncio.create_task(
|
|
431
|
-
self._run_lifecycle(input_data, setup_data, callback),
|
|
432
|
-
name="module_lifecycle",
|
|
433
|
-
)
|
|
434
|
-
if done_callback is not None:
|
|
435
|
-
self._task.add_done_callback(done_callback)
|
|
451
|
+
logger.debug(f"Run lifecycle {self.job_id}")
|
|
452
|
+
await self._run_lifecycle(input_data, setup_data)
|
|
436
453
|
except Exception:
|
|
437
454
|
self._status = ModuleStatus.FAILED
|
|
438
455
|
logger.exception("Error during module lifecyle")
|
|
456
|
+
finally:
|
|
457
|
+
await self.stop()
|
|
439
458
|
|
|
440
459
|
async def stop(self) -> None:
|
|
441
460
|
"""Stop the module."""
|
|
442
|
-
logger.info("Stopping module %s
|
|
443
|
-
if self._status not in {ModuleStatus.RUNNING, ModuleStatus.STOPPING}:
|
|
444
|
-
return
|
|
445
|
-
|
|
461
|
+
logger.info("Stopping module %s | job_id=%s", self.name, self.job_id)
|
|
446
462
|
try:
|
|
447
463
|
self._status = ModuleStatus.STOPPING
|
|
448
|
-
if self._task and not self._task.done():
|
|
449
|
-
self._task.cancel()
|
|
450
|
-
with contextlib.suppress(asyncio.CancelledError):
|
|
451
|
-
await self._task
|
|
452
464
|
logger.debug("Module %s stopped", self.name)
|
|
453
465
|
await self.cleanup()
|
|
466
|
+
await self.context.callbacks.send_message(ModuleCodeModel(code="__END_OF_STREAM__"))
|
|
454
467
|
self._status = ModuleStatus.STOPPED
|
|
455
468
|
logger.debug("Module %s cleaned", self.name)
|
|
456
469
|
except Exception:
|
|
@@ -464,14 +477,32 @@ class BaseModule( # noqa: PLR0904
|
|
|
464
477
|
) -> None:
|
|
465
478
|
"""Start the module."""
|
|
466
479
|
try:
|
|
467
|
-
logger.info(
|
|
480
|
+
logger.info(
|
|
481
|
+
"Run Config Setup lifecycle",
|
|
482
|
+
extra={
|
|
483
|
+
"mission_id": self.mission_id,
|
|
484
|
+
"setup_id": self.setup_id,
|
|
485
|
+
"setup_version_id": self.setup_version_id,
|
|
486
|
+
"job_id": self.job_id,
|
|
487
|
+
},
|
|
488
|
+
)
|
|
468
489
|
self._status = ModuleStatus.RUNNING
|
|
469
|
-
|
|
490
|
+
self.context.callbacks.set_config_setup = callback
|
|
491
|
+
content = await self.run_config_setup(self.context, config_setup_data)
|
|
470
492
|
|
|
471
493
|
wrapper = config_setup_data.model_dump()
|
|
472
494
|
wrapper["content"] = content.model_dump()
|
|
473
495
|
await callback(self.create_setup_model(wrapper))
|
|
474
496
|
self._status = ModuleStatus.STOPPING
|
|
475
497
|
except Exception:
|
|
498
|
+
logger.error("Error during module lifecyle")
|
|
476
499
|
self._status = ModuleStatus.FAILED
|
|
477
|
-
logger.exception(
|
|
500
|
+
logger.exception(
|
|
501
|
+
"Error during module lifecyle",
|
|
502
|
+
extra={
|
|
503
|
+
"mission_id": self.mission_id,
|
|
504
|
+
"setup_id": self.setup_id,
|
|
505
|
+
"setup_version_id": self.setup_version_id,
|
|
506
|
+
"job_id": self.job_id,
|
|
507
|
+
},
|
|
508
|
+
)
|
|
@@ -5,17 +5,18 @@ from collections.abc import AsyncGenerator, AsyncIterator, Callable, Coroutine
|
|
|
5
5
|
from contextlib import asynccontextmanager
|
|
6
6
|
from typing import Any, Generic
|
|
7
7
|
|
|
8
|
-
from digitalkin.models import ModuleStatus
|
|
9
8
|
from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
|
|
9
|
+
from digitalkin.models.module.module import ModuleCodeModel
|
|
10
|
+
from digitalkin.models.module.task_monitor import TaskStatus
|
|
10
11
|
from digitalkin.modules._base_module import BaseModule
|
|
11
12
|
from digitalkin.services.services_config import ServicesConfig
|
|
12
13
|
from digitalkin.services.services_models import ServicesMode
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
16
|
+
class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, OutputModelT]):
|
|
16
17
|
"""Abstract base class for managing background module jobs."""
|
|
17
18
|
|
|
18
|
-
async def
|
|
19
|
+
async def start(self) -> None:
|
|
19
20
|
"""Start the job manager.
|
|
20
21
|
|
|
21
22
|
This method initializes any necessary resources or configurations
|
|
@@ -24,8 +25,8 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
|
24
25
|
|
|
25
26
|
@staticmethod
|
|
26
27
|
async def job_specific_callback(
|
|
27
|
-
callback: Callable[[str, OutputModelT], Coroutine[Any, Any, None]], job_id: str
|
|
28
|
-
) -> Callable[[OutputModelT], Coroutine[Any, Any, None]]:
|
|
28
|
+
callback: Callable[[str, OutputModelT | ModuleCodeModel], Coroutine[Any, Any, None]], job_id: str
|
|
29
|
+
) -> Callable[[OutputModelT | ModuleCodeModel], Coroutine[Any, Any, None]]:
|
|
29
30
|
"""Generate a job-specific callback function.
|
|
30
31
|
|
|
31
32
|
Args:
|
|
@@ -36,7 +37,7 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
|
36
37
|
Callable: A wrapped callback function that includes the job ID.
|
|
37
38
|
"""
|
|
38
39
|
|
|
39
|
-
def callback_wrapper(output_data: OutputModelT) -> Coroutine[Any, Any, None]:
|
|
40
|
+
def callback_wrapper(output_data: OutputModelT | ModuleCodeModel) -> Coroutine[Any, Any, None]:
|
|
40
41
|
"""Wrapper for the callback function.
|
|
41
42
|
|
|
42
43
|
Args:
|
|
@@ -53,12 +54,14 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
|
53
54
|
self,
|
|
54
55
|
module_class: type[BaseModule],
|
|
55
56
|
services_mode: ServicesMode,
|
|
57
|
+
**kwargs, # noqa: ANN003
|
|
56
58
|
) -> None:
|
|
57
59
|
"""Initialize the job manager.
|
|
58
60
|
|
|
59
61
|
Args:
|
|
60
62
|
module_class: The class of the module to be managed.
|
|
61
63
|
services_mode: The mode of operation for the services (e.g., ASYNC or SYNC).
|
|
64
|
+
**kwargs: Additional keyword arguments for the job manager.
|
|
62
65
|
"""
|
|
63
66
|
self.module_class = module_class
|
|
64
67
|
|
|
@@ -68,6 +71,7 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
|
68
71
|
mode=services_mode,
|
|
69
72
|
)
|
|
70
73
|
setattr(self.module_class, "services_config", services_config)
|
|
74
|
+
super().__init__(**kwargs)
|
|
71
75
|
|
|
72
76
|
@abc.abstractmethod # type: ignore
|
|
73
77
|
@asynccontextmanager # type: ignore
|
|
@@ -156,14 +160,14 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
|
156
160
|
"""
|
|
157
161
|
|
|
158
162
|
@abc.abstractmethod
|
|
159
|
-
async def get_module_status(self, job_id: str) ->
|
|
163
|
+
async def get_module_status(self, job_id: str) -> TaskStatus:
|
|
160
164
|
"""Retrieve the status of a module job.
|
|
161
165
|
|
|
162
166
|
Args:
|
|
163
167
|
job_id: The unique identifier of the job.
|
|
164
168
|
|
|
165
169
|
Returns:
|
|
166
|
-
|
|
170
|
+
ModuleStatu: The status of the job.
|
|
167
171
|
"""
|
|
168
172
|
|
|
169
173
|
@abc.abstractmethod
|