digitalkin 0.2.21__py3-none-any.whl → 0.2.23__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 CHANGED
@@ -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.2.21"
8
+ __version__ = "0.2.23"
@@ -79,10 +79,10 @@ class ModuleServer(BaseServer):
79
79
 
80
80
  def start(self) -> None:
81
81
  """Start the module server and register with the registry if configured."""
82
- logger.info(self.server_config)
82
+ logger.info("Starting module server",extra={"server_config": self.server_config})
83
83
  super().start()
84
84
 
85
- logger.info(self.server_config)
85
+ logger.debug("Starting module server",extra={"server_config": self.server_config})
86
86
  # If a registry address is provided, register the module
87
87
  if self.server_config.registry_address:
88
88
  try:
@@ -91,17 +91,15 @@ class ModuleServer(BaseServer):
91
91
  logger.exception("Failed to register with registry")
92
92
 
93
93
  if self.module_servicer is not None:
94
- logger.info(
95
- "Setup post init started with config: %s", self.client_config
94
+ logger.debug(
95
+ "Setup post init started",extra={"client_config": self.client_config}
96
96
  )
97
97
  self.module_servicer.setup.__post_init__(self.client_config)
98
98
 
99
99
  async def start_async(self) -> None:
100
100
  """Start the module server and register with the registry if configured."""
101
- logger.info(self.server_config)
101
+ logger.info("Starting module server",extra={"server_config": self.server_config})
102
102
  await super().start_async()
103
-
104
- logger.info(self.server_config)
105
103
  # If a registry address is provided, register the module
106
104
  if self.server_config.registry_address:
107
105
  try:
@@ -111,7 +109,7 @@ class ModuleServer(BaseServer):
111
109
 
112
110
  if self.module_servicer is not None:
113
111
  logger.info(
114
- "Setup post init started with config: %s", self.client_config
112
+ "Setup post init started",extra={"client_config": self.client_config}
115
113
  )
116
114
  await self.module_servicer.job_manager._start()
117
115
  self.module_servicer.setup.__post_init__(self.client_config)
@@ -99,9 +99,14 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
99
99
  Raises:
100
100
  ServicerError: if the setup data is not returned or job creation fails.
101
101
  """
102
- logger.info("ConfigSetupVersion called for module: '%s'", self.module_class.__name__)
103
102
  logger.info(
104
- "Context : %s, setup_version: %s, mission_id: %s", context, request.setup_version, request.mission_id
103
+ "ConfigSetupVersion called for module: '%s'",
104
+ self.module_class.__name__,
105
+ extra={
106
+ "module_class": self.module_class,
107
+ "setup_version": request.setup_version,
108
+ "mission_id": request.mission_id,
109
+ },
105
110
  )
106
111
  # Process the module input
107
112
  # TODO: Secret should be used here as well
@@ -134,7 +139,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
134
139
  return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
135
140
 
136
141
  updated_setup_data = await self.job_manager.generate_config_setup_module_response(job_id)
137
- logger.warning(f"Updated setup data: {updated_setup_data=}")
142
+ logger.info("Setup updated")
143
+ logger.debug(f"Updated setup data: {updated_setup_data=}")
138
144
  setup_version.content = json_format.ParseDict(
139
145
  updated_setup_data,
140
146
  struct_pb2.Struct(),
@@ -159,8 +165,11 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
159
165
  Raises:
160
166
  ServicerError: the necessary query didn't work.
161
167
  """
162
- logger.info("StartModule called for module: '%s'", self.module_class.__name__)
163
- logger.info("Context : %s, setup_id: %s, mission_id: %s", context, request.setup_id, request.mission_id)
168
+ logger.info(
169
+ "StartModule called for module: '%s'",
170
+ self.module_class.__name__,
171
+ extra={"module_class": self.module_class, "setup_id": request.setup_id, "mission_id": request.mission_id},
172
+ )
164
173
  # Process the module input
165
174
  # TODO: Check failure of input data format
166
175
  input_data = self.module_class.create_input_model(dict(request.input.items()))
@@ -43,9 +43,9 @@ class GrpcClientWrapper:
43
43
  private_key=private_key,
44
44
  )
45
45
 
