digitalhub 0.13.0b3__py3-none-any.whl → 0.14.9__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.
Files changed (139) hide show
  1. digitalhub/__init__.py +3 -8
  2. digitalhub/context/api.py +43 -6
  3. digitalhub/context/builder.py +1 -5
  4. digitalhub/context/context.py +28 -13
  5. digitalhub/entities/_base/_base/entity.py +0 -15
  6. digitalhub/entities/_base/context/entity.py +1 -4
  7. digitalhub/entities/_base/entity/builder.py +5 -5
  8. digitalhub/entities/_base/entity/entity.py +0 -8
  9. digitalhub/entities/_base/executable/entity.py +195 -87
  10. digitalhub/entities/_base/material/entity.py +11 -23
  11. digitalhub/entities/_base/material/utils.py +28 -4
  12. digitalhub/entities/_base/runtime_entity/builder.py +53 -18
  13. digitalhub/entities/_base/unversioned/entity.py +1 -1
  14. digitalhub/entities/_base/versioned/entity.py +1 -1
  15. digitalhub/entities/_commons/enums.py +1 -31
  16. digitalhub/entities/_commons/metrics.py +64 -30
  17. digitalhub/entities/_commons/utils.py +119 -30
  18. digitalhub/entities/_constructors/_resources.py +151 -0
  19. digitalhub/entities/{_base/entity/_constructors → _constructors}/name.py +18 -0
  20. digitalhub/entities/_processors/base/crud.py +381 -0
  21. digitalhub/entities/_processors/base/import_export.py +118 -0
  22. digitalhub/entities/_processors/base/processor.py +299 -0
  23. digitalhub/entities/_processors/base/special_ops.py +104 -0
  24. digitalhub/entities/_processors/context/crud.py +652 -0
  25. digitalhub/entities/_processors/context/import_export.py +242 -0
  26. digitalhub/entities/_processors/context/material.py +123 -0
  27. digitalhub/entities/_processors/context/processor.py +400 -0
  28. digitalhub/entities/_processors/context/special_ops.py +476 -0
  29. digitalhub/entities/_processors/processors.py +12 -0
  30. digitalhub/entities/_processors/utils.py +38 -102
  31. digitalhub/entities/artifact/crud.py +58 -22
  32. digitalhub/entities/artifact/utils.py +28 -13
  33. digitalhub/entities/builders.py +2 -0
  34. digitalhub/entities/dataitem/crud.py +63 -20
  35. digitalhub/entities/dataitem/table/entity.py +27 -22
  36. digitalhub/entities/dataitem/utils.py +82 -32
  37. digitalhub/entities/function/_base/entity.py +3 -6
  38. digitalhub/entities/function/crud.py +55 -24
  39. digitalhub/entities/model/_base/entity.py +62 -20
  40. digitalhub/entities/model/crud.py +59 -23
  41. digitalhub/entities/model/mlflow/utils.py +29 -20
  42. digitalhub/entities/model/utils.py +28 -13
  43. digitalhub/entities/project/_base/builder.py +0 -6
  44. digitalhub/entities/project/_base/entity.py +337 -164
  45. digitalhub/entities/project/_base/spec.py +4 -4
  46. digitalhub/entities/project/crud.py +28 -71
  47. digitalhub/entities/project/utils.py +7 -3
  48. digitalhub/entities/run/_base/builder.py +0 -4
  49. digitalhub/entities/run/_base/entity.py +70 -63
  50. digitalhub/entities/run/crud.py +79 -26
  51. digitalhub/entities/secret/_base/entity.py +1 -5
  52. digitalhub/entities/secret/crud.py +31 -28
  53. digitalhub/entities/task/_base/builder.py +0 -4
  54. digitalhub/entities/task/_base/entity.py +5 -5
  55. digitalhub/entities/task/_base/models.py +13 -16
  56. digitalhub/entities/task/crud.py +61 -29
  57. digitalhub/entities/trigger/_base/entity.py +1 -5
  58. digitalhub/entities/trigger/crud.py +89 -30
  59. digitalhub/entities/workflow/_base/entity.py +3 -8
  60. digitalhub/entities/workflow/crud.py +55 -24
  61. digitalhub/factory/entity.py +283 -0
  62. digitalhub/factory/enums.py +18 -0
  63. digitalhub/factory/registry.py +197 -0
  64. digitalhub/factory/runtime.py +44 -0
  65. digitalhub/factory/utils.py +3 -54
  66. digitalhub/runtimes/_base.py +2 -2
  67. digitalhub/stores/client/{dhcore/api_builder.py → api_builder.py} +3 -3
  68. digitalhub/stores/client/builder.py +19 -31
  69. digitalhub/stores/client/client.py +322 -0
  70. digitalhub/stores/client/configurator.py +408 -0
  71. digitalhub/stores/client/enums.py +50 -0
  72. digitalhub/stores/client/{dhcore/error_parser.py → error_parser.py} +0 -4
  73. digitalhub/stores/client/header_manager.py +61 -0
  74. digitalhub/stores/client/http_handler.py +152 -0
  75. digitalhub/stores/client/{_base/key_builder.py → key_builder.py} +14 -14
  76. digitalhub/stores/client/params_builder.py +330 -0
  77. digitalhub/stores/client/response_processor.py +102 -0
  78. digitalhub/stores/client/utils.py +35 -0
  79. digitalhub/stores/{credentials → configurator}/api.py +5 -9
  80. digitalhub/stores/configurator/configurator.py +123 -0
  81. digitalhub/stores/{credentials → configurator}/enums.py +27 -10
  82. digitalhub/stores/configurator/handler.py +213 -0
  83. digitalhub/stores/{credentials → configurator}/ini_module.py +31 -22
  84. digitalhub/stores/data/_base/store.py +0 -20
  85. digitalhub/stores/data/api.py +5 -7
  86. digitalhub/stores/data/builder.py +53 -27
  87. digitalhub/stores/data/local/store.py +0 -103
  88. digitalhub/stores/data/remote/store.py +0 -4
  89. digitalhub/stores/data/s3/configurator.py +39 -77
  90. digitalhub/stores/data/s3/store.py +57 -37
  91. digitalhub/stores/data/sql/configurator.py +66 -46
  92. digitalhub/stores/data/sql/store.py +171 -104
  93. digitalhub/stores/readers/data/factory.py +0 -8
  94. digitalhub/stores/readers/data/pandas/reader.py +9 -19
  95. digitalhub/utils/file_utils.py +0 -17
  96. digitalhub/utils/generic_utils.py +1 -14
  97. digitalhub/utils/git_utils.py +0 -8
  98. digitalhub/utils/io_utils.py +0 -12
  99. digitalhub/utils/store_utils.py +44 -0
  100. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/METADATA +5 -4
  101. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/RECORD +112 -113
  102. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/WHEEL +1 -1
  103. digitalhub/entities/_commons/types.py +0 -9
  104. digitalhub/entities/_processors/base.py +0 -531
  105. digitalhub/entities/_processors/context.py +0 -1299
  106. digitalhub/entities/task/_base/utils.py +0 -22
  107. digitalhub/factory/factory.py +0 -381
  108. digitalhub/stores/client/_base/api_builder.py +0 -34
  109. digitalhub/stores/client/_base/client.py +0 -243
  110. digitalhub/stores/client/_base/params_builder.py +0 -34
  111. digitalhub/stores/client/api.py +0 -36
  112. digitalhub/stores/client/dhcore/client.py +0 -613
  113. digitalhub/stores/client/dhcore/configurator.py +0 -675
  114. digitalhub/stores/client/dhcore/enums.py +0 -34
  115. digitalhub/stores/client/dhcore/key_builder.py +0 -62
  116. digitalhub/stores/client/dhcore/models.py +0 -40
  117. digitalhub/stores/client/dhcore/params_builder.py +0 -278
  118. digitalhub/stores/client/dhcore/utils.py +0 -94
  119. digitalhub/stores/client/local/api_builder.py +0 -116
  120. digitalhub/stores/client/local/client.py +0 -573
  121. digitalhub/stores/client/local/enums.py +0 -15
  122. digitalhub/stores/client/local/key_builder.py +0 -62
  123. digitalhub/stores/client/local/params_builder.py +0 -120
  124. digitalhub/stores/credentials/__init__.py +0 -3
  125. digitalhub/stores/credentials/configurator.py +0 -210
  126. digitalhub/stores/credentials/handler.py +0 -176
  127. digitalhub/stores/credentials/store.py +0 -81
  128. digitalhub/stores/data/enums.py +0 -15
  129. digitalhub/stores/data/s3/utils.py +0 -78
  130. /digitalhub/entities/{_base/entity/_constructors → _constructors}/__init__.py +0 -0
  131. /digitalhub/entities/{_base/entity/_constructors → _constructors}/metadata.py +0 -0
  132. /digitalhub/entities/{_base/entity/_constructors → _constructors}/spec.py +0 -0
  133. /digitalhub/entities/{_base/entity/_constructors → _constructors}/status.py +0 -0
  134. /digitalhub/entities/{_base/entity/_constructors → _constructors}/uuid.py +0 -0
  135. /digitalhub/{stores/client/_base → entities/_processors/base}/__init__.py +0 -0
  136. /digitalhub/{stores/client/dhcore → entities/_processors/context}/__init__.py +0 -0
  137. /digitalhub/stores/{client/local → configurator}/__init__.py +0 -0
  138. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/licenses/AUTHORS +0 -0
  139. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/licenses/LICENSE +0 -0
