supervisely 6.73.297__py3-none-any.whl → 6.73.299__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.

Potentially problematic release.


This version of supervisely might be problematic. Click here for more details.

Files changed (28) hide show
  1. supervisely/api/module_api.py +2 -0
  2. supervisely/api/task_api.py +61 -0
  3. supervisely/app/fastapi/index.html +1 -1
  4. supervisely/app/fastapi/templating.py +1 -1
  5. supervisely/app/widgets/__init__.py +2 -1
  6. supervisely/app/widgets/button/button.py +3 -1
  7. supervisely/app/widgets/button/template.html +4 -0
  8. supervisely/app/widgets/custom_models_selector/custom_models_selector.py +2 -2
  9. supervisely/app/widgets/experiment_selector/experiment_selector.py +2 -3
  10. supervisely/app/widgets/run_app_button/__init__.py +0 -0
  11. supervisely/app/widgets/run_app_button/run_app_button.py +300 -0
  12. supervisely/app/widgets/run_app_button/script.js +46 -0
  13. supervisely/app/widgets/run_app_button/template.html +10 -0
  14. supervisely/nn/artifacts/artifacts.py +3 -3
  15. supervisely/nn/benchmark/base_benchmark.py +14 -10
  16. supervisely/nn/experiments.py +42 -26
  17. supervisely/nn/training/gui/training_artifacts.py +13 -11
  18. supervisely/nn/training/gui/training_logs.py +39 -5
  19. supervisely/nn/training/gui/utils.py +11 -1
  20. supervisely/nn/training/train_app.py +70 -18
  21. supervisely/video_annotation/frame_collection.py +13 -4
  22. supervisely/video_annotation/video_annotation.py +8 -2
  23. {supervisely-6.73.297.dist-info → supervisely-6.73.299.dist-info}/METADATA +1 -1
  24. {supervisely-6.73.297.dist-info → supervisely-6.73.299.dist-info}/RECORD +28 -24
  25. {supervisely-6.73.297.dist-info → supervisely-6.73.299.dist-info}/LICENSE +0 -0
  26. {supervisely-6.73.297.dist-info → supervisely-6.73.299.dist-info}/WHEEL +0 -0
  27. {supervisely-6.73.297.dist-info → supervisely-6.73.299.dist-info}/entry_points.txt +0 -0
  28. {supervisely-6.73.297.dist-info → supervisely-6.73.299.dist-info}/top_level.txt +0 -0
@@ -619,6 +619,8 @@ class ApiField:
619
619
  """"""
620
620
  GROUP_ID = "groupId"
621
621
  """"""
622
+ EXPERIMENT = "experiment"
623
+ """"""
622
624
 
623
625
 
624
626
  def _get_single_item(items):
@@ -1136,3 +1136,64 @@ class TaskApi(ModuleApiBase, ModuleWithStatus):
1136
1136
  f"Invalid status value: {status}. Allowed values: {self.Status.values()}"
1137
1137
  )
1138
1138
  self._api.post("tasks.status.update", {ApiField.ID: task_id, ApiField.STATUS: status})