46
- return grpc.secure_channel(config.address, channel_credentials)
46
+ return grpc.secure_channel(config.address, channel_credentials, options=config.channel_options)
47
47
  # Insecure channel
48
- return grpc.insecure_channel(config.address)
48
+ return grpc.insecure_channel(config.address, options=config.channel_options)
49
49
 
50
50
  def exec_grpc_query(self, query_endpoint: str, request: Any) -> Any: # noqa: ANN401
51
51
  """Execute a gRPC query with from the query's rpc endpoint name.
@@ -65,8 +65,8 @@ class GrpcClientWrapper:
65
65
  logger.debug("send request to %s", query_endpoint)
66
66
  response = getattr(self.stub, query_endpoint)(request)
67
67
  logger.debug("receive response from request to registry: %s", response)
68
- except grpc.RpcError:
69
- logger.exception("RPC error during registration:")
68
+ except grpc.RpcError as e:
69
+ logger.exception("RPC error during %s: %s", query_endpoint, e.details())
70
70
  raise ServerError
71
71
  else:
72
72
  return response
@@ -169,9 +169,17 @@ class ClientConfig(ChannelConfig):
169
169
  mode: Client operation mode (sync/async)
170
170
  security: Security mode (secure/insecure)
171
171
  credentials: Client credentials for secure mode
172
+ channel_options: Additional channel options
172
173
  """
173
174
 
174
175
  credentials: ClientCredentials | None = Field(None, description="Client credentials for secure mode")
176
+ channel_options: list[tuple[str, Any]] = Field(
177
+ default_factory=lambda: [
178
+ ("grpc.max_receive_message_length", 50 * 1024 * 1024), # 50MB
179
+ ("grpc.max_send_message_length", 50 * 1024 * 1024), # 50MB
180
+ ],
181
+ description="Additional channel options",
182
+ )
175
183
 
176
184
  @field_validator("credentials")
177
185
  @classmethod
@@ -213,7 +221,13 @@ class ServerConfig(ChannelConfig):
213
221
 
214
222
  max_workers: int = Field(10, description="Maximum number of workers for sync mode")
215
223
  credentials: ServerCredentials | None = Field(None, description="Server credentials for secure mode")
216
- server_options: list[tuple[str, Any]] = Field(default_factory=list, description="Additional server options")
224
+ server_options: list[tuple[str, Any]] = Field(
225
+ default_factory=lambda: [
226
+ ("grpc.max_receive_message_length", 50 * 1024 * 1024), # 50MB
227
+ ("grpc.max_send_message_length", 50 * 1024 * 1024), # 50MB
228
+ ],
229
+ description="Additional server options",
230
+ )
217
231
  enable_reflection: bool = Field(default=True, description="Enable reflection for the server")
218
232
  enable_health_check: bool = Field(default=True, description="Enable health check service")
219
233
 
digitalkin/logger.py CHANGED
@@ -1,12 +1,15 @@
1
1
  """This module sets up a logger."""
2
2
 
3
+ import json
3
4
  import logging
5
+ import os
4
6
  import sys
5
- from typing import ClassVar
7
+ from datetime import datetime, timezone
8
+ from typing import Any, ClassVar
6
9
 
7
10
 
8
- class ColorFormatter(logging.Formatter):
9
- """Color formatter for logging."""
11
+ class ColorJSONFormatter(logging.Formatter):
12
+ """Color JSON formatter for development (pretty-printed with colors)."""
10
13
 
11
14
  grey = "\x1b[38;20m"
12
15
  green = "\x1b[32;20m"
@@ -15,28 +18,73 @@ class ColorFormatter(logging.Formatter):
15
18
  red = "\x1b[31;20m"
16
19
  bold_red = "\x1b[31;1m"
17
20
  reset = "\x1b[0m"