@@ -9,9 +9,9 @@ import typing
9
9
  from typing import Any
10
10
 
11
11
  from digitalhub.context.api import get_context
12
- from digitalhub.entities._base.entity._constructors.uuid import build_uuid
13
12
  from digitalhub.entities._base.material.utils import build_log_path_from_source, eval_local_source
14
13
  from digitalhub.entities._commons.enums import EntityKinds, EntityTypes
14
+ from digitalhub.entities._constructors.uuid import build_uuid
15
15
  from digitalhub.stores.data.api import get_store
16
16
  from digitalhub.stores.readers.data.api import get_reader_by_object
17
17
  from digitalhub.utils.enums import FileExtensions
@@ -33,16 +33,39 @@ def eval_source(
33
33
  project: str | None = None,
34
34
  ) -> Any:
35
35
  """
36
- Evaluate if source is local.
36
+ Evaluate and process data source for dataitem creation.
37
+
38
+ Determines the appropriate source handling based on whether a source
39
+ path or data object is provided. For table dataitems with data objects,
40
+ writes the data to a Parquet file and returns the file path.
37
41
 
38
42
  Parameters
39
43
  ----------
40
44
  source : SourcesOrListOfSources
41
- Source(s).
45
+ The source specification(s) for the dataitem. Can be file paths,
46
+ URLs, or other source identifiers.
47
+ data : Any
48
+ The data object to process (e.g., DataFrame). Alternative to source.
49
+ Exactly one of source or data must be provided.
50
+ kind : str
51
+ The kind of dataitem being created (e.g., 'table').
52
+ name : str
53
+ The name of the dataitem, used for generating file paths.
54
+ project : str
55
+ The project name, used for context and path generation.
42
56
 
43
57
  Returns
44
58
  -------
45
- None
59
+ Any
60
+ The processed source. Returns the original source if provided,
61
+ or the path to a generated file if data was processed.
62
+
63
+ Raises
64
+ ------
65
+ ValueError
66
+ If both source and data are provided or both are None.
67
+ NotImplementedError
68
+ If the specified kind is not supported for data processing.
46
69
  """
