digitalkin 0.2.26__py3-none-any.whl → 0.3.0.dev1__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/module_server.py +27 -44
- digitalkin/grpc_servers/module_servicer.py +27 -22
- digitalkin/grpc_servers/utils/models.py +1 -1
- digitalkin/logger.py +1 -9
- 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 +6 -6
- 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 +47 -68
- 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 +228 -0
- digitalkin/modules/job_manager/task_manager.py +389 -0
- digitalkin/modules/job_manager/task_session.py +275 -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/filesystem/default_filesystem.py +0 -2
- digitalkin/services/storage/grpc_storage.py +1 -1
- {digitalkin-0.2.26.dist-info → digitalkin-0.3.0.dev1.dist-info}/METADATA +20 -19
- {digitalkin-0.2.26.dist-info → digitalkin-0.3.0.dev1.dist-info}/RECORD +38 -25
- {digitalkin-0.2.26.dist-info → digitalkin-0.3.0.dev1.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.26.dist-info → digitalkin-0.3.0.dev1.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.2.26.dist-info → digitalkin-0.3.0.dev1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Logger Mixin to ease and merge every logs."""
|
|
2
|
+
|
|
3
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LoggerMixin:
|
|
7
|
+
"""Mixin providing callback operations through the callbacks strategy.
|
|
8
|
+
|
|
9
|
+
This mixin wraps callback strategy calls to provide a cleaner API
|
|
10
|
+
for logging and messaging in trigger handlers.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def log_debug(context: ModuleContext, message: str) -> None:
|
|
15
|
+
"""Log debug message using the callbacks strategy.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
context: Module context containing the callbacks strategy
|
|
19
|
+
message: Debug message to log
|
|
20
|
+
*args: Positional arguments for message formatting
|
|
21
|
+
**kwargs: Keyword arguments for logger
|
|
22
|
+
"""
|
|
23
|
+
return context.callbacks.logger.debug(message)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def log_info(context: ModuleContext, message: str) -> None:
|
|
27
|
+
"""Log info message using the callbacks strategy.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
context: Module context containing the callbacks strategy
|
|
31
|
+
message: Info message to log
|
|
32
|
+
*args: Positional arguments for message formatting
|
|
33
|
+
**kwargs: Keyword arguments for logger
|
|
34
|
+
"""
|
|
35
|
+
return context.callbacks.logger.info(message)
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def log_warning(context: ModuleContext, message: str) -> None:
|
|
39
|
+
"""Log warning message using the callbacks strategy.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
context: Module context containing the callbacks strategy
|
|
43
|
+
message: Warning message to log
|
|
44
|
+
*args: Positional arguments for message formatting
|
|
45
|
+
**kwargs: Keyword arguments for logger
|
|
46
|
+
"""
|
|
47
|
+
return context.callbacks.logger.warning(message)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def log_error(context: ModuleContext, message: str) -> None:
|
|
51
|
+
"""Log error message using the callbacks strategy.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
context: Module context containing the callbacks strategy
|
|
55
|
+
message: Error message to log
|
|
56
|
+
*args: Positional arguments for message formatting
|
|
57
|
+
**kwargs: Keyword arguments for logger
|
|
58
|
+
"""
|
|
59
|
+
return context.callbacks.logger.error(message)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Storage Mixin to ease storage access in Triggers."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
6
|
+
from digitalkin.services.storage.storage_strategy import StorageRecord
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StorageMixin:
|
|
10
|
+
"""Mixin providing storage operations through the storage strategy.
|
|
11
|
+
|
|
12
|
+
This mixin wraps storage strategy calls to provide a cleaner API
|
|
13
|
+
for trigger handlers.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def store_storage(
|
|
18
|
+
context: ModuleContext,
|
|
19
|
+
collection: str,
|
|
20
|
+
record_id: str | None,
|
|
21
|
+
data: dict[str, Any],
|
|
22
|
+
data_type: Literal["OUTPUT", "VIEW", "LOGS", "OTHER"] = "OUTPUT",
|
|
23
|
+
) -> StorageRecord:
|
|
24
|
+
"""Store data using the storage strategy.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
context: Module context containing the storage strategy
|
|
28
|
+
collection: Collection name for the data
|
|
29
|
+
record_id: Optional record identifier
|
|
30
|
+
data: Data to store
|
|
31
|
+
data_type: Type of data being stored
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Result from the storage strategy
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
StorageServiceError: If storage operation fails
|
|
38
|
+
"""
|
|
39
|
+
return context.storage.store(collection, record_id, data, data_type=data_type)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def read_storage(context: ModuleContext, collection: str, record_id: str) -> StorageRecord | None:
|
|
43
|
+
"""Read data from storage.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
context: Module context containing the storage strategy
|
|
47
|
+
collection: Collection name
|
|
48
|
+
record_id: Record identifier
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Retrieved data
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
StorageServiceError: If read operation fails
|
|
55
|
+
"""
|
|
56
|
+
return context.storage.read(collection, record_id)
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def update_storage(
|
|
60
|
+
context: ModuleContext,
|
|
61
|
+
collection: str,
|
|
62
|
+
record_id: str,
|
|
63
|
+
data: dict[str, Any],
|
|
64
|
+
) -> StorageRecord | None:
|
|
65
|
+
"""Update existing data in storage.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
context: Module context containing the storage strategy
|
|
69
|
+
collection: Collection name
|
|
70
|
+
record_id: Record identifier
|
|
71
|
+
data: Updated data
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Result from the storage strategy
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
StorageServiceError: If update operation fails
|
|
78
|
+
"""
|
|
79
|
+
return context.storage.update(collection, record_id, data)
|
|
@@ -5,6 +5,7 @@ from digitalkin.models.module.module_context import ModuleContext
|
|
|
5
5
|
from digitalkin.models.module.module_types import (
|
|
6
6
|
DataModel,
|
|
7
7
|
DataTrigger,
|
|
8
|
+
DataTriggerT,
|
|
8
9
|
InputModelT,
|
|
9
10
|
OutputModelT,
|
|
10
11
|
SecretModelT,
|
|
@@ -15,6 +16,7 @@ from digitalkin.models.module.module_types import (
|
|
|
15
16
|
__all__ = [
|
|
16
17
|
"DataModel",
|
|
17
18
|
"DataTrigger",
|
|
19
|
+
"DataTriggerT",
|
|
18
20
|
"InputModelT",
|
|
19
21
|
"Module",
|
|
20
22
|
"ModuleContext",
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from enum import Enum, auto
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ModuleCodeModel(BaseModel):
|
|
9
|
+
"""typed error/code model."""
|
|
10
|
+
|
|
11
|
+
code: str = Field(...)
|
|
12
|
+
message: str | None = Field(default=None)
|
|
13
|
+
short_description: str | None = Field(default=None)
|
|
6
14
|
|
|
7
15
|
|
|
8
16
|
class ModuleStatus(Enum):
|
|
@@ -1,24 +1,108 @@
|
|
|
1
1
|
"""Define the module context used in the triggers."""
|
|
2
2
|
|
|
3
3
|
from types import SimpleNamespace
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
6
|
+
from digitalkin.services.agent.agent_strategy import AgentStrategy
|
|
5
7
|
from digitalkin.services.cost.cost_strategy import CostStrategy
|
|
6
8
|
from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
|
|
9
|
+
from digitalkin.services.identity.identity_strategy import IdentityStrategy
|
|
10
|
+
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
11
|
+
from digitalkin.services.snapshot.snapshot_strategy import SnapshotStrategy
|
|
7
12
|
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
8
13
|
|
|
9
14
|
|
|
10
|
-
class
|
|
11
|
-
"""
|
|
15
|
+
class Session(SimpleNamespace):
|
|
16
|
+
"""Session data container with mandatory setup_id and mission_id."""
|
|
17
|
+
|
|
18
|
+
mission_id: str
|
|
19
|
+
setup_version_id: str
|
|
20
|
+
|
|
21
|
+
def __init__(self, mission_id: str, setup_version_id: str, **kwargs: dict[str, Any]) -> None:
|
|
22
|
+
"""Init Module Session.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
mission_id: current mission_id.
|
|
26
|
+
setup_version_id: used setup config.
|
|
27
|
+
kwargs: user defined session variables.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
ValueError: If mandatory args are missing
|
|
31
|
+
"""
|
|
32
|
+
if not setup_version_id:
|
|
33
|
+
msg = "setup_version_id is mandatory and cannot be empty"
|
|
34
|
+
raise ValueError(msg)
|
|
35
|
+
if not mission_id:
|
|
36
|
+
msg = "mission_id is mandatory and cannot be empty"
|
|
37
|
+
raise ValueError(msg)
|
|
38
|
+
|
|
39
|
+
self.mission_id = mission_id
|
|
40
|
+
self.setup_version_id = setup_version_id
|
|
41
|
+
|
|
42
|
+
super().__init__(**kwargs)
|
|
12
43
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
storage (StorageStrategy): The strategy for handling storage operations.
|
|
44
|
+
|
|
45
|
+
class ModuleContext:
|
|
46
|
+
"""ModuleContext provides a container for strategies and resources used by a module.
|
|
17
47
|
|
|
18
48
|
This context object is designed to be passed to module components, providing them with
|
|
19
49
|
access to shared strategies and resources. Additional attributes may be set dynamically.
|
|
20
50
|
"""
|
|
21
51
|
|
|
52
|
+
# services list
|
|
53
|
+
agent: AgentStrategy
|
|
22
54
|
cost: CostStrategy
|
|
23
55
|
filesystem: FilesystemStrategy
|
|
56
|
+
identity: IdentityStrategy
|
|
57
|
+
registry: RegistryStrategy
|
|
58
|
+
snapshot: SnapshotStrategy
|
|
24
59
|
storage: StorageStrategy
|
|
60
|
+
|
|
61
|
+
session: Session
|
|
62
|
+
callbacks: SimpleNamespace
|
|
63
|
+
metadata: SimpleNamespace
|
|
64
|
+
helpers: SimpleNamespace
|
|
65
|
+
state: SimpleNamespace = SimpleNamespace()
|
|
66
|
+
|
|
67
|
+
def __init__( # noqa: PLR0913, PLR0917
|
|
68
|
+
self,
|
|
69
|
+
agent: AgentStrategy,
|
|
70
|
+
cost: CostStrategy,
|
|
71
|
+
filesystem: FilesystemStrategy,
|
|
72
|
+
identity: IdentityStrategy,
|
|
73
|
+
registry: RegistryStrategy,
|
|
74
|
+
snapshot: SnapshotStrategy,
|
|
75
|
+
storage: StorageStrategy,
|
|
76
|
+
session: dict[str, Any],
|
|
77
|
+
metadata: dict[str, Any] = {},
|
|
78
|
+
helpers: dict[str, Any] = {},
|
|
79
|
+
callbacks: dict[str, Any] = {},
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Register mandatory services, session, metadata and callbacks.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
agent: AgentStrategy.
|
|
85
|
+
cost: CostStrategy.
|
|
86
|
+
filesystem: FilesystemStrategy.
|
|
87
|
+
identity: IdentityStrategy.
|
|
88
|
+
registry: RegistryStrategy.
|
|
89
|
+
snapshot: SnapshotStrategy.
|
|
90
|
+
storage: StorageStrategy.
|
|
91
|
+
metadata: dict defining differents Module metadata.
|
|
92
|
+
helpers: dict different user defined helpers.
|
|
93
|
+
session: dict referring the session IDs or informations.
|
|
94
|
+
callbacks: Functions allowing user to agent interaction.
|
|
95
|
+
"""
|
|
96
|
+
# Core services
|
|
97
|
+
self.agent = agent
|
|
98
|
+
self.cost = cost
|
|
99
|
+
self.filesystem = filesystem
|
|
100
|
+
self.identity = identity
|
|
101
|
+
self.registry = registry
|
|
102
|
+
self.snapshot = snapshot
|
|
103
|
+
self.storage = storage
|
|
104
|
+
|
|
105
|
+
self.metadata = SimpleNamespace(**metadata)
|
|
106
|
+
self.session = Session(**session)
|
|
107
|
+
self.helpers = SimpleNamespace(**helpers)
|
|
108
|
+
self.callbacks = SimpleNamespace(**callbacks)
|
|
@@ -9,9 +9,9 @@ from digitalkin.logger import logger
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class DataTrigger(BaseModel):
|
|
12
|
-
"""Defines the root input model exposing the protocol.
|
|
12
|
+
"""Defines the root input/output model exposing the protocol.
|
|
13
13
|
|
|
14
|
-
The mandatory protocol is important to define the module beahvior following the user or agent input.
|
|
14
|
+
The mandatory protocol is important to define the module beahvior following the user or agent input/output.
|
|
15
15
|
|
|
16
16
|
Example:
|
|
17
17
|
class MyInput(DataModel):
|
|
@@ -24,16 +24,16 @@ class DataTrigger(BaseModel):
|
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
26
|
protocol: ClassVar[str]
|
|
27
|
-
created_at: str =
|
|
27
|
+
created_at: str = datetime.now(tz=timezone.utc).isoformat()
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
DataTriggerT = TypeVar("DataTriggerT", bound=DataTrigger)
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class DataModel(BaseModel, Generic[DataTriggerT]):
|
|
34
|
-
"""Base definition of input model showing mandatory root fields.
|
|
34
|
+
"""Base definition of input/output model showing mandatory root fields.
|
|
35
35
|
|
|
36
|
-
The Model define the Module Input, usually referring to multiple input type defined by an union.
|
|
36
|
+
The Model define the Module Input/output, usually referring to multiple input/output type defined by an union.
|
|
37
37
|
|
|
38
38
|
Example:
|
|
39
39
|
class ModuleInput(DataModel):
|
|
@@ -102,4 +102,4 @@ class SetupModel(BaseModel):
|
|
|
102
102
|
__config__=ConfigDict(arbitrary_types_allowed=True),
|
|
103
103
|
**clean_fields,
|
|
104
104
|
)
|
|
105
|
-
return cast("type[SetupModelT]", m)
|
|
105
|
+
return cast("type[SetupModelT]", m) # type: ignore
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TaskStatus(Enum):
|
|
11
|
+
"""."""
|
|
12
|
+
|
|
13
|
+
PENDING = "pending"
|
|
14
|
+
RUNNING = "running"
|
|
15
|
+
CANCELLED = "cancelled"
|
|
16
|
+
COMPLETED = "completed"
|
|
17
|
+
FAILED = "failed"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SignalType(Enum):
|
|
21
|
+
"""."""
|
|
22
|
+
|
|
23
|
+
START = "start"
|
|
24
|
+
STOP = "stop"
|
|
25
|
+
CANCEL = "cancel"
|
|
26
|
+
PAUSE = "pause"
|
|
27
|
+
RESUME = "resume"
|
|
28
|
+
STATUS = "status"
|
|
29
|
+
|
|
30
|
+
ACK_CANCEL = "ack_cancel"
|
|
31
|
+
ACK_PAUSE = "ack_pause"
|
|
32
|
+
ACK_RESUME = "ack_resume"
|
|
33
|
+
ACK_STATUS = "ack_status"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SignalMessage(BaseModel):
|
|
37
|
+
"""."""
|
|
38
|
+
|
|
39
|
+
task_id: str = Field(..., description="Unique identifier for the task")
|
|
40
|
+
status: TaskStatus = Field(..., description="Current status of the task")
|
|
41
|
+
action: SignalType = Field(..., description="Type of signal action")
|
|
42
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
43
|
+
payload: dict[str, Any] = Field(default={}, description="Optional payload for the signal")
|
|
44
|
+
model_config = {"use_enum_values": True}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class HeartbeatMessage(BaseModel):
|
|
48
|
+
"""."""
|
|
49
|
+
|
|
50
|
+
task_id: str = Field(..., description="Unique identifier for the task")
|
|
51
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
"""Storage model."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
5
7
|
|
|
6
|
-
class StorageModel(BaseModel):
|
|
7
|
-
"""Storage model."""
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
class BaseRole(str, Enum):
|
|
10
|
+
"""Officially supported Role Enum for chat messages."""
|
|
11
|
+
|
|
12
|
+
ASSISTANT = "assistant"
|
|
13
|
+
HUMAN = "human"
|
|
14
|
+
SYSTEM = "system"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Role = BaseRole | str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseMessage(BaseModel):
|
|
21
|
+
"""Base Model representing a simple message in the chat history."""
|
|
22
|
+
|
|
23
|
+
role: Role = Field(..., description="Role of the message sender")
|
|
24
|
+
content: Any = Field(..., description="The content of the message | preferably a BaseModel.")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ChatHistory(BaseModel):
|
|
28
|
+
"""Storage chat history model for the OpenAI Archetype module."""
|
|
29
|
+
|
|
30
|
+
messages: list[BaseMessage] = Field(..., description="List of messages in the chat history")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FileModel(BaseModel):
|
|
34
|
+
"""File model."""
|
|
35
|
+
|
|
36
|
+
file_id: str = Field(..., description="ID of the file")
|
|
37
|
+
name: str = Field(..., description="Name of the file")
|
|
38
|
+
metadata: dict[str, Any] = Field(..., description="Metadata of the file")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class FileHistory(BaseModel):
|
|
42
|
+
"""File history model."""
|
|
43
|
+
|
|
44
|
+
files: list[FileModel] = Field(..., description="List of files")
|