18
- format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)" # type: ignore
19
-
20
- FORMATS: ClassVar[dict[int, str]] = {
21
- logging.DEBUG: grey + format + reset + "\n", # type: ignore
22
- logging.INFO: green + format + reset + "\n", # type: ignore
23
- logging.WARNING: yellow + format + reset + "\n", # type: ignore
24
- logging.ERROR: red + format + reset + "\n", # type: ignore
25
- logging.CRITICAL: bold_red + format + reset + "\n", # type: ignore
21
+
22
+ COLORS: ClassVar[dict[int, str]] = {
23
+ logging.DEBUG: grey,
24
+ logging.INFO: green,
25
+ logging.WARNING: yellow,
26
+ logging.ERROR: red,
27
+ logging.CRITICAL: bold_red,
26
28
  }
27
29
 
28
- def format(self, record: logging.LogRecord) -> str: # type: ignore
29
- """Format the log record.
30
+ def format(self, record: logging.LogRecord) -> str:
31
+ """Format the log record as colored JSON for development.
30
32
 
31
33
  Args:
32
34
  record: The log record to format.
33
35
 
34
36
  Returns:
35
- str: The formatted log record.
37
+ str: The colored JSON formatted log record.
36
38
  """
37
- log_fmt = self.FORMATS.get(record.levelno)
38
- formatter = logging.Formatter(log_fmt)
39
- return formatter.format(record)
39
+ log_obj: dict[str, Any] = {
40
+ "timestamp": datetime.fromtimestamp(record.created, tz=timezone.utc).isoformat(),
41
+ "level": record.levelname.lower(),
42
+ "logger": record.name,
43
+ "message": record.getMessage(),
44
+ "location": f"{record.filename}:{record.lineno}",
45
+ "function": record.funcName,
46
+ }
47
+
48
+ # Add exception info if present
49
+ if record.exc_info:
50
+ log_obj["exception"] = self.formatException(record.exc_info)
51
+
52
+ # Add any extra fields
53
+ skip_attrs = {
54
+ "name",
55
+ "msg",
56
+ "args",
57
+ "created",
58
+ "filename",
59
+ "funcName",
60
+ "levelname",
61
+ "levelno",
62
+ "lineno",
63
+ "module",
64
+ "msecs",
65
+ "message",
66
+ "pathname",
67
+ "process",
68
+ "processName",
69
+ "relativeCreated",
70
+ "thread",
71
+ "threadName",
72
+ "exc_info",
73
+ "exc_text",
74
+ "stack_info",
75
+ }
76
+
77
+ extras = {key: value for key, value in record.__dict__.items() if key not in skip_attrs}
78
+
79
+ if extras:
80
+ log_obj["extra"] = extras
81
+
82
+ # Pretty print with color
83
+ color = self.COLORS.get(record.levelno, self.grey)
84
+ json_str = json.dumps(log_obj, indent=2, default=str)
85
+ if os.getenv("RAILWAY_SERVICE_NAME"):
86
+ json_str.replace("\\n", "\n")
87
+ return f"{color}{json_str}{self.reset}"
40
88
 
41
89
 
42
90
  logging.basicConfig(
@@ -54,8 +102,7 @@ logger = logging.getLogger("digitalkin")
54
102
  if not logger.handlers:
55
103
  ch = logging.StreamHandler()
56
104
  ch.setLevel(logging.INFO)
57
-
58
- ch.setFormatter(ColorFormatter())
105
+ ch.setFormatter(ColorJSONFormatter())
59
106
 
60
107
  logger.addHandler(ch)
61
108
  logger.propagate = False
@@ -327,9 +327,14 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
327
327
  Returns:
328
328
  tuple[list[FilesystemRecord], int]: List of files and total count
329
329
  """
330
+ match filters.context:
331
+ case "setup":
332
+ context_id = self.setup_id
333
+ case "mission":
334
+ context_id = self.mission_id
330
335
  with GrpcFilesystem._handle_grpc_errors("GetFiles"):
331
336
  request = filesystem_pb2.GetFilesRequest(
332
- context=filters.context,
337
+ context=context_id,
333
338
  filters=self._filter_to_proto(filters),
334
339
  include_content=include_content,
335
340
  list_size=list_size,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.2.21
3
+ Version: 0.2.23
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
@@ -7,19 +7,19 @@ base_server/mock/__init__.py,sha256=YZFT-F1l_TpvJYuIPX-7kTeE1CfOjhx9YmNRXVoi-jQ,
7
7
  base_server/mock/mock_pb2.py,sha256=sETakcS3PAAm4E-hTCV1jIVaQTPEAIoVVHupB8Z_k7Y,1843
8
8
  base_server/mock/mock_pb2_grpc.py,sha256=BbOT70H6q3laKgkHfOx1QdfmCS_HxCY4wCOX84YAdG4,3180
9
9
  digitalkin/__init__.py,sha256=7LLBAba0th-3SGqcpqFO-lopWdUkVLKzLZiMtB-mW3M,162
10
- digitalkin/__version__.py,sha256=QJb8Hs9hhnpQm5I47kYxQurSZN7wI4xdHPE8xSSPwIA,191
11
- digitalkin/logger.py,sha256=cFbIAZHOFx3nddOssRNYLXyqUPzR4CgDR_c-5wmB-og,1685
10
+ digitalkin/__version__.py,sha256=ztRCETPoZd6W_J-mnJ5nS74c-ka0qrOuTY9L88iKUGc,191
11
+ digitalkin/logger.py,sha256=I6ARTJ2oWJ97VqMT2oMb6JU2J2GKOZi9LZk7PUplh2E,2889
12
12
  digitalkin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  digitalkin/grpc_servers/__init__.py,sha256=0cJBlwipSmFdXkyH3T0i6OJ1WpAtNsZgYX7JaSnkbtg,804
14
14
  digitalkin/grpc_servers/_base_server.py,sha256=NXnnZPjJqUDWoumhEbb7EOEWB7d8XYwpQs-l97NTe4k,18647
15
- digitalkin/grpc_servers/module_server.py,sha256=hOvoY2XFjxmgkbAsMex5a-m7OPyljnz0Gh9BJxtDtJo,10259
16
- digitalkin/grpc_servers/module_servicer.py,sha256=dnEozIDwkAoinh3lIbT3ZJ9YANlghC3iiQKNYFNHmTI,18805
15
+ digitalkin/grpc_servers/module_server.py,sha256=81l1i32F7XGQDgBXjQ7Dpukh-KTpmHzM5PKka8LjviE,10386
16
+ digitalkin/grpc_servers/module_servicer.py,sha256=hhUAH6K5OcvkQEbXQUeUTAHJxkus-KlVwJnG1bGHTo4,18963
17
17
  digitalkin/grpc_servers/registry_server.py,sha256=StY18DKYoPKQIU1SIzgito6D4_QA1aMVddZ8O2WGlHY,2223
18
18
  digitalkin/grpc_servers/registry_servicer.py,sha256=dqsKGHZ0LnaIvGt4ipaAuigd37sbJBndT4MAT029GsY,16471
19
19
  digitalkin/grpc_servers/utils/exceptions.py,sha256=SyOgvjggaUECYmSiqy8KJLHwHVt5IClSTxslHM-IZzI,931
20
20
  digitalkin/grpc_servers/utils/factory.py,sha256=jm6rFjiqmtSv7BIHNAOxsG9xXtSvWpx9TfzSQiX97MQ,5899
21
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=GXRqCTCufwWd8sVMoClnl-JpeBjgmdlSCD0a3YMU9D0,2594
22
- digitalkin/grpc_servers/utils/models.py,sha256=80F5oHiv8MOqMoDZJBXmJSRoVYJRyhaVcijQ2LinAig,8428
21
+ digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=iPHwVAaGeuV0ZW7YhTc7E3SWPyWX244Cy0iFf2DSf_Q,2685
22
+ digitalkin/grpc_servers/utils/models.py,sha256=hiwiGHy3sEBBFdOUBCoC1n_7GVeJhjRmi0sl4tCEJy0,8965
23
23
  digitalkin/grpc_servers/utils/types.py,sha256=rQ78s4nAet2jy-NIDj_PUWriT0kuGHr_w6ELjmjgBao,539
24
24
  digitalkin/models/__init__.py,sha256=hDHtUfswaNh8wo4NZaBItg9JqC0uNSRqXArNWSrGynY,163
25
25
  digitalkin/models/module/__init__.py,sha256=fgTVbsNmLZgM43Vy-Ea5-g0EG3pncmAmJl9nk-OQdzo,561
@@ -53,7 +53,7 @@ digitalkin/services/cost/grpc_cost.py,sha256=cGtb0atPXSEEOrNIWee-o3ScfNRSAFXJGDu
53
53
  digitalkin/services/filesystem/__init__.py,sha256=BhwMl_BUvM0d65fmglkp0SVwn3RfYiUOKJgIMnOCaGM,381
54
54
  digitalkin/services/filesystem/default_filesystem.py,sha256=qfC4jorfo86fBBnth-4ft13VuaGdvIHgWOVurCykXIk,15182
55
55
  digitalkin/services/filesystem/filesystem_strategy.py,sha256=zibVLvX_IBQ-kgh-KYzHdszDeiHFPEAZszu_k99x1GQ,9487
56
- digitalkin/services/filesystem/grpc_filesystem.py,sha256=0Wd4ZSEPznbATUABJwhn44wCWMX066Glm_FtLYSk16s,12819
56
+ digitalkin/services/filesystem/grpc_filesystem.py,sha256=ilxTFjelmf8_bVSs3xLlsyWFHVlSJf27c4j8H7R1Rkw,12987
57
57
  digitalkin/services/identity/__init__.py,sha256=InkeyLgFYYwItx8mePA8HpfacOMWZwwuc0G4pWtKq9s,270
58
58
  digitalkin/services/identity/default_identity.py,sha256=Y2auZHrGSZTIN5D8HyjLvLcNbYFM1CNUE23x7p5VIGw,386
59
59
  digitalkin/services/identity/identity_strategy.py,sha256=skappBbds1_qa0Gr24FGrNX1N0_OYhYT1Lh7dUaAirE,429
@@ -76,14 +76,14 @@ digitalkin/utils/arg_parser.py,sha256=nvjI1pKDY1HfS0oGcMQPtdTQcggXLtpxXMbnMxNEKR
76
76
  digitalkin/utils/development_mode_action.py,sha256=TqRuAF_A7bDD4twRB4PnZcRoNeaiAnEdxM5kvy4aoaA,1511
77
77
  digitalkin/utils/llm_ready_schema.py,sha256=JjMug_lrQllqFoanaC091VgOqwAd-_YzcpqFlS7p778,2375
78
78
  digitalkin/utils/package_discover.py,sha256=3e9-6Vf3yAAv2VkHHVK4QVqHJBxQqg3d8uuDTsXph24,13471
79
- digitalkin-0.2.21.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
79
+ digitalkin-0.2.23.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
80
80
  modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  modules/cpu_intensive_module.py,sha256=ejB9XPnFfA0uCuFUQbM3fy5UYfqqAlF36rv_P5Ri8ho,8363
82
82
  modules/minimal_llm_module.py,sha256=Ijld__ZnhzfLwpXD1XVkLZ7jyKZKyOFZczOpiPttJZc,11216
83
83
  modules/text_transform_module.py,sha256=bwPSnEUthZQyfLwcTLo52iAxItAoknkLh8Y3m5aywaY,7251
84
84
  services/filesystem_module.py,sha256=71Mcja8jCQqiqFHPdsIXplFIHTvgkxRhp0TRXuCfgkk,7430
85
85
  services/storage_module.py,sha256=ybTMqmvGaTrR8PqJ4FU0cwxaDjT36TskVrGoetTGmno,6955
86
- digitalkin-0.2.21.dist-info/METADATA,sha256=JdeHnI5TK-xDUJe-Im3Zd8ILrSAkjlkljBCBz-2_ilA,30579
87
- digitalkin-0.2.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
88
- digitalkin-0.2.21.dist-info/top_level.txt,sha256=gcjqlyrZuLjIyxrOIavCQM_olpr6ND5kPKkZd2j0xGo,40
89
- digitalkin-0.2.21.dist-info/RECORD,,
86
+ digitalkin-0.2.23.dist-info/METADATA,sha256=6xGU63KlOxOodbF57XVFJGRE-2ufQiQISkzXaeuW_bw,30579
87
+ digitalkin-0.2.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
88
+ digitalkin-0.2.23.dist-info/top_level.txt,sha256=gcjqlyrZuLjIyxrOIavCQM_olpr6ND5kPKkZd2j0xGo,40
89
+ digitalkin-0.2.23.dist-info/RECORD,,