1139
+
1140
+ def set_output_experiment(self, task_id: int, experiment_info: dict) -> Dict:
1141
+ """
1142
+ Sets output for the task with experiment info.
1143
+
1144
+ :param task_id: Task ID in Supervisely.
1145
+ :type task_id: int
1146
+ :param experiment_info: Experiment info from TrainApp.
1147
+ :type experiment_info: dict
1148
+ :return: None
1149
+ :rtype: :class:`NoneType`
1150
+
1151
+ Example of experiment_info:
1152
+
1153
+ experiment_info = {
1154
+ 'experiment_name': '247_Lemons_RT-DETRv2-M',
1155
+ 'framework_name': 'RT-DETRv2',
1156
+ 'model_name': 'RT-DETRv2-M',
1157
+ 'task_type': 'object detection',
1158
+ 'project_id': 76,
1159
+ 'task_id': 247,
1160
+ 'model_files': {'config': 'model_config.yml'},
1161
+ 'checkpoints': ['checkpoints/best.pth', 'checkpoints/checkpoint0025.pth', 'checkpoints/checkpoint0050.pth', 'checkpoints/last.pth'],
1162
+ 'best_checkpoint': 'best.pth',
1163
+ 'export': {'ONNXRuntime': 'export/best.onnx'},
1164
+ 'app_state': 'app_state.json',
1165
+ 'model_meta': 'model_meta.json',
1166
+ 'train_val_split': 'train_val_split.json',
1167
+ 'train_size': 4,
1168
+ 'val_size': 2,
1169
+ 'hyperparameters': 'hyperparameters.yaml',
1170
+ 'hyperparameters_id': 45234,
1171
+ 'artifacts_dir': '/experiments/76_Lemons/247_RT-DETRv2/',
1172
+ 'datetime': '2025-01-22 18:13:43',
1173
+ 'evaluation_report_id': 12961,
1174
+ 'evaluation_report_link': 'https://app.supervisely.com/model-benchmark?id=12961',
1175
+ 'evaluation_metrics': {
1176
+ 'mAP': 0.994059405940594,
1177
+ 'AP50': 1.0, 'AP75': 1.0,
1178
+ 'f1': 0.9944444444444445,
1179
+ 'precision': 0.9944444444444445,
1180
+ 'recall': 0.9944444444444445,
1181
+ 'iou': 0.9726227736959404,
1182
+ 'classification_accuracy': 1.0,
1183
+ 'calibration_score': 0.8935745942476048,
1184
+ 'f1_optimal_conf': 0.500377893447876,
1185
+ 'expected_calibration_error': 0.10642540575239527,
1186
+ 'maximum_calibration_error': 0.499622106552124
1187
+ },
1188
+ 'primary_metric': 'mAP'
1189
+ 'logs': {
1190
+ 'type': 'tensorboard',
1191
+ 'link': '/experiments/76_Lemons/247_RT-DETRv2/logs/'
1192
+ },
1193
+ }
1194
+ """
1195
+ output = {ApiField.EXPERIMENT: {ApiField.DATA: {**experiment_info}}}
1196
+ resp = self._api.post(
1197
+ "tasks.output.set", {ApiField.TASK_ID: task_id, ApiField.OUTPUT: output}
1198
+ )
1199
+ return resp.json()
@@ -86,7 +86,7 @@
86
86
 
87
87
  <div id="sly-app">
88
88
  <sly-app>
89
- <template v-slot="{ post, state, data, session }">
89
+ <template v-slot="{ post, state, data, session, isStaticVersion, publicApiInstance }">
90
90
  <div
91
91
  :style="{'padding': `${state.app_body_padding} ${state.app_body_padding} 0`, 'display': 'flex', 'flex-direction': 'row', 'place-items': 'center'}"
92
92
  {% if __app_session_info_solid__ %} {% if __app_session_info_extra_content__ %}
@@ -14,7 +14,7 @@ from supervisely.app.widgets_context import JinjaWidgets
14
14
  js_bundle_version = "2.1.98"
15
15
 
16
16
  # https://github.com/supervisely-ecosystem/supervisely-app-frontend-js
17
- js_frontend_version = "v0.0.54"
17
+ js_frontend_version = "v0.0.55"
18
18
 
19
19
 
20
20
  pyodide_version = "v0.25.0"
@@ -148,4 +148,5 @@ from supervisely.app.widgets.select_dataset_tree.select_dataset_tree import Sele
148
148
  from supervisely.app.widgets.grid_gallery_v2.grid_gallery_v2 import GridGalleryV2
149
149
  from supervisely.app.widgets.report_thumbnail.report_thumbnail import ReportThumbnail
150
150
  from supervisely.app.widgets.experiment_selector.experiment_selector import ExperimentSelector
151
- from supervisely.app.widgets.bokeh.bokeh import Bokeh
151
+ from supervisely.app.widgets.bokeh.bokeh import Bokeh
152
+ from supervisely.app.widgets.run_app_button.run_app_button import RunAppButton
@@ -67,6 +67,7 @@ class Button(Widget):
67
67
  emit_on_click: Optional[str] = None,
68
68
  style: Optional[str] = None,
69
69
  call_on_click: Optional[str] = None,
