supervisely 6.73.410__py3-none-any.whl → 6.73.470__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 (190) hide show
  1. supervisely/__init__.py +136 -1
  2. supervisely/_utils.py +81 -0
  3. supervisely/annotation/json_geometries_map.py +2 -0
  4. supervisely/annotation/label.py +80 -3
  5. supervisely/api/annotation_api.py +9 -9
  6. supervisely/api/api.py +67 -43
  7. supervisely/api/app_api.py +72 -5
  8. supervisely/api/dataset_api.py +108 -33
  9. supervisely/api/entity_annotation/figure_api.py +113 -49
  10. supervisely/api/image_api.py +82 -0
  11. supervisely/api/module_api.py +10 -0
  12. supervisely/api/nn/deploy_api.py +15 -9
  13. supervisely/api/nn/ecosystem_models_api.py +201 -0
  14. supervisely/api/nn/neural_network_api.py +12 -3
  15. supervisely/api/pointcloud/pointcloud_api.py +38 -0
  16. supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
  17. supervisely/api/project_api.py +213 -6
  18. supervisely/api/task_api.py +11 -1
  19. supervisely/api/video/video_annotation_api.py +4 -2
  20. supervisely/api/video/video_api.py +79 -1
  21. supervisely/api/video/video_figure_api.py +24 -11
  22. supervisely/api/volume/volume_api.py +38 -0
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +14 -6
  25. supervisely/app/fastapi/__init__.py +1 -0
  26. supervisely/app/fastapi/custom_static_files.py +1 -1
  27. supervisely/app/fastapi/multi_user.py +88 -0
  28. supervisely/app/fastapi/subapp.py +175 -42
  29. supervisely/app/fastapi/templating.py +1 -1
  30. supervisely/app/fastapi/websocket.py +77 -9
  31. supervisely/app/singleton.py +21 -0
  32. supervisely/app/v1/app_service.py +18 -2
  33. supervisely/app/v1/constants.py +7 -1
  34. supervisely/app/widgets/__init__.py +11 -1
  35. supervisely/app/widgets/agent_selector/template.html +1 -0
  36. supervisely/app/widgets/card/card.py +20 -0
  37. supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
  38. supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
  39. supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
  40. supervisely/app/widgets/dialog/dialog.py +12 -0
  41. supervisely/app/widgets/dialog/template.html +2 -1
  42. supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
  43. supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
  44. supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
  45. supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
  46. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
  47. supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
  48. supervisely/app/widgets/fast_table/fast_table.py +713 -126
  49. supervisely/app/widgets/fast_table/script.js +492 -95
  50. supervisely/app/widgets/fast_table/style.css +54 -0
  51. supervisely/app/widgets/fast_table/template.html +45 -5
  52. supervisely/app/widgets/heatmap/__init__.py +0 -0
  53. supervisely/app/widgets/heatmap/heatmap.py +523 -0
  54. supervisely/app/widgets/heatmap/script.js +378 -0
  55. supervisely/app/widgets/heatmap/style.css +227 -0
  56. supervisely/app/widgets/heatmap/template.html +21 -0
  57. supervisely/app/widgets/input_tag/input_tag.py +102 -15
  58. supervisely/app/widgets/input_tag_list/__init__.py +0 -0
  59. supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
  60. supervisely/app/widgets/input_tag_list/template.html +70 -0
  61. supervisely/app/widgets/radio_table/radio_table.py +10 -2
  62. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  63. supervisely/app/widgets/radio_tabs/template.html +1 -0
  64. supervisely/app/widgets/select/select.py +6 -4
  65. supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
  66. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
  67. supervisely/app/widgets/table/table.py +68 -13
  68. supervisely/app/widgets/tabs/tabs.py +22 -6
  69. supervisely/app/widgets/tabs/template.html +5 -1
  70. supervisely/app/widgets/transfer/style.css +3 -0
  71. supervisely/app/widgets/transfer/template.html +3 -1
  72. supervisely/app/widgets/transfer/transfer.py +48 -45
  73. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  74. supervisely/convert/image/csv/csv_converter.py +24 -15
  75. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
  76. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
  77. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
  78. supervisely/convert/video/video_converter.py +2 -2
  79. supervisely/geometry/polyline_3d.py +110 -0
  80. supervisely/io/env.py +161 -1
  81. supervisely/nn/artifacts/__init__.py +1 -1
  82. supervisely/nn/artifacts/artifacts.py +10 -2
  83. supervisely/nn/artifacts/detectron2.py +1 -0
  84. supervisely/nn/artifacts/hrda.py +1 -0
  85. supervisely/nn/artifacts/mmclassification.py +20 -0
  86. supervisely/nn/artifacts/mmdetection.py +5 -3
  87. supervisely/nn/artifacts/mmsegmentation.py +1 -0
  88. supervisely/nn/artifacts/ritm.py +1 -0
  89. supervisely/nn/artifacts/rtdetr.py +1 -0
  90. supervisely/nn/artifacts/unet.py +1 -0
  91. supervisely/nn/artifacts/utils.py +3 -0
  92. supervisely/nn/artifacts/yolov5.py +2 -0
  93. supervisely/nn/artifacts/yolov8.py +1 -0
  94. supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
  95. supervisely/nn/experiments.py +9 -0
  96. supervisely/nn/inference/cache.py +37 -17
  97. supervisely/nn/inference/gui/serving_gui_template.py +39 -13
  98. supervisely/nn/inference/inference.py +953 -211
  99. supervisely/nn/inference/inference_request.py +15 -8
  100. supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
  101. supervisely/nn/inference/object_detection/object_detection.py +1 -0
  102. supervisely/nn/inference/predict_app/__init__.py +0 -0
  103. supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
  104. supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
  105. supervisely/nn/inference/predict_app/gui/gui.py +915 -0
  106. supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
  107. supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
  108. supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
  109. supervisely/nn/inference/predict_app/gui/preview.py +93 -0
  110. supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
  111. supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
  112. supervisely/nn/inference/predict_app/gui/utils.py +399 -0
  113. supervisely/nn/inference/predict_app/predict_app.py +176 -0
  114. supervisely/nn/inference/session.py +47 -39
  115. supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
  116. supervisely/nn/inference/tracking/point_tracking.py +5 -1
  117. supervisely/nn/inference/tracking/tracker_interface.py +4 -0
  118. supervisely/nn/inference/uploader.py +9 -5
  119. supervisely/nn/model/model_api.py +44 -22
  120. supervisely/nn/model/prediction.py +15 -1
  121. supervisely/nn/model/prediction_session.py +70 -14
  122. supervisely/nn/prediction_dto.py +7 -0
  123. supervisely/nn/tracker/__init__.py +6 -8
  124. supervisely/nn/tracker/base_tracker.py +54 -0
  125. supervisely/nn/tracker/botsort/__init__.py +1 -0
  126. supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
  127. supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
  128. supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
  129. supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
  130. supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
  131. supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
  132. supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
  133. supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
  134. supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
  135. supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
  136. supervisely/nn/tracker/botsort_tracker.py +273 -0
  137. supervisely/nn/tracker/calculate_metrics.py +264 -0
  138. supervisely/nn/tracker/utils.py +273 -0
  139. supervisely/nn/tracker/visualize.py +520 -0
  140. supervisely/nn/training/gui/gui.py +152 -49
  141. supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
  142. supervisely/nn/training/gui/model_selector.py +8 -6
  143. supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
  144. supervisely/nn/training/gui/training_artifacts.py +3 -1
  145. supervisely/nn/training/train_app.py +225 -46
  146. supervisely/project/pointcloud_episode_project.py +12 -8
  147. supervisely/project/pointcloud_project.py +12 -8
  148. supervisely/project/project.py +221 -75
  149. supervisely/template/experiment/experiment.html.jinja +105 -55
  150. supervisely/template/experiment/experiment_generator.py +258 -112
  151. supervisely/template/experiment/header.html.jinja +31 -13
  152. supervisely/template/experiment/sly-style.css +7 -2
  153. supervisely/versions.json +3 -1
  154. supervisely/video/sampling.py +42 -20
  155. supervisely/video/video.py +41 -12
  156. supervisely/video_annotation/video_figure.py +38 -4
  157. supervisely/volume/stl_converter.py +2 -0
  158. supervisely/worker_api/agent_rpc.py +24 -1
  159. supervisely/worker_api/rpc_servicer.py +31 -7
  160. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
  161. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
  162. supervisely_lib/__init__.py +6 -1
  163. supervisely/app/widgets/experiment_selector/style.css +0 -27
  164. supervisely/app/widgets/experiment_selector/template.html +0 -61
  165. supervisely/nn/tracker/bot_sort/__init__.py +0 -21
  166. supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
  167. supervisely/nn/tracker/bot_sort/matching.py +0 -127
  168. supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
  169. supervisely/nn/tracker/deep_sort/__init__.py +0 -6
  170. supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
  171. supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
  172. supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
  173. supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
  174. supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
  175. supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
  176. supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
  177. supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
  178. supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
  179. supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
  180. supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
  181. supervisely/nn/tracker/tracker.py +0 -285
  182. supervisely/nn/tracker/utils/kalman_filter.py +0 -492
  183. supervisely/nn/tracking/__init__.py +0 -1
  184. supervisely/nn/tracking/boxmot.py +0 -114
  185. supervisely/nn/tracking/tracking.py +0 -24
  186. /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
  187. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
  188. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
  189. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
  190. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/top_level.txt +0 -0