47
70
  if (source is None) == (data is None):
48
71
  raise ValueError("You must provide source or data.")
@@ -66,34 +89,46 @@ def eval_data(
66
89
  source: SourcesOrListOfSources,
67
90
  data: Any | None = None,
68
91
  file_format: str | None = None,
92
+ read_df_params: dict | None = None,
69
93
  engine: str | None = None,
70
94
  ) -> Any:
71
95
  """
72
- Evaluate data is loaded.
96
+ Evaluate and load data from source or return provided data.
97
+
98
+ For table dataitems, loads data from the source using the appropriate
99
+ store and reader. For other kinds, returns the data as-is.
73
100
 
74
101
  Parameters
75
102
  ----------
76
- project : str
77
- Project name.
78
- source : str
79
- Source(s).
103
+ kind : str
104
+ The kind of dataitem (e.g., 'table') that determines
105
+ how data should be processed.
106
+ source : SourcesOrListOfSources
107
+ The source specification(s) to load data from.
80
108
  data : Any
81
- Dataframe to log. Alternative to source.
109
+ Pre-loaded data object. If provided, may be returned directly
110
+ depending on the dataitem kind.
82
111
  file_format : str
83
- Extension of the file.
112
+ The file format specification for reading the source
113
+ (e.g., 'parquet', 'csv').
84
114
  engine : str
85
- Engine to use.
115
+ The engine to use for reading the data (e.g., 'pandas', 'polars').
86
116
 
87
117
  Returns
88
118
  -------
89
- None
119
+ Any
120
+ The loaded data object for table dataitems, or the original
121
+ data parameter for other kinds.
90
122
  """