70
+ visible_by_vue_field: Optional[str] = "",
70
71
  ):
71
72
  self._widget_routes = {}
72
73
 
@@ -88,6 +89,7 @@ class Button(Widget):
88
89
  self._emit_on_click = emit_on_click
89
90
  self._style = style
90
91
  self._call_on_click = call_on_click
92
+ self._visible_by_vue_field = visible_by_vue_field
91
93
 
92
94
  super().__init__(widget_id=widget_id, file_path=__file__)
93
95
 
@@ -117,7 +119,7 @@ class Button(Widget):
117
119
 
118
120
  def get_json_state(self) -> None:
119
121
  """Button widget doesn't have state, so this method returns None."""
120
- return None
122
+ return {"visible_by_vue_field": self._visible_by_vue_field}
121
123
 
122
124
  @property
123
125
  def text(self) -> str:
@@ -6,7 +6,11 @@ if widget._link is not none
6
6
  {%
7
7
  endif
8
8
  %}
9
+
9
10
  <el-button
11
+ {% if widget._visible_by_vue_field %}
12
+ v-show="{{{widget._visible_by_vue_field}}}"
13
+ {% endif %}
10
14
  :type="data.{{{widget.widget_id}}}.button_type"
11
15
  :plain="data.{{{widget.widget_id}}}.plain"
12
16
  :loading="data.{{{widget.widget_id}}}.loading"
@@ -4,6 +4,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
4
4
  from datetime import datetime
5
5
  from typing import Callable, Dict, List, Union
6
6
 
7
+ import supervisely.io.env as sly_env
7
8
  from supervisely import env, logger
8
9
  from supervisely._utils import abs_url, is_development
9
10
  from supervisely.api.api import Api
@@ -25,7 +26,6 @@ from supervisely.app.widgets import (
25
26
  )
26
27
  from supervisely.io.fs import get_file_name_with_ext
27
28
  from supervisely.nn.artifacts.artifacts import TrainInfo
28
- import supervisely.io.env as sly_env
29
29
 
30
30
  WEIGHTS_DIR = "weights"
31
31
 
@@ -427,7 +427,7 @@ class CustomModelsSelector(Widget):
427
427
  )
428
428
  return train_info.task_type, model_row
429
429
  except Exception as e:
430
- logger.warning(f"Failed to process train info: {train_info}. Error: {repr(e)}")
430
+ logger.debug(f"Failed to process train info: {train_info}. Error: {repr(e)}")
431
431
  return None, None
432
432
 
433
433
  table_rows = defaultdict(list)
@@ -21,7 +21,6 @@ from supervisely.io.fs import get_file_name_with_ext
21
21
  from supervisely.nn.experiments import ExperimentInfo
22
22
  from supervisely.nn.utils import ModelSource
23
23
 
24
-
25
24
  WEIGHTS_DIR = "weights"
26
25
 
27
26
  COL_ID = "task id".upper()
@@ -272,7 +271,7 @@ class ExperimentSelector(Widget):
272
271
  benchmark_report_link = f"/model-benchmark?id={self._benchmark_report_id}"
273
272
 
274
273
  benchmark_widget = Text(
275
- f"<i class='zmdi zmdi-chart' style='color: #7f858e'></i> <a href='{benchmark_report_link}' target='_blank'>evaluation report</a>",
274
+ f"<i class='zmdi zmdi-chart' style='color: #7f858e'></i> <a href='{benchmark_report_link}' target='_blank'>Evaluation report</a>",
276
275
  "text",
277
276
  )
278
277
  return benchmark_widget
@@ -378,7 +377,7 @@ class ExperimentSelector(Widget):
378
377
  )
379
378
  return experiment_info.task_type, model_row
380
379
  except Exception as e:
381
- logger.warn(f"Failed to process experiment info: {experiment_info}")
380
+ logger.debug(f"Failed to process experiment info: {experiment_info}")
382
381
  return None, None
383
382
 
384
383
  table_rows = defaultdict(list)