@@ -70,6 +70,11 @@ from supervisely.api.module_api import (
70
70
  _get_single_item,
71
71
  )
72
72
  from supervisely.imaging import image as sly_image
73
+ from supervisely.io.env import (
74
+ add_uploaded_ids_to_env,
75
+ app_categories,
76
+ increment_upload_count,
77
+ )
73
78
  from supervisely.io.fs import (
74
79
  OFFSETS_PKL_BATCH_SIZE,
75
80
  OFFSETS_PKL_SUFFIX,
@@ -392,6 +397,9 @@ class ImageInfo(NamedTuple):
392
397
  #: Format: "YYYY-MM-DDTHH:MM:SS.sssZ"
393
398
  embeddings_updated_at: Optional[str] = None
394
399
 
400
+ #: :class:`int`: :class:`Dataset<supervisely.project.project.Project>` ID in Supervisely.
401
+ project_id: int = None
402
+
395
403
  # DO NOT DELETE THIS COMMENT
396
404
  #! New fields must be added with default values to keep backward compatibility.
397
405
 
@@ -471,6 +479,7 @@ class ImageApi(RemoveableBulkModuleApi):
471
479
  ApiField.OFFSET_END,
472
480
  ApiField.AI_SEARCH_META,
473
481
  ApiField.EMBEDDINGS_UPDATED_AT,
482
+ ApiField.PROJECT_ID,
474
483
  ]