91
123
  if kind == EntityKinds.DATAITEM_TABLE.value:
92
124
  if data is None:
125
+ if read_df_params is None:
126
+ read_df_params = {}
93
127
  return get_store(source).read_df(
94
128
  source,
95
129
  file_format=file_format,
96
130
  engine=engine,
131
+ **read_df_params,
97
132
  )
98
133
  return data
99
134
 
@@ -108,29 +143,37 @@ def process_kwargs(
108
143
  **kwargs,
109
144
  ) -> dict:
110
145
  """
111
- Process spec kwargs.
146
+ Process and enhance specification parameters for dataitem creation.
147
+
148
+ Processes the keyword arguments for dataitem specification, handling
149
+ schema extraction for table dataitems and path generation. Extracts
150
+ schema information from data objects when available.
112
151
 
113
152
  Parameters
114
153
  ----------
115
154
  project : str
116
- Project name.
155
+ The name of the project.
117
156
  name : str
118
- Object name.
157
+ The name of the dataitem entity.
119
158
  kind : str
120
- Kind the object.
159
+ The kind of dataitem being created (e.g., 'table').
121
160
  source : SourcesOrListOfSources
122
- Source(s).
161
+ The source specification(s) for the dataitem content.
123
162
  data : Any
124
- Dataframe to log. Alternative to source.
163
+ The data object for schema extraction and processing.
164
+ Used as an alternative to source for table dataitems.
125
165
  path : str
126
- Destination path of the entity. If not provided, it's generated.
166
+ The destination path for the dataitem entity.
167
+ If None, a path will be automatically generated.
127
168
  **kwargs : dict
128
- Spec parameters.
169
+ Additional specification parameters to be processed
170
+ and passed to the dataitem creation.
129
171
 
130
172
  Returns
131
173
  -------
132
174
  dict
133
- Kwargs updated.
175
+ The updated kwargs dictionary with processed path,
176
+ UUID, and schema information included.
134
177
  """
135
178
  if data is not None:
136
179
  if kind == EntityKinds.DATAITEM_TABLE.value:
@@ -147,16 +190,17 @@ def process_kwargs(
147
190
 
148
191
  def clean_tmp_path(pth: SourcesOrListOfSources) -> None:
149
192
  """
150
- Clean temporary path.
193
+ Clean up temporary files and directories.
194
+
195
+ Removes temporary files or directories created during dataitem
196
+ processing. Handles both single paths and lists of paths,
197
+ ignoring any errors that occur during cleanup.
151
198
 
152
199
  Parameters
153
200
  ----------
154
201
  pth : SourcesOrListOfSources
155
- Path to clean.
156
-
157
- Returns
158
- -------
159
- None
202
+ The path or list of paths to clean up. Can be file paths
203
+ or directory paths that need to be removed.
160
204
  """
161
205
  if isinstance(pth, list):
162
206
  for p in pth:
@@ -167,19 +211,25 @@ def clean_tmp_path(pth: SourcesOrListOfSources) -> None:
167
211
 
168
212
  def post_process(obj: Dataitem, data: Any) -> Dataitem:
169
213
  """
170
- Post process object.
214
+ Post-process dataitem object with additional metadata and previews.
215
+
216
+ Enhances the dataitem object with additional information extracted
217
+ from the data. For table dataitems, generates and stores a data
218
+ preview in the object's status.
171
219
 
172
220
  Parameters
173
221
  ----------
174
222
  obj : Dataitem
175
- The object.
223
+ The dataitem object to post-process and enhance.
176
224
  data : Any
177
- The data.
225
+ The data object used to generate previews and extract
226
+ additional metadata information.
178
227
 
179
228
  Returns
180
229
  -------
181
230
  Dataitem
182
- The object.
231
+ The enhanced dataitem object with updated status information
232
+ and saved changes.
183
233
  """
184
234
  if obj.kind == EntityKinds.DATAITEM_TABLE.value:
185
235
  reader = get_reader_by_object(data)
@@ -9,8 +9,7 @@ from concurrent.futures import ThreadPoolExecutor
9
9
 
10
10
  from digitalhub.entities._base.executable.entity import ExecutableEntity
11
11
  from digitalhub.entities._commons.enums import EntityTypes, Relationship
12
- from digitalhub.factory.factory import factory
13
- from digitalhub.utils.exceptions import BackendError
12
+ from digitalhub.factory.entity import entity_factory
14
13
 
15
14
  if typing.TYPE_CHECKING:
16
15
  from digitalhub.entities._base.entity.metadata import Metadata
@@ -76,8 +75,8 @@ class Function(ExecutableEntity):
76
75
  Run instance.
77
76
  """
78
77
  # Get task and run kind
79
- task_kind = factory.get_task_kind_from_action(self.kind, action)
80
- run_kind = factory.get_run_kind(self.kind)
78
+ task_kind = entity_factory.get_task_kind_from_action(self.kind, action)
79
+ run_kind = entity_factory.get_run_kind_from_action(self.kind, action)
81
80
 
82
81
  # Create or update new task
83
82
  task = self._get_or_create_task(task_kind)
@@ -91,8 +90,6 @@ class Function(ExecutableEntity):
91
90
 
92
91
  # If execution is done by DHCore backend, return the object
93
92
  if not local_execution:
94
- if self._context().local:
95
- raise BackendError("Cannot run remote function with local backend.")
96
93
  if wait:
97
94
  return run.wait(log_info=log_info)
98
95
  return run
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
  import typing
8
8
 
9
9
  from digitalhub.entities._commons.enums import EntityTypes
10
- from digitalhub.entities._processors.context import context_processor
10
+ from digitalhub.entities._processors.processors import context_processor
11
11
 
12
12
  if typing.TYPE_CHECKING:
13
13
  from digitalhub.entities.function._base.entity import Function
@@ -76,7 +76,6 @@ def get_function(
76
76
  identifier: str,
77
77
  project: str | None = None,
78
78
  entity_id: str | None = None,
79
- **kwargs,
80
79
  ) -> Function:
81
80
  """
82
81
  Get object from backend.
@@ -89,8 +88,6 @@ def get_function(
89
88
  Project name.
90
89
  entity_id : str
91
90
  Entity ID.
92
- **kwargs : dict
93
- Parameters to pass to the API call.
94
91
 
95
92
  Returns
96
93
  -------
@@ -108,18 +105,16 @@ def get_function(
108
105
  >>> entity_id="my-function-id")
109
106
  """
110
107
  return context_processor.read_context_entity(
111
- identifier,
108
+ identifier=identifier,
112
109
  entity_type=ENTITY_TYPE,
113
110
  project=project,
114
111
  entity_id=entity_id,
115
- **kwargs,
116
112
  )
117
113
 
118
114
 
119
115
  def get_function_versions(
120
116
  identifier: str,
121
117
  project: str | None = None,
122
- **kwargs,
123
118
  ) -> list[Function]:
124
119
  """
125
120
  Get object versions from backend.
@@ -130,8 +125,6 @@ def get_function_versions(
130
125
  Entity key (store://...) or entity name.
131
126
  project : str
132
127
  Project name.
133
- **kwargs : dict
134
- Parameters to pass to the API call.
135
128
 
136
129
  Returns
137
130
  -------
@@ -148,14 +141,23 @@ def get_function_versions(
148
141
  >>> project="my-project")
149
142
  """
150
143
  return context_processor.read_context_entity_versions(
151
- identifier,
144
+ identifier=identifier,
152
145
  entity_type=ENTITY_TYPE,
153
146
  project=project,
154
- **kwargs,
155
147
  )
156
148
 
157
149
 
158
- def list_functions(project: str, **kwargs) -> list[Function]:
150
+ def list_functions(
151
+ project: str,
152
+ q: str | None = None,
153
+ name: str | None = None,
154
+ kind: str | None = None,
155
+ user: str | None = None,
156
+ state: str | None = None,
157
+ created: str | None = None,
158
+ updated: str | None = None,
159
+ versions: str | None = None,
160
+ ) -> list[Function]:
159
161
  """
160
162
  List all latest version objects from backend.
161
163
 
@@ -163,8 +165,22 @@ def list_functions(project: str, **kwargs) -> list[Function]:
163
165
  ----------
164
166
  project : str
165
167
  Project name.
166
- **kwargs : dict
167
- Parameters to pass to the API call.
168
+ q : str
169
+ Query string to filter objects.
170
+ name : str
171
+ Object name.
172
+ kind : str
173
+ Kind of the object.
174
+ user : str
175
+ User that created the object.
176
+ state : str
177
+ Object state.
178
+ created : str
179
+ Creation date filter.
180
+ updated : str
181
+ Update date filter.
182
+ versions : str
183
+ Object version, default is latest.
168
184
 
169
185
  Returns
170
186
  -------
@@ -178,18 +194,36 @@ def list_functions(project: str, **kwargs) -> list[Function]:
178
194
  return context_processor.list_context_entities(
179
195
  project=project,
180
196
  entity_type=ENTITY_TYPE,
181
- **kwargs,
197
+ q=q,
198
+ name=name,
199
+ kind=kind,
200
+ user=user,
201
+ state=state,
202
+ created=created,
203
+ updated=updated,
204
+ versions=versions,
182
205
  )
183
206
 
184
207
 
185
- def import_function(file: str) -> Function:
208
+ def import_function(
209
+ file: str | None = None,
210
+ key: str | None = None,
211
+ reset_id: bool = False,
212
+ context: str | None = None,
213
+ ) -> Function:
186
214
  """
187
- Import object from a YAML file and create a new object into the backend.
215
+ Import an object from a YAML file or from a storage key.
188
216
 
189
217
  Parameters
190
218
  ----------
191
219
  file : str
192
- Path to YAML file.
220
+ Path to the YAML file.
221
+ key : str
222
+ Entity key (store://...).
223
+ reset_id : bool
224
+ Flag to determine if the ID of executable entities should be reset.
225
+ context : str
226
+ Project name to use for context resolution.
193
227
 
194
228
  Returns
195
229
  -------
@@ -200,7 +234,7 @@ def import_function(file: str) -> Function:
200
234
  --------
201
235
  >>> obj = import_function("my-function.yaml")
202
236
  """
203
- return context_processor.import_executable_entity(file)
237
+ return context_processor.import_executable_entity(file, key, reset_id, context)
204
238
 
205
239
 
206
240
  def load_function(file: str) -> Function:
@@ -256,7 +290,6 @@ def delete_function(
256
290
  entity_id: str | None = None,
257
291
  delete_all_versions: bool = False,
258
292
  cascade: bool = True,
259
- **kwargs,
260
293
  ) -> dict:
261
294
  """
262
295
  Delete object from backend.
@@ -270,11 +303,10 @@ def delete_function(
270
303
  entity_id : str
271
304
  Entity ID.
272
305
  delete_all_versions : bool
273
- Delete all versions of the named entity. If True, use entity name instead of entity key as identifier.
306
+ Delete all versions of the named entity.
307
+ If True, use entity name instead of entity key as identifier.
274
308
  cascade : bool
275
309
  Cascade delete.
276
- **kwargs : dict
277
- Parameters to pass to the API call.
278
310
 
279
311
  Returns
280
312
  -------
@@ -298,5 +330,4 @@ def delete_function(
298
330
  entity_id=entity_id,
299
331
  delete_all_versions=delete_all_versions,
300
332
  cascade=cascade,
301
- **kwargs,
302
333
  )
@@ -9,7 +9,7 @@ import typing
9
9
  from digitalhub.entities._base.material.entity import MaterialEntity
10
10
  from digitalhub.entities._commons.enums import EntityTypes
11
11
  from digitalhub.entities._commons.metrics import MetricType, set_metrics, validate_metric_value
12
- from digitalhub.entities._processors.context import context_processor
12
+ from digitalhub.entities._processors.processors import context_processor
13
13
 
14
14
  if typing.TYPE_CHECKING:
15
15
  from digitalhub.entities._base.entity.metadata import Metadata
@@ -81,10 +81,6 @@ class Model(MaterialEntity):
81
81
  single_value : bool
82
82
  If True, value is a single value.
83
83
 
84
- Returns
85
- -------
86
- None
87
-
88
84
  Examples
89
85
  --------
90
86
  Log a new value in a list
@@ -94,13 +90,27 @@ class Model(MaterialEntity):
94
90
  >>> entity.log_metric("loss", 0.0019)
95
91
 
96
92
  Log a list of values and append them to existing metric:
97
- >>> entity.log_metric("loss", [0.0018, 0.0015])
93
+ >>> entity.log_metric(
94
+ ... "loss",
95
+ ... [
96
+ ... 0.0018,
97
+ ... 0.0015,
98
+ ... ],
99
+ ... )
98
100
 
99
101
  Log a single value (not represented as list):
100
- >>> entity.log_metric("accuracy", 0.9, single_value=True)
102
+ >>> entity.log_metric(
103
+ ... "accuracy",
104
+ ... 0.9,
105
+ ... single_value=True,
106
+ ... )
101
107
 
102
108
  Log a list of values and overwrite existing metric:
103
- >>> entity.log_metric("accuracy", [0.8, 0.9], overwrite=True)
109
+ >>> entity.log_metric(
110
+ ... "accuracy",
111
+ ... [0.8, 0.9],
112
+ ... overwrite=True,
113
+ ... )
104
114
  """
105
115
  self._set_metrics(key, value, overwrite, single_value)
106
116
  context_processor.update_metric(self.project, self.ENTITY_TYPE, self.id, key, self.status.metrics[key])
@@ -121,19 +131,59 @@ class Model(MaterialEntity):
121
131
  overwrite : bool
122
132
  If True, overwrite existing metrics.
123
133
 
124
- Returns
125
- -------
126
- None
134
+ Examples
135
+ --------
136
+ Log multiple metrics at once
137
+ >>> entity.log_metrics(
138
+ ... {
139
+ ... "loss": 0.002,
140
+ ... "accuracy": 0.95,
141
+ ... }
142
+ ... )
143
+
144
+ Log metrics with lists and single values
145
+ >>> entity.log_metrics(
146
+ ... {
147
+ ... "loss": [
148
+ ... 0.1,
149
+ ... 0.05,
150
+ ... ],
151
+ ... "epoch": 10,
152
+ ... }
153
+ ... )
154
+
155
+ Append to existing metrics (default behavior)
156
+ >>> entity.log_metrics(
157
+ ... {
158
+ ... "loss": 0.001,
159
+ ... "accuracy": 0.96,
160
+ ... }
161
+ ... ) # Appends to existing
162
+
163
+ Overwrite existing metrics
164
+ >>> entity.log_metrics(
165
+ ... {
166
+ ... "loss": 0.0005,
167
+ ... "accuracy": 0.98,
168
+ ... },
169
+ ... overwrite=True,
170
+ ... )
127
171
 
128
172
  See also
129
173
  --------
130
174
  log_metric
131
175
  """
132
176
  for key, value in metrics.items():
177
+ # For lists, use log_metric which handles appending correctly
133
178
  if isinstance(value, list):
134
179
  self.log_metric(key, value, overwrite)
180
+
181
+ # For single values, check if we should append or create new
135
182
  else:
136
- self.log_metric(key, value, overwrite, single_value=True)
183
+ if not overwrite and key in self.status.metrics:
184
+ self.log_metric(key, value)
185
+ else:
186
+ self.log_metric(key, value, overwrite, single_value=True)
137
187
 
138
188
  ##############################
139
189
  # Helper methods
@@ -142,10 +192,6 @@ class Model(MaterialEntity):
142
192
  def _get_metrics(self) -> None:
143
193
  """
144
194
  Get model metrics from backend.
145
-
146
- Returns
147
- -------
148
- None
149
195
  """
150
196
  self.status.metrics = context_processor.read_metrics(
151
197
  project=self.project,
@@ -173,10 +219,6 @@ class Model(MaterialEntity):
173
219
  If True, overwrite existing metric.
174
220
  single_value : bool
175
221
  If True, value is a single value.
176
-
177
- Returns
178
- -------
179
- None
180
222
  """
181
223
  value = validate_metric_value(value)
182
224
  self.status.metrics = set_metrics(