File without changes
@@ -0,0 +1,300 @@
1
+ try:
2
+ from typing import Literal
3
+ except ImportError:
4
+ from typing_extensions import Literal
5
+
6
+ from typing import Optional
7
+
8
+ from supervisely.app import DataJson, StateJson
9
+ from supervisely.app.widgets import Widget
10
+ from supervisely.app.widgets_context import JinjaWidgets
11
+
12
+
13
+ class RunAppButton(Widget):
14
+ def __init__(
15
+ self,
16
+ workspace_id: int,
17
+ module_id: int,
18
+ payload: dict = None,
19
+ text: Optional[str] = "Button",
20
+ button_type: Optional[
21
+ Literal["primary", "info", "warning", "danger", "success", "text"]
22
+ ] = "primary",
23
+ button_size: Optional[Literal["mini", "small", "large"]] = None,
24
+ plain: Optional[bool] = False,
25
+ icon: Optional[str] = None,
26
+ icon_gap: Optional[int] = 5,
27
+ available_in_offline: Optional[bool] = False,
28
+ visible_by_vue_field: Optional[str] = "",
29
+ widget_id: Optional[str] = None,
30
+ ):
31
+ """
32
+ Button the runs an app on Supervisely instance.
33
+
34
+ :param workspace_id: Workspace ID.
35
+ :type workspace_id: int
36
+ :param module_id: Module ID.
37
+ :type module_id: int
38
+ :param payload: Payload to be sent to the app.
39
+ :type payload: dict, optional
40
+ :param text: Text to be displayed on the button.
41
+ :type text: str, optional
42
+ :param button_type: Type of the button.
43
+ :type button_type: Literal["primary", "info", "warning", "danger", "success", "text"], optional
44
+ :param button_size: Size of the button.
45
+ :type button_size: Literal["mini", "small", "large"], optional
46
+ :param plain: If True, the button will be plain.
47
+ :type plain: bool, optional
48
+ :param icon: Icon to be displayed on the button.
49
+ :type icon: str, optional
50
+ :param icon_gap: Gap between the icon and the text.
51
+ :type icon_gap: int, optional
52
+ :param available_in_offline: If True, the button will be available in offline session.
53
+ :type available_in_offline: bool, optional
54
+ :param visible_by_vue_field: Vue field that controls the button visibility. If set to "isStaticVersion", the button will be visible only in offline session.
55
+ :type visible_by_vue_field: str, optional
56
+ :param widget_id: Widget ID.
57
+ :type widget_id: str, optional
58
+
59
+ :Usage example:
60
+ .. code-block:: python
61
+ from supervisely.app.widgets import RunAppButton
62
+
63
+ workspace_id = 123
64
+ project_id = 555
65
+ app_module_id = 777
66
+
67
+ run_app_button = RunAppButton(
68
+ workspace_id=workspace_id,
69
+ module_id=app_module_id,
70
+ payload={"state": {"slyProjectId": project_id}},
71
+ text="Run App",
72
+ button_type="info",
73
+ plain=True,
74
+ icon="zmdi zmdi-chart",
75
+ available_in_offline=True,
76
+ visible_by_vue_field="isStaticVersion",
77
+ )
78
+ """
79
+ self._text = text
80
+ self._button_type = button_type
81
+ self._button_size = button_size
82
+ self._plain = plain
83
+ self._icon_gap = icon_gap
84
+ if icon is None:
85
+ self._icon = ""
86
+ else:
87
+ self._icon = f'<i class="{icon}" style="margin-right: {icon_gap}px"></i>'
88
+
89
+ self._available_in_offline = available_in_offline
90
+ self._visible_by_vue_field = visible_by_vue_field
91
+
92
+ self._loading = False
93
+ self._disabled = False
94
+ self._workspace_id = workspace_id
95
+ self._module_id = module_id
96
+ self._payload = payload
97
+
98
+ super().__init__(widget_id=widget_id, file_path=__file__)
99
+
100
+ script_path = "./sly/css/app/widgets/run_app_button/script.js"
101
+ JinjaWidgets().context["__widget_scripts__"][self.__class__.__name__] = script_path
102
+
103
+ def get_json_data(self):
104
+ """Returns dictionary with widget data, which defines the appearance and behavior of the widget.
105
+
106
+ Dictionary contains the following fields:
107
+ - text: Text to be displayed on the button.
108
+ - button_type: Type of the button.
109
+ - plain: If True, the button will be plain.
110
+ - button_size: Size of the button.
111
+ - loading: If True, the button will show loading animation.
112
+ - disabled: If True, the button will be disabled.
113
+ - icon: Icon to be displayed on the button.
114
+ - available_in_offline: If True, the button will be available in offline session.
115
+ """
116
+ return {
117
+ "options": {
118
+ "text": self._text,
119
+ "button_type": self._button_type,
120
+ "plain": self._plain,
121
+ "button_size": self._button_size,
122
+ "loading": self._loading,
123
+ "disabled": self._disabled,
124
+ "icon": self._icon,
125
+ "available_in_offline": self._available_in_offline,
126
+ }
127
+ }
128
+
129
+ def get_json_state(self) -> None:
130
+ """Button widget doesn't have state, so this method returns None."""
131
+ return {
132
+ "workspace_id": self._workspace_id,
133
+ "module_id": self._module_id,
134
+ "payload": self._payload,
135
+ "visible_by_vue_field": self._visible_by_vue_field,
136
+ }
137
+
138
+ @property
139
+ def workspace_id(self) -> int:
140
+ """Returns the workspace ID.
141
+
142
+ :return: Workspace ID.
143
+ :rtype: int
144
+ """
145
+ return self._workspace_id
146
+
147
+ @workspace_id.setter
148
+ def workspace_id(self, value: int) -> None:
149
+ """Sets the workspace ID.
150
+
151
+ :param value: Workspace ID.
152
+ :type value: int
153
+ """
154
+ self._workspace_id = value
155
+ StateJson()[self.widget_id]["workspace_id"] = self._workspace_id
156
+ StateJson().send_changes()
157
+
158
+ @property
159
+ def text(self) -> str:
160
+ """Returns the text to be displayed on the button.
161
+
162
+ :return: Text to be displayed on the button.
163
+ :rtype: str
164
+ """
165
+ return self._text
166
+
167
+ @text.setter
168
+ def text(self, value: str) -> None:
169
+ """Sets the text to be displayed on the button.
170
+
171
+ :param value: Text to be displayed on the button.
172
+ :type value: str
173
+ """
174
+ self._text = value
175
+ DataJson()[self.widget_id]["options"]["text"] = self._text
176
+ DataJson().send_changes()
177
+
178
+ @property
179
+ def payload(self) -> dict:
180
+ """Returns the payload to be sent to the app.
181
+
182
+ :return: Payload to be sent to the app.
183
+ :rtype: dict
184
+ """
185
+ return self._payload
186
+
187
+ @payload.setter
188
+ def payload(self, value: dict) -> None:
189
+ """Sets the payload to be sent to the app.
190
+
191
+ :param value: Payload to be sent to the app.
192
+ :type value: dict
193
+ """
194
+ self._payload = value
195
+ StateJson()[self.widget_id]["payload"] = self._payload
196
+ StateJson().send_changes()
197
+
198
+ @property
199
+ def icon(self) -> str:
200
+ """Returns the icon to be displayed on the button.
201
+
202
+ :return: Icon to be displayed on the button.
203
+ :rtype: str
204
+ """
205
+ return self._icon
206
+
207
+ @icon.setter
208
+ def icon(self, value: str) -> None:
209
+ """Sets the icon to be displayed on the button.
210
+
211
+ :param value: Icon to be displayed on the button.
212
+ :type value: str
213
+ """
214
+ if value is None:
215
+ self._icon = ""
216
+ else:
217
+ self._icon = f'<i class="{value}" style="margin-right: {self._icon_gap}px"></i>'
218
+ DataJson()[self.widget_id]["options"]["icon"] = self._icon
219
+ DataJson().send_changes()
220
+
221
+ @property
222
+ def button_type(self) -> str:
223
+ """Returns the type of the button.
224
+
225
+ :return: Type of the button.
226
+ :rtype: str
227
+ """
228
+ return self._button_type
229
+
230
+ @button_type.setter
231
+ def button_type(
232
+ self, value: Literal["primary", "info", "warning", "danger", "success", "text"]
233
+ ) -> None:
234
+ """Sets the type of the button.
235
+
236
+ :param value: Type of the button.
237
+ :type value: Literal["primary", "info", "warning", "danger", "success", "text"]
238
+ """
239
+ self._button_type = value
240
+ DataJson()[self.widget_id]["options"]["button_type"] = self._button_type
241
+ DataJson().send_changes()
242
+
243
+ @property
244
+ def plain(self) -> bool:
245
+ """Returns True if the button is plain, False otherwise.
246
+
247
+ :return: True if the button is plain, False otherwise.
248
+ :rtype: bool
249
+ """
250
+ return self._plain
251
+
252
+ @plain.setter
253
+ def plain(self, value: bool) -> None:
254
+ """Sets the button to be plain or not.
255
+
256
+ :param value: If True, the button will be plain.
257
+ :type value: bool
258
+ """
259
+ self._plain = value
260
+ DataJson()[self.widget_id]["options"]["plain"] = self._plain
261
+ DataJson().send_changes()
262
+
263
+ @property
264
+ def loading(self) -> bool:
265
+ """Returns True if the button shows loading animation, False otherwise.
266
+
267
+ :return: True if the button shows loading animation, False otherwise.
268
+ :rtype: bool
269
+ """
270
+ return self._loading
271
+
272
+ @loading.setter
273
+ def loading(self, value: bool) -> None:
274
+ """Sets the button loading animation.
275
+
276
+ :param value: If True, the animation will be enabled, otherwise disabled.
277
+ :type value: bool
278
+ """
279
+ self._loading = value
280
+ DataJson()[self.widget_id]["options"]["loading"] = self._loading
281
+ DataJson().send_changes()
282
+
283
+ @property
284
+ def disabled(self) -> bool:
285
+ """Returns True if the button is disabled, False otherwise.
286
+
287
+ :return: True if the button is disabled, False otherwise.
288
+ :rtype: bool
289
+ """
290
+ return self._disabled
291
+
292
+ @disabled.setter
293
+ def disabled(self, value: bool) -> None:
294
+ """Sets the button to be disabled or not.
295
+
296
+ :param value: If True, the button will be disabled.
297
+ :type value: bool
298
+ """
299
+ self._disabled = value
300
+ DataJson()[self.widget_id]["options"]["disabled"] = self._disabled
@@ -0,0 +1,46 @@
1
+ Vue.component('sly-run-app-button', {
2
+ props: {
3
+ 'publicApiInstance': { 'type': Object },
4
+ 'workspaceId': { 'type': Number }, 'moduleId': { 'type': Number }, 'payload': { 'type': Object }, 'options': { 'type': Object }
5
+ },
6
+ data() {
7
+ return {
8
+ loading: false,
9
+ };
10
+ },
11
+ methods: {
12
+ async runApp() {
13
+ try {
14
+ this.loading = true;
15
+
16
+ const tasks = await this.publicApiInstance.post('tasks.run.app', {
17
+ params: this.payload,
18
+ workspaceId: this.workspaceId,
19
+ moduleId: this.moduleId,
20
+ nodeId: null,
21
+ }).then(response => response.data);
22
+
23
+ const task = tasks[0];
24
+ const origin = new URL(this.publicApiInstance.defaults.baseURL).origin;
25
+ window.open(`${origin}/apps/${task.appId}/sessions/${task.taskId}`, '_blank');
26
+ } finally {
27
+ this.loading = false;
28
+ }
29
+ }
30
+ },
31
+ template: `
32
+ <el-button
33
+ :class="{'available-in-offline': options.available_in_offline}"
34
+ @click="runApp"
35
+ v-loading="options.loading || loading"
36
+ :type="options.button_type"
37
+ :plain="options.plain"
38
+ :size="options.button_size"
39
+ :disabled="options.disabled"
40
+
41
+ >
42
+ <span v-html="options.icon"></span>
43
+ <span v-html="options.text"></span>
44
+ </el-button>
45
+ `,
46
+ });
@@ -0,0 +1,10 @@
1
+ <sly-run-app-button
2
+ :public-api-instance="publicApiInstance"
3
+ :workspace-id="state.{{{widget.widget_id}}}.workspace_id"
4
+ :module-id="state.{{{widget.widget_id}}}.module_id"
5
+ :payload="state.{{{widget.widget_id}}}.payload"
6
+ :options="data.{{{widget.widget_id}}}.options"
7
+ {% if widget._visible_by_vue_field %}
8
+ v-show="{{{widget._visible_by_vue_field}}}"
9
+ {% endif %}
10
+ ></sly-run-app-button>
@@ -372,7 +372,7 @@ class BaseTrainArtifacts:
372
372
  _upload_metadata(train_json)