475
484
 
476
485
  @staticmethod
@@ -2613,6 +2622,16 @@ class ImageApi(RemoveableBulkModuleApi):
2613
2622
  info_json_copy[ApiField.EXT] = info_json[ApiField.MIME].split("/")[1]
2614
2623
  # results.append(self.InfoType(*[info_json_copy[field_name] for field_name in self.info_sequence()]))
2615
2624
  results.append(self._convert_json_info(info_json_copy))
2625
+
2626
+ try:
2627
+ if "import" in app_categories():
2628
+ ids = [info.id for info in results[-len(batch_names) :]]
2629
+ if len(ids) > 0:
2630
+ increment_upload_count(dataset_id, len(ids))
2631
+ add_uploaded_ids_to_env(dataset_id, ids)
2632
+ except:
2633
+ pass
2634
+
2616
2635
  break
2617
2636
  except HTTPError as e:
2618
2637
  error_details = e.response.json().get("details", {})
@@ -5508,3 +5527,66 @@ class ImageApi(RemoveableBulkModuleApi):
5508
5527
  method,
5509
5528
  {ApiField.IMAGES: images},
5510
5529
  )
5530
+
5531
+ def get_subsequent_image_ids(
5532
+ self,
5533
+ image_id: int,
5534
+ images_count: Optional[int] = None,
5535
+ job_id: Optional[int] = None,
5536
+ params: Optional[dict] = None,
5537
+ dataset_id: Optional[int] = None,
5538
+ project_id: Optional[int] = None,
5539
+ ) -> List[int]:
5540
+ """
5541
+ Get list of subsequent image IDs after the specified image ID.
5542
+
5543
+ :param image_id: Image ID in Supervisely.
5544
+ :type image_id: int
5545
+ :param images_count: Number of subsequent images to retrieve. If None, retrieves all subsequent images.
5546
+ :type images_count: int, optional
5547
+ :param job_id: Job ID to filter images. If None, does not filter by job ID.
5548
+ :type job_id: int, optional
5549
+ :param params: Additional parameters for filtering and sorting images.
5550
+ :type params: dict, optional
5551
+ :param dataset_id: Dataset ID to filter images.
5552
+ :type dataset_id: int, optional
5553
+ :param project_id: Project ID to filter images. If None, makes a request to retrieve it from the specified image.
5554
+ :type project_id: int, optional
5555
+ """
5556
+ data = {
5557
+ "recursive": True,
5558
+ "projectId": project_id,
5559
+ "filters": [],
5560
+ "sort": "name",
5561
+ "sort_order": "asc",
5562
+ }
5563
+
5564
+ if params is not None:
5565
+ data.update(params)
5566
+
5567
+ if data["projectId"] is None:
5568
+ image_info = self.get_info_by_id(image_id)
5569
+ if image_info is None:
5570
+ raise ValueError(f"Image with ID {image_id} not found.")
5571
+ project_id = self._api.dataset.get_info_by_id(image_info.dataset_id).project_id
5572
+ if job_id is not None:
5573
+ self._api.add_header("x-job-id", str(job_id))
5574
+ if dataset_id is not None:
5575
+ data["datasetId"] = dataset_id
5576
+
5577
+ image_infos = self.get_list_all_pages(
5578
+ "images.list",
5579
+ data,
5580
+ limit=None,
5581
+ return_first_response=False,
5582
+ )
5583
+ self._api.headers.pop("x-job-id", None)
5584
+ image_ids = [img_info.id for img_info in image_infos]
5585
+ if len(image_ids) == 0:
5586
+ raise ValueError("No images found with the specified criteria.")
5587
+ elif image_id not in image_ids:
5588
+ raise ValueError(f"Image with ID {image_id} not found in the specified entity.")
5589
+
5590
+ target_idx = image_ids.index(image_id) + 1
5591
+ to_idx = target_idx + images_count if images_count is not None else len(image_ids)
5592
+ return image_ids[target_idx:to_idx]
@@ -691,6 +691,10 @@ class ApiField:
691
691
  """"""
692
692
  UPDATE_STRATEGY = "updateStrategy"
693
693
  """"""
694
+ FILE_PATH = "filePath"
695
+ """"""
696
+ FILE_KEY = "fileKey"
697
+ """"""
694
698
  LOCAL_ENTITIES_COUNT = "localEntitiesCount"
695
699
  """"""
696
700
  REMOTE_ENTITIES_COUNT = "remoteEntitiesCount"
@@ -703,6 +707,12 @@ class ApiField:
703
707
  """"""
704
708
  ERROR_MESSAGE = "errorMessage"
705
709
  """"""
710
+ UNIQUE_ITEMS = "uniqueItems"
711
+ """"""
712
+ NN_CREATED = "nnCreated"
713
+ """"""
714
+ NN_UPDATED = "nnUpdated"
715
+ """"""
706
716
 
707
717
 
708
718
  def _get_single_item(items):
@@ -157,16 +157,15 @@ class DeployApi:
157
157
  "device": device,
158
158
  "model_source": ModelSource.CUSTOM,
159
159
  "model_files": {
160
- "checkpoint": Path(
161
- experiment_info.artifacts_dir, "checkpoints", checkpoint_name
162
- ).as_posix(),
163
- "config": Path(
164
- experiment_info.artifacts_dir, experiment_info.model_files["config"]
165
- ).as_posix(),
160
+ key: Path(experiment_info.artifacts_dir, value).as_posix()
161
+ for key, value in experiment_info.model_files.items()
166
162
  },