373
373
  logger.info(f"Metadata for '{artifacts_folder}' was generated")
374
374
  else:
375
- logger.warn(f"Invalid metadata for '{artifacts_folder}'")
375
+ logger.debug(f"Invalid metadata for '{artifacts_folder}'")
376
376
  train_json = None
377
377
  return train_json
378
378
 
@@ -436,7 +436,7 @@ class BaseTrainArtifacts:
436
436
  )
437
437
  is_valid = self._validate_train_json(json_data)
438
438
  if not is_valid:
439
- logger.warn(f"Invalid metadata for '{artifacts_folder}'")
439
+ logger.debug(f"Invalid metadata for '{artifacts_folder}'")
440
440
  json_data = None
441
441
  return json_data
442
442
 
@@ -590,7 +590,7 @@ class BaseTrainArtifacts:
590
590
 
591
591
  return ExperimentInfo(**experiment_info_data)
592
592
  except Exception as e:
593
- logger.warning(f"Failed to build experiment info: {e}")
593
+ logger.debug(f"Failed to build experiment info: {e}")
594
594
  return None
595
595
 
596
596
  train_infos = self.get_list(sort)
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from pathlib import Path
3
- from typing import Callable, List, Optional, Tuple, Union, Type
3
+ from typing import Callable, List, Optional, Tuple, Type, Union
4
4
 
5
5
  import numpy as np
6
6
 
@@ -96,7 +96,7 @@ class BaseBenchmark:
96
96
  def key_metrics(self):
97
97
  eval_results = self.get_eval_result()
98
98
  return eval_results.key_metrics
99
-
99
+
100
100
  @property
101
101
  def primary_metric_name(self) -> str:
102
102
  return self._get_evaluator_class().eval_result_cls.PRIMARY_METRIC
@@ -415,7 +415,6 @@ class BaseBenchmark:
415
415
  "id": app_info["id"],
416
416
  }
417
417
  else:
418
- logger.warning("session.task_id is not set. App info will not be fetched.")
419
418
  app_info = None
420
419
  model_info = {
421
420
  **deploy_info,
@@ -561,15 +560,20 @@ class BaseBenchmark:
561
560
  def lnk(self):
562
561
  return self.visualizer.renderer.lnk
563
562
 
564
- def upload_report_link(self, remote_dir: str):
565
- template_path = os.path.join(remote_dir, "template.vue")
566
- vue_template_info = self.api.file.get_info_by_path(self.team_id, template_path)
567
- self.report_id = vue_template_info.id
568
-
569
- report_link = "/model-benchmark?id=" + str(vue_template_info.id)
563
+ def upload_report_link(self, remote_dir: str, report_id: int = None, local_dir: str = None):
564
+ if report_id is None:
565
+ template_path = os.path.join(remote_dir, "template.vue")
566
+ vue_template_info = self.api.file.get_info_by_path(self.team_id, template_path)
567
+ self.report_id = vue_template_info.id
568
+ report_id = vue_template_info.id
570
569
 
570
+ report_link = "/model-benchmark?id=" + str(report_id)
571
571
  lnk_name = "Model Evaluation Report.lnk"
572
- local_path = os.path.join(self.get_layout_results_dir(), lnk_name)
572
+
573
+ if local_dir is None:
574
+ local_dir = self.get_layout_results_dir()
575
+ local_path = os.path.join(local_dir, lnk_name)
576
+
573
577
  with open(local_path, "w") as file:
574
578
  file.write(report_link)
575
579