167
163
  "model_info": experiment_info.to_json(),
168
164
  "runtime": runtime,
169
165
  }
166
+ deploy_params["model_files"]["checkpoint"] = Path(
167
+ experiment_info.artifacts_dir, "checkpoints", checkpoint_name
168
+ ).as_posix()
170
169
  self._load_model_from_api(session_id, deploy_params)
171
170
 
172
171
  def _find_agent(self, team_id: int = None, public=True, gpu=True):
@@ -239,6 +238,7 @@ class DeployApi:
239
238
  RTDETR,
240
239
  Detectron2,
241
240
  MMClassification,
241
+ MMPretrain,
242
242
  MMDetection,
243
243
  MMDetection3,
244
244
  MMSegmentation,
@@ -262,6 +262,7 @@ class DeployApi:
262
262
  RTDETR(team_id).framework_name: RTDETR(team_id).serve_slug,
263
263
  Detectron2(team_id).framework_name: Detectron2(team_id).serve_slug,
264
264
  MMClassification(team_id).framework_name: MMClassification(team_id).serve_slug,
265
+ MMPretrain(team_id).framework_name: MMPretrain(team_id).serve_slug,
265
266
  MMDetection(team_id).framework_name: MMDetection(team_id).serve_slug,
266
267
  MMDetection3(team_id).framework_name: MMDetection3(team_id).serve_slug,
267
268
  MMSegmentation(team_id).framework_name: MMSegmentation(team_id).serve_slug,
@@ -547,7 +548,6 @@ class DeployApi:
547
548
  _attempt_delay_sec = 1
548
549
  _attempts = timeout // _attempt_delay_sec
549
550
 
550
- # @TODO: Run app in team?
551
551
  if workspace_id is None:
552
552
  workspace_id = env.workspace_id()
553
553
  kwargs = get_valid_kwargs(
@@ -768,10 +768,14 @@ class DeployApi:
768
768
  for file_key, file_path in experiment_info.model_files.items():
769
769
  full_file_path = os.path.join(experiment_info.artifacts_dir, file_path)
770
770
  if not self._api.file.exists(team_id, full_file_path):
771
- logger.debug(f"Model file not found: '{full_file_path}'. Trying to find it by checkpoint path.")
771
+ logger.debug(
772
+ f"Model file not found: '{full_file_path}'. Trying to find it by checkpoint path."
773
+ )
772
774
  full_file_path = os.path.join(artifacts_dir, file_path)
773
775
  if not self._api.file.exists(team_id, full_file_path):
774
- raise ValueError(f"Model file not found: '{full_file_path}'. Make sure that the file exists in the artifacts directory.")
776
+ raise ValueError(
777
+ f"Model file not found: '{full_file_path}'. Make sure that the file exists in the artifacts directory."
778
+ )
775
779
  deploy_params["model_files"][file_key] = full_file_path
776
780
  logger.debug(f"Model file added: {full_file_path}")
777
781
  return module_id, serve_app_name, deploy_params
@@ -845,6 +849,7 @@ class DeployApi:
845
849
  RTDETR,
846
850
  Detectron2,
847
851
  MMClassification,
852
+ MMPretrain,
848
853
  MMDetection,
849
854
  MMDetection3,
850
855
  MMSegmentation,
@@ -863,6 +868,7 @@ class DeployApi:
863
868
  frameworks = {
864
869
  "/detectron2": Detectron2,
865
870
  "/mmclassification": MMClassification,
871
+ "/mmclassification-v2": MMPretrain,
866
872
  "/mmdetection": MMDetection,
867
873
  "/mmdetection-3": MMDetection3,
868
874
  "/mmsegmentation": MMSegmentation,
@@ -0,0 +1,201 @@
1
+ from typing import List, Literal
2
+
3
+ from supervisely.api.api import Api
4
+ from supervisely.api.module_api import ApiField, ModuleApi
5
+
6
+
7
+ class ModelApiField:
8
+ NAME = "name"
9
+ FRAMEWORK = "framework"
10
+ TASK_TYPE = "task"
11
+ MODALITY = "modality"
12
+ TRAIN_MODULE_ID = "trainModuleId"
13
+ SERVE_MODULE_ID = "serveModuleId"
14
+ ARCHITECTURE = "architecture"
15
+ PRETRAINED = "pretrained"
16
+ NUM_CLASSES = "numClasses"
17
+ SIZE = "size"
18
+ PARAMS_M = "paramsM"
19
+ GFLOPS = "GFLOPs"
20
+ TAGS = "tags"
21
+ RUNTIMES = "runtimes"
22
+ FILES = "files"
23
+ SPEED_TESTS = "speedTests"
24
+ EVALUATION = "evaluation"
25
+
26
+
27
+ class EcosystemModelsApi(ModuleApi):
28
+
29
+ def __init__(self, api: Api):
30
+ self._api = api
31
+
32
+ def _convert_json_info(self, json_info):
33
+ return json_info
34
+
35
+ def get_list_all_pages(
36
+ self,
37
+ method,
38
+ data,
39
+ progress_cb=None,
40
+ convert_json_info_cb=None,
41
+ limit: int = None,
42
+ return_first_response: bool = False,
43
+ ):
44
+ """
45
+ Get list of all or limited quantity entities from the Supervisely server.
46
+
47
+ :param method: Request method name
48
+ :type method: str
49
+ :param data: Dictionary with request body info
50
+ :type data: dict
51
+ :param progress_cb: Function for tracking download progress.
52
+ :type progress_cb: Progress, optional
53
+ :param convert_json_info_cb: Function for convert json info
54
+ :type convert_json_info_cb: Callable, optional
55
+ :param limit: Number of entity to retrieve
56
+ :type limit: int, optional
57
+ :param return_first_response: Specify if return first response
58
+ :type return_first_response: bool, optional
59
+ """
60
+
61
+ if convert_json_info_cb is None:
62
+ convert_func = self._convert_json_info
63
+ else:
64
+ convert_func = convert_json_info_cb
65
+
66
+ if ApiField.SORT not in data:
67
+ data = self._add_sort_param(data)
68
+ first_response = self._api.get(method, {}, data=data).json()
69
+ total = first_response["total"]
70
+ per_page = first_response["perPage"]
71
+ pages_count = first_response["pagesCount"]
72
+
73
+ limit_exceeded = False
74
+ results = first_response["entities"]
75
+ if limit is not None and len(results) > limit:
76
+ limit_exceeded = True
77
+
78
+ if progress_cb is not None:
79
+ progress_cb(len(results))
80
+ if (pages_count == 1 and len(results) == total) or limit_exceeded is True:
81
+ pass
82
+ else:
83
+ for page_idx in range(2, pages_count + 1):
84
+ temp_resp = self._api.get(
85
+ method, {}, data={**data, "page": page_idx, "per_page": per_page}
86
+ )
87
+ temp_items = temp_resp.json()["entities"]
88
+ results.extend(temp_items)
89
+ if progress_cb is not None:
90
+ progress_cb(len(temp_items))
91
+ if limit is not None and len(results) > limit:
92
+ limit_exceeded = True
93
+ break
94
+
95
+ if len(results) != total and limit is None:
96
+ raise RuntimeError(
97
+ "Method {!r}: error during pagination, some items are missed".format(method)
98
+ )
99
+
100
+ if limit is not None:
101
+ results = results[:limit]
102
+ if return_first_response:
103
+ return [convert_func(item) for item in results], first_response
104
+ return [convert_func(item) for item in results]
105
+
106
+ def list_models(self, local=False):
107
+ method = "ecosystem.models.list"
108
+ data = {"localModels": local}
109
+ return self.get_list_all_pages(method, data=data)
110
+
111
+ def add(
112
+ self,
113
+ name: str,
114
+ framework: str,
115
+ task_type: str,
116
+ tain_module_id: int,
117
+ serve_module_id: int,
118
+ modality: Literal["images", "videos"] = "images",
119
+ architecture: str = None,
120
+ pretrained: bool = False,
121
+ num_classes: int = None,
122
+ size: int = None,
123
+ params_m: int = None,
124
+ gflops: float = None,
125
+ tags: List[str] = None,
126
+ runtimes: List[str] = None,
127
+ files: List[str] = None,
128
+ speed_tests: List = None,
129
+ evaluation: dict = None,
130
+ ):
131
+ method = "ecosystem.models.add"
132
+ data = {
133
+ ModelApiField.NAME: name,
134
+ ModelApiField.FRAMEWORK: framework,
135
+ ModelApiField.TASK_TYPE: task_type,
136
+ ModelApiField.MODALITY: modality,
137
+ ModelApiField.TRAIN_MODULE_ID: tain_module_id,
138
+ ModelApiField.SERVE_MODULE_ID: serve_module_id,
139
+ }
140
+ optional_fields = {
141
+ ModelApiField.ARCHITECTURE: architecture,
142
+ ModelApiField.PRETRAINED: pretrained,
143
+ ModelApiField.NUM_CLASSES: num_classes,
144
+ ModelApiField.SIZE: size,
145
+ ModelApiField.PARAMS_M: params_m,
146
+ ModelApiField.GFLOPS: gflops,
147
+ ModelApiField.TAGS: tags,
148
+ ModelApiField.RUNTIMES: runtimes,
149
+ ModelApiField.FILES: files,
150
+ ModelApiField.SPEED_TESTS: speed_tests,
151
+ ModelApiField.EVALUATION: evaluation,
152
+ }
153
+ for key, value in optional_fields.items():
154
+ if value is not None:
155
+ data[key] = value
156
+ return self._api.post(method, data=data)
157
+
158
+ def update_model(
159
+ self,
160
+ model_id: int,
161
+ name: str = None,
162
+ framework: str = None,
163
+ task_type: str = None,
164
+ tain_module_id: int = None,
165
+ serve_module_id: int = None,
166
+ modality: Literal["images", "videos"] = None,
167
+ architecture: str = None,
168
+ pretrained: bool = False,
169
+ num_classes: int = None,
170
+ size: int = None,
171
+ params_m: int = None,
172
+ gflops: float = None,
173
+ tags: List[str] = None,
174
+ runtimes: List[str] = None,
175
+ files: List[str] = None,
176
+ speed_tests: List = None,
177
+ evaluation: dict = None,
178
+ ):
179
+ data = {
180
+ ModelApiField.NAME: name,
181
+ ModelApiField.FRAMEWORK: framework,
182
+ ModelApiField.TASK_TYPE: task_type,
183
+ ModelApiField.MODALITY: modality,
184
+ ModelApiField.TRAIN_MODULE_ID: tain_module_id,
185
+ ModelApiField.SERVE_MODULE_ID: serve_module_id,
186
+ ModelApiField.ARCHITECTURE: architecture,
187
+ ModelApiField.PRETRAINED: pretrained,
188
+ ModelApiField.NUM_CLASSES: num_classes,
189
+ ModelApiField.SIZE: size,
190
+ ModelApiField.PARAMS_M: params_m,
191
+ ModelApiField.GFLOPS: gflops,
192
+ ModelApiField.TAGS: tags,
193
+ ModelApiField.RUNTIMES: runtimes,
194
+ ModelApiField.FILES: files,
195
+ ModelApiField.SPEED_TESTS: speed_tests,
196
+ ModelApiField.EVALUATION: evaluation,
197
+ }
198
+ data = {k: v for k, v in data.items() if v is not None}
199
+ method = "ecosystem.models.update"
200
+ data["id"] = model_id
201
+ return self._api.post(method, data=data)
@@ -23,9 +23,11 @@ class NeuralNetworkApi:
23
23
 
24
24
  def __init__(self, api: "Api"):
25
25
  from supervisely.api.nn.deploy_api import DeployApi
26
+ from supervisely.api.nn.ecosystem_models_api import EcosystemModelsApi
26
27
 
27
28
  self._api = api
28
29
  self._deploy_api = DeployApi(api)
30
+ self.ecosystem_models_api = EcosystemModelsApi(api)
29
31
 
30
32
  def deploy(
31
33
  self,
@@ -168,7 +170,7 @@ class NeuralNetworkApi:
168
170
  workspaces = [workspace_id]
169
171
  elif team_id is not None:
170
172
  workspaces = self._api.workspace.get_list(team_id)
171
- workspaces = [workspace["id"] for workspace in workspaces]
173
+ workspaces = [workspace.id for workspace in workspaces]
172
174
  else:
173
175
  workspace_id = env.workspace_id(raise_not_found=False)
174
176
  if workspace_id is None:
@@ -178,7 +180,7 @@ class NeuralNetworkApi:
178
180
  "Workspace ID and Team ID are not specified and cannot be found in the environment."
179
181
  )
180
182
  workspaces = self._api.workspace.get_list(team_id)
181
- workspaces = [workspace["id"] for workspace in workspaces]
183
+ workspaces = [workspace.id for workspace in workspaces]
182
184
  else:
183
185
  workspaces = [workspace_id]
184
186
 
@@ -202,7 +204,14 @@ class NeuralNetworkApi:
202
204
  # get deploy infos and filter results
203
205
  result = []
204
206
  for task in all_tasks:
205
- deploy_info = self._deploy_api.get_deploy_info(task["id"])
207
+ try:
208
+ deploy_info = self._deploy_api.get_deploy_info(task["id"])
209
+ except Exception as e:
210
+ logger.warning(
211
+ f"Failed to get deploy info for task {task['id']}: {e}",
212
+ exc_info=True,
213
+ )
214
+ continue
206
215
  if model is not None:
207
216
  checkpoint = deploy_info["checkpoint_name"]
208
217
  deployed_model = deploy_info["model_name"]
@@ -1431,3 +1431,41 @@ class PointcloudApi(RemoveableBulkModuleApi):
1431
1431
  )
1432
1432
  tasks.append(task)
1433
1433
  await asyncio.gather(*tasks)
1434
+
1435
+ def rename(
1436
+ self,
1437
+ id: int,
1438
+ name: str,
1439
+ ) -> PointcloudInfo:
1440
+ """Renames Pointcloud with given ID to a new name.
1441
+
1442
+ :param id: Pointcloud ID in Supervisely.
1443
+ :type id: int
1444
+ :param name: New Pointcloud name.
1445
+ :type name: str
1446
+ :return: Information about updated Pointcloud.
1447
+ :rtype: :class:`PointcloudInfo`
1448
+
1449
+ :Usage example:
1450
+
1451
+ .. code-block:: python
1452
+
1453
+ import supervisely as sly
1454
+
1455
+ api = sly.Api.from_env()
1456
+ os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
1457
+ os.environ['API_TOKEN'] = 'Your Supervisely API Token'
1458
+
1459
+ pointcloud_id = 123456
1460
+ new_pointcloud_name = "3333_new.pcd"
1461
+
1462
+ api.pointcloud.rename(id=pointcloud_id, name=new_pointcloud_name)
1463
+ """
1464
+
1465
+ data = {
1466
+ ApiField.ID: id,
1467
+ ApiField.NAME: name,
1468
+ }
1469
+
1470
+ response = self._api.post("images.editInfo", data)
1471
+ return self._convert_json_info(response.json())
@@ -107,6 +107,9 @@ class PointcloudEpisodeAnnotationAPI(EntityAnnotationAPI):
107
107
  """
108
108
 
109
109
  response = self._api.post(self._method_download, {ApiField.DATASET_ID: dataset_id})
110
+ result = response.json()
111
+ if len(result) == 0:
112
+ return PointcloudEpisodeAnnotation().to_json()
110
113
  return response.json()[0]
111
114
 
112
115
  def download_bulk(self, dataset_id, entity_ids):