label-studio-sdk 1.0.7__py3-none-any.whl → 1.0.10__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 label-studio-sdk might be problematic. Click here for more details.

Files changed (206) hide show
  1. label_studio_sdk/__init__.py +26 -7
  2. label_studio_sdk/_extensions/label_studio_tools/core/label_config.py +13 -4
  3. label_studio_sdk/_extensions/label_studio_tools/core/utils/io.py +16 -4
  4. label_studio_sdk/_extensions/label_studio_tools/core/utils/json_schema.py +86 -0
  5. label_studio_sdk/_extensions/pager_ext.py +8 -0
  6. label_studio_sdk/_legacy/schema/label_config_schema.json +42 -11
  7. label_studio_sdk/actions/client.py +91 -40
  8. label_studio_sdk/actions/types/actions_create_request_filters.py +14 -24
  9. label_studio_sdk/actions/types/actions_create_request_filters_items_item.py +16 -26
  10. label_studio_sdk/actions/types/actions_create_request_filters_items_item_value.py +3 -1
  11. label_studio_sdk/actions/types/actions_create_request_selected_items.py +1 -2
  12. label_studio_sdk/actions/types/actions_create_request_selected_items_excluded.py +15 -25
  13. label_studio_sdk/actions/types/actions_create_request_selected_items_included.py +15 -25
  14. label_studio_sdk/annotations/__init__.py +2 -2
  15. label_studio_sdk/annotations/client.py +278 -104
  16. label_studio_sdk/annotations/types/__init__.py +2 -1
  17. label_studio_sdk/annotations/types/annotations_create_bulk_request_selected_items.py +34 -0
  18. label_studio_sdk/annotations/types/annotations_create_bulk_response_item.py +11 -21
  19. label_studio_sdk/base_client.py +46 -27
  20. label_studio_sdk/client.py +1 -0
  21. label_studio_sdk/comments/client.py +190 -44
  22. label_studio_sdk/converter/converter.py +66 -18
  23. label_studio_sdk/converter/imports/yolo.py +1 -1
  24. label_studio_sdk/converter/utils.py +3 -2
  25. label_studio_sdk/core/__init__.py +21 -4
  26. label_studio_sdk/core/client_wrapper.py +9 -10
  27. label_studio_sdk/core/file.py +37 -8
  28. label_studio_sdk/core/http_client.py +52 -28
  29. label_studio_sdk/core/jsonable_encoder.py +33 -31
  30. label_studio_sdk/core/pagination.py +5 -4
  31. label_studio_sdk/core/pydantic_utilities.py +272 -4
  32. label_studio_sdk/core/query_encoder.py +38 -13
  33. label_studio_sdk/core/request_options.py +3 -0
  34. label_studio_sdk/core/serialization.py +272 -0
  35. label_studio_sdk/errors/bad_request_error.py +2 -3
  36. label_studio_sdk/export_storage/azure/client.py +228 -58
  37. label_studio_sdk/export_storage/azure/types/azure_create_response.py +19 -29
  38. label_studio_sdk/export_storage/azure/types/azure_update_response.py +19 -29
  39. label_studio_sdk/export_storage/client.py +48 -18
  40. label_studio_sdk/export_storage/gcs/client.py +228 -58
  41. label_studio_sdk/export_storage/gcs/types/gcs_create_response.py +19 -29
  42. label_studio_sdk/export_storage/gcs/types/gcs_update_response.py +19 -29
  43. label_studio_sdk/export_storage/local/client.py +222 -56
  44. label_studio_sdk/export_storage/local/types/local_create_response.py +17 -27
  45. label_studio_sdk/export_storage/local/types/local_update_response.py +17 -27
  46. label_studio_sdk/export_storage/redis/client.py +228 -58
  47. label_studio_sdk/export_storage/redis/types/redis_create_response.py +20 -30
  48. label_studio_sdk/export_storage/redis/types/redis_update_response.py +20 -30
  49. label_studio_sdk/export_storage/s3/client.py +228 -58
  50. label_studio_sdk/export_storage/s3/types/s3create_response.py +27 -35
  51. label_studio_sdk/export_storage/s3/types/s3update_response.py +27 -35
  52. label_studio_sdk/export_storage/s3s/client.py +187 -43
  53. label_studio_sdk/export_storage/types/export_storage_list_types_response_item.py +11 -21
  54. label_studio_sdk/files/client.py +172 -56
  55. label_studio_sdk/import_storage/azure/client.py +223 -53
  56. label_studio_sdk/import_storage/azure/types/azure_create_response.py +22 -32
  57. label_studio_sdk/import_storage/azure/types/azure_update_response.py +22 -32
  58. label_studio_sdk/import_storage/client.py +48 -18
  59. label_studio_sdk/import_storage/gcs/client.py +223 -53
  60. label_studio_sdk/import_storage/gcs/types/gcs_create_response.py +22 -32
  61. label_studio_sdk/import_storage/gcs/types/gcs_update_response.py +22 -32
  62. label_studio_sdk/import_storage/local/client.py +223 -53
  63. label_studio_sdk/import_storage/local/types/local_create_response.py +17 -27
  64. label_studio_sdk/import_storage/local/types/local_update_response.py +17 -27
  65. label_studio_sdk/import_storage/redis/client.py +223 -53
  66. label_studio_sdk/import_storage/redis/types/redis_create_response.py +20 -30
  67. label_studio_sdk/import_storage/redis/types/redis_update_response.py +20 -30
  68. label_studio_sdk/import_storage/s3/client.py +223 -53
  69. label_studio_sdk/import_storage/s3/types/s3create_response.py +31 -39
  70. label_studio_sdk/import_storage/s3/types/s3update_response.py +31 -39
  71. label_studio_sdk/import_storage/s3s/client.py +222 -52
  72. label_studio_sdk/import_storage/types/import_storage_list_types_response_item.py +11 -21
  73. label_studio_sdk/label_interface/control_tags.py +205 -10
  74. label_studio_sdk/label_interface/interface.py +80 -6
  75. label_studio_sdk/label_interface/region.py +1 -10
  76. label_studio_sdk/ml/client.py +280 -78
  77. label_studio_sdk/ml/types/ml_create_response.py +21 -31
  78. label_studio_sdk/ml/types/ml_update_response.py +21 -31
  79. label_studio_sdk/model_providers/client.py +656 -21
  80. label_studio_sdk/predictions/client.py +247 -101
  81. label_studio_sdk/projects/__init__.py +3 -0
  82. label_studio_sdk/projects/client.py +309 -115
  83. label_studio_sdk/projects/client_ext.py +16 -0
  84. label_studio_sdk/projects/exports/__init__.py +3 -0
  85. label_studio_sdk/projects/exports/client.py +447 -296
  86. label_studio_sdk/projects/exports/client_ext.py +134 -0
  87. label_studio_sdk/projects/exports/types/__init__.py +6 -0
  88. label_studio_sdk/projects/exports/types/exports_convert_response.py +24 -0
  89. label_studio_sdk/projects/exports/types/exports_list_formats_response_item.py +44 -0
  90. label_studio_sdk/projects/types/projects_create_response.py +29 -34
  91. label_studio_sdk/projects/types/projects_import_tasks_response.py +19 -29
  92. label_studio_sdk/projects/types/projects_list_response.py +11 -21
  93. label_studio_sdk/projects/types/projects_update_response.py +24 -34
  94. label_studio_sdk/prompts/client.py +309 -92
  95. label_studio_sdk/prompts/indicators/client.py +67 -23
  96. label_studio_sdk/prompts/runs/client.py +95 -40
  97. label_studio_sdk/prompts/types/prompts_batch_failed_predictions_request_failed_predictions_item.py +14 -24
  98. label_studio_sdk/prompts/types/prompts_batch_failed_predictions_response.py +11 -21
  99. label_studio_sdk/prompts/types/prompts_batch_predictions_request_results_item.py +26 -29
  100. label_studio_sdk/prompts/types/prompts_batch_predictions_response.py +11 -21
  101. label_studio_sdk/prompts/versions/client.py +389 -75
  102. label_studio_sdk/tasks/client.py +263 -90
  103. label_studio_sdk/tasks/types/tasks_list_response.py +15 -25
  104. label_studio_sdk/types/__init__.py +16 -6
  105. label_studio_sdk/types/annotation.py +29 -38
  106. label_studio_sdk/types/annotation_filter_options.py +14 -24
  107. label_studio_sdk/types/annotations_dm_field.py +30 -39
  108. label_studio_sdk/types/azure_blob_export_storage.py +28 -37
  109. label_studio_sdk/types/azure_blob_import_storage.py +28 -37
  110. label_studio_sdk/types/base_task.py +30 -39
  111. label_studio_sdk/types/base_task_updated_by.py +3 -1
  112. label_studio_sdk/types/base_user.py +14 -21
  113. label_studio_sdk/types/comment.py +12 -21
  114. label_studio_sdk/types/comment_created_by.py +1 -1
  115. label_studio_sdk/types/converted_format.py +12 -22
  116. label_studio_sdk/types/data_manager_task_serializer.py +31 -40
  117. label_studio_sdk/types/data_manager_task_serializer_annotators_item.py +1 -1
  118. label_studio_sdk/types/data_manager_task_serializer_drafts_item.py +13 -22
  119. label_studio_sdk/types/data_manager_task_serializer_predictions_item.py +15 -24
  120. label_studio_sdk/types/export.py +17 -26
  121. label_studio_sdk/types/export_format.py +25 -0
  122. label_studio_sdk/types/export_snapshot.py +45 -0
  123. label_studio_sdk/types/export_snapshot_status.py +5 -0
  124. label_studio_sdk/types/file_upload.py +11 -21
  125. label_studio_sdk/types/filter.py +16 -26
  126. label_studio_sdk/types/filter_group.py +12 -22
  127. label_studio_sdk/types/gcs_export_storage.py +28 -37
  128. label_studio_sdk/types/gcs_import_storage.py +28 -37
  129. label_studio_sdk/types/inference_run.py +14 -23
  130. label_studio_sdk/types/inference_run_cost_estimate.py +47 -0
  131. label_studio_sdk/types/inference_run_created_by.py +1 -1
  132. label_studio_sdk/types/inference_run_organization.py +1 -1
  133. label_studio_sdk/types/key_indicator_value.py +12 -22
  134. label_studio_sdk/types/key_indicators.py +0 -1
  135. label_studio_sdk/types/key_indicators_item.py +15 -25
  136. label_studio_sdk/types/key_indicators_item_additional_kpis_item.py +13 -23
  137. label_studio_sdk/types/key_indicators_item_extra_kpis_item.py +13 -23
  138. label_studio_sdk/types/local_files_export_storage.py +25 -34
  139. label_studio_sdk/types/local_files_import_storage.py +24 -33
  140. label_studio_sdk/types/ml_backend.py +23 -32
  141. label_studio_sdk/types/model_provider_connection.py +47 -26
  142. label_studio_sdk/types/model_provider_connection_budget_reset_period.py +5 -0
  143. label_studio_sdk/types/model_provider_connection_created_by.py +1 -1
  144. label_studio_sdk/types/model_provider_connection_organization.py +1 -1
  145. label_studio_sdk/types/model_provider_connection_provider.py +3 -1
  146. label_studio_sdk/types/prediction.py +21 -30
  147. label_studio_sdk/types/project.py +48 -55
  148. label_studio_sdk/types/project_import.py +21 -30
  149. label_studio_sdk/types/project_label_config.py +12 -22
  150. label_studio_sdk/types/prompt.py +24 -32
  151. label_studio_sdk/types/prompt_associated_projects_item.py +6 -0
  152. label_studio_sdk/types/prompt_associated_projects_item_id.py +20 -0
  153. label_studio_sdk/types/prompt_created_by.py +1 -1
  154. label_studio_sdk/types/prompt_organization.py +1 -1
  155. label_studio_sdk/types/prompt_version.py +13 -22
  156. label_studio_sdk/types/prompt_version_created_by.py +1 -1
  157. label_studio_sdk/types/prompt_version_organization.py +1 -1
  158. label_studio_sdk/types/prompt_version_provider.py +3 -1
  159. label_studio_sdk/types/redis_export_storage.py +29 -38
  160. label_studio_sdk/types/redis_import_storage.py +28 -37
  161. label_studio_sdk/types/refined_prompt_response.py +19 -29
  162. label_studio_sdk/types/s3export_storage.py +36 -43
  163. label_studio_sdk/types/s3import_storage.py +37 -44
  164. label_studio_sdk/types/s3s_export_storage.py +26 -33
  165. label_studio_sdk/types/s3s_import_storage.py +35 -42
  166. label_studio_sdk/types/serialization_option.py +12 -22
  167. label_studio_sdk/types/serialization_options.py +18 -28
  168. label_studio_sdk/types/task.py +46 -48
  169. label_studio_sdk/types/task_annotators_item.py +1 -1
  170. label_studio_sdk/types/task_comment_authors_item.py +5 -0
  171. label_studio_sdk/types/task_filter_options.py +15 -25
  172. label_studio_sdk/types/user_simple.py +11 -21
  173. label_studio_sdk/types/view.py +16 -26
  174. label_studio_sdk/types/webhook.py +19 -28
  175. label_studio_sdk/types/webhook_serializer_for_update.py +19 -28
  176. label_studio_sdk/types/workspace.py +22 -31
  177. label_studio_sdk/users/client.py +257 -63
  178. label_studio_sdk/users/types/users_get_token_response.py +12 -22
  179. label_studio_sdk/users/types/users_reset_token_response.py +12 -22
  180. label_studio_sdk/version.py +0 -1
  181. label_studio_sdk/versions/__init__.py +5 -0
  182. label_studio_sdk/versions/client.py +112 -0
  183. label_studio_sdk/versions/types/__init__.py +6 -0
  184. label_studio_sdk/versions/types/versions_get_response.py +73 -0
  185. label_studio_sdk/versions/types/versions_get_response_edition.py +5 -0
  186. label_studio_sdk/views/client.py +219 -52
  187. label_studio_sdk/views/types/views_create_request_data.py +13 -23
  188. label_studio_sdk/views/types/views_create_request_data_filters.py +14 -24
  189. label_studio_sdk/views/types/views_create_request_data_filters_items_item.py +16 -26
  190. label_studio_sdk/views/types/views_create_request_data_filters_items_item_value.py +3 -1
  191. label_studio_sdk/views/types/views_update_request_data.py +13 -23
  192. label_studio_sdk/views/types/views_update_request_data_filters.py +14 -24
  193. label_studio_sdk/views/types/views_update_request_data_filters_items_item.py +16 -26
  194. label_studio_sdk/views/types/views_update_request_data_filters_items_item_value.py +3 -1
  195. label_studio_sdk/webhooks/client.py +191 -61
  196. label_studio_sdk/workspaces/client.py +164 -41
  197. label_studio_sdk/workspaces/members/client.py +109 -31
  198. label_studio_sdk/workspaces/members/types/members_create_response.py +12 -22
  199. label_studio_sdk/workspaces/members/types/members_list_response_item.py +12 -22
  200. {label_studio_sdk-1.0.7.dist-info → label_studio_sdk-1.0.10.dist-info}/METADATA +9 -5
  201. {label_studio_sdk-1.0.7.dist-info → label_studio_sdk-1.0.10.dist-info}/RECORD +203 -186
  202. {label_studio_sdk-1.0.7.dist-info → label_studio_sdk-1.0.10.dist-info}/WHEEL +1 -1
  203. label_studio_sdk/types/export_convert.py +0 -32
  204. label_studio_sdk/types/export_create.py +0 -54
  205. label_studio_sdk/types/export_create_status.py +0 -5
  206. {label_studio_sdk-1.0.7.dist-info → label_studio_sdk-1.0.10.dist-info}/LICENSE +0 -0
@@ -93,6 +93,15 @@ class ControlTag(LabelStudioTag):
93
93
  and tag.tag not in _NOT_CONTROL_TAGS
94
94
  )
95
95
 
96
+ def to_json_schema(self):
97
+ """
98
+ Converts the current ControlTag instance into a JSON Schema.
99
+
100
+ Returns:
101
+ dict: A dictionary representing the JSON Schema.
102
+ """
103
+ return {"type": "string"}
104
+
96
105
  @classmethod
97
106
  def parse_node(cls, tag: xml.etree.ElementTree.Element, tags_mapping=None) -> "ControlTag":
98
107
  """
@@ -261,11 +270,11 @@ class ControlTag(LabelStudioTag):
261
270
 
262
271
  def _validate_labels(self, labels):
263
272
  """Check that labels is a subset of self.labels, used for
264
- example when you're validate the annotaion or prediction to
273
+ example when you're validate the annotation or prediction to
265
274
  make sure there no undefined labels used.
266
275
 
267
276
  """
268
- if not self.labels:
277
+ if not self.labels or not labels:
269
278
  return True
270
279
 
271
280
  return set(labels).issubset(set(self.labels))
@@ -335,10 +344,11 @@ class ControlTag(LabelStudioTag):
335
344
 
336
345
  return to_name
337
346
  else:
338
- if len(self.to_name) > 1:
339
- raise Exception(
340
- "Multiple to_name in control tag, specify to_name in function"
341
- )
347
+ # TODO: "Pairwise" tag has multiple to_name
348
+ # if len(self.to_name) > 1:
349
+ # raise Exception(
350
+ # "Multiple to_name in control tag, specify to_name in function"
351
+ # )
342
352
 
343
353
  return self.to_name[0]
344
354
 
@@ -415,7 +425,7 @@ class ControlTag(LabelStudioTag):
415
425
  )
416
426
 
417
427
  kwargs[self._label_attr_name] = label
418
-
428
+
419
429
  return self._label_simple(to_name=to_name, **kwargs)
420
430
 
421
431
  def label(
@@ -446,12 +456,29 @@ class ControlTag(LabelStudioTag):
446
456
  Region
447
457
  A new Region object with the specified label applied.
448
458
  """
449
- if hasattr(self, "_label_attr_name"):
459
+ if hasattr(self, "_label_attr_name") and label is not None:
450
460
  return self._label_with_labels(
451
461
  label=label, to_name=to_name, *args, **kwargs
452
462
  )
453
463
  else:
454
464
  return self._label_simple(to_name=to_name, *args, **kwargs)
465
+
466
+ def get_labels(self, regions: List[Dict]):
467
+ """
468
+ Returns the simplified representation of the label. Sort of a reverse to label() method to retrieve an input `label` from an output regions
469
+ """
470
+ values = [region.get('value') for region in regions if region.get('from_name') == self.name]
471
+ values = list(filter(lambda x: x is not None, values))
472
+ if not hasattr(self, "_label_attr_name"):
473
+ return values
474
+ labels = []
475
+ for value in values:
476
+ if len(value) == 1 and self._label_attr_name in value:
477
+ v = value[self._label_attr_name]
478
+ labels.append(v[0] if type(v) == list and len(v) == 1 else v)
479
+ else:
480
+ labels.append(value)
481
+ return labels[0] if len(labels) == 1 else labels
455
482
 
456
483
  def as_tuple(self):
457
484
  """ """
@@ -485,6 +512,34 @@ class ChoicesTag(ControlTag):
485
512
  _label_attr_name: str = "choices"
486
513
  _value_class: Type[ChoicesValue] = ChoicesValue
487
514
 
515
+ @property
516
+ def is_multiple_choice(self):
517
+ return self.attr.get("choice") == "multiple"
518
+
519
+ def to_json_schema(self):
520
+ """
521
+ Converts the current ChoicesTag instance into a JSON Schema.
522
+
523
+ Returns:
524
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
525
+ """
526
+ if self.is_multiple_choice:
527
+ return {
528
+ "type": "array",
529
+ "items": {
530
+ "type": "string",
531
+ "enum": self.labels,
532
+ },
533
+ "uniqueItems": True,
534
+ "description": f"Choices for {self.to_name[0]}",
535
+ }
536
+
537
+ return {
538
+ "type": "string",
539
+ "enum": self.labels,
540
+ "description": f"Choices for {self.to_name[0]}"
541
+ }
542
+
488
543
 
489
544
  class LabelsValue(SpanSelection):
490
545
  labels: List[str]
@@ -496,6 +551,41 @@ class LabelsTag(ControlTag):
496
551
  _label_attr_name: str = "labels"
497
552
  _value_class: Type[LabelsValue] = LabelsValue
498
553
 
554
+ def to_json_schema(self):
555
+ """
556
+ Converts the current LabelsTag instance into a JSON Schema.
557
+
558
+ Returns:
559
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
560
+ """
561
+ return {
562
+ "type": "array",
563
+ "items": {
564
+ "type": "object",
565
+ "required": ["start", "end", "labels"],
566
+ "properties": {
567
+ "start": {
568
+ "type": "integer",
569
+ "minimum": 0
570
+ },
571
+ "end": {
572
+ "type": "integer",
573
+ "minimum": 0
574
+ },
575
+ "labels": {
576
+ "type": "array",
577
+ "items": {
578
+ "type": "string",
579
+ "enum": self.labels
580
+ }
581
+ },
582
+ "text": {
583
+ "type": "string"
584
+ }
585
+ }
586
+ },
587
+ "description": f"Labels and span indices for {self.to_name[0]}"
588
+ }
499
589
 
500
590
  ## Image tags
501
591
 
@@ -684,6 +774,26 @@ class NumberTag(ControlTag):
684
774
  """ """
685
775
  tag: str = "Number"
686
776
  _value_class: Type[NumberValue] = NumberValue
777
+ _label_attr_name: str = "number"
778
+
779
+ def to_json_schema(self):
780
+ """
781
+ Converts the current NumberTag instance into a JSON Schema.
782
+
783
+ Returns:
784
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
785
+ """
786
+ schema = {
787
+ "type": "number",
788
+ "description": f"Number for {self.to_name[0]}"
789
+ }
790
+
791
+ if 'min' in self.attr:
792
+ schema["minimum"] = float(self.attr['min'])
793
+ if 'max' in self.attr:
794
+ schema["maximum"] = float(self.attr['max'])
795
+
796
+ return schema
687
797
 
688
798
 
689
799
  class DateTimeValue(BaseModel):
@@ -694,6 +804,25 @@ class DateTimeTag(ControlTag):
694
804
  """ """
695
805
  tag: str = "DateTime"
696
806
  _value_class: Type[DateTimeValue] = DateTimeValue
807
+ _label_attr_name: str = "datetime"
808
+
809
+ def _label_simple(self, to_name: Optional[str] = None, *args, **kwargs) -> Region:
810
+ # TODO: temporary fix to force datetime to be a string
811
+ kwargs['datetime'] = kwargs['datetime'][0]
812
+ return super()._label_simple(to_name, *args, **kwargs)
813
+
814
+ def to_json_schema(self):
815
+ """
816
+ Converts the current DateTimeTag instance into a JSON Schema.
817
+
818
+ Returns:
819
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
820
+ """
821
+ return {
822
+ "type": "string",
823
+ "format": "date-time",
824
+ "description": f"Date and time for {self.to_name[0]}"
825
+ }
697
826
 
698
827
 
699
828
  class HyperTextLabelsValue(SpanSelectionOffsets):
@@ -715,12 +844,27 @@ class PairwiseTag(ControlTag):
715
844
  """ """
716
845
  tag: str = "Pairwise"
717
846
  _value_class: Type[PairwiseValue] = PairwiseValue
847
+ _label_attr_name: str = "selected"
718
848
 
719
- def label(self, side):
849
+ def label(self, label):
720
850
  """ """
721
- value = PairwiseValue(selected=side)
851
+ value = PairwiseValue(selected=label)
852
+ # <Pairwise> tag has equal from_name and to_name, and string label that's not a list of strings
722
853
  return Region(from_tag=self, to_tag=self, value=value)
723
854
 
855
+ def to_json_schema(self):
856
+ """
857
+ Converts the current PairwiseTag instance into a JSON Schema.
858
+
859
+ Returns:
860
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
861
+ """
862
+ return {
863
+ "type": "string",
864
+ "enum": ["left", "right"],
865
+ "description": f"Pairwise selection between {self.to_name[0]} (left) and {self.to_name[1]} (right)"
866
+ }
867
+
724
868
 
725
869
  class ParagraphLabelsValue(SpanSelectionOffsets):
726
870
  paragraphlabels: List[str]
@@ -759,6 +903,22 @@ class RatingTag(ControlTag):
759
903
  """ """
760
904
  tag: str = "Rating"
761
905
  _value_class: Type[RatingValue] = RatingValue
906
+ _label_attr_name: str = "rating"
907
+
908
+ def to_json_schema(self):
909
+ """
910
+ Converts the current RatingTag instance into a JSON Schema.
911
+
912
+ Returns:
913
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
914
+ """
915
+ max_rating = int(self.attr.get('maxRating', 5)) # Default to 5 if not specified
916
+ return {
917
+ "type": "integer",
918
+ "minimum": 0,
919
+ "maximum": max_rating,
920
+ "description": f"Rating for {self.to_name[0]} (0 to {max_rating})"
921
+ }
762
922
 
763
923
 
764
924
  class RelationsTag(ControlTag):
@@ -784,6 +944,27 @@ class TaxonomyTag(ControlTag):
784
944
  """ """
785
945
  tag: str = "Taxonomy"
786
946
  _value_class: Type[TaxonomyValue] = TaxonomyValue
947
+ _label_attr_name: str = "taxonomy"
948
+
949
+ def to_json_schema(self):
950
+ """
951
+ Converts the current TaxonomyTag instance into a JSON Schema.
952
+
953
+ Returns:
954
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
955
+ """
956
+ return {
957
+ "type": "array",
958
+ "items": {
959
+ "type": "array",
960
+ "items": {
961
+ "type": "string",
962
+ # TODO: enforce the order of the enums according to the taxonomy tree
963
+ "enum": self.labels
964
+ }
965
+ },
966
+ "description": f"Taxonomy for {self.to_name[0]}. Each item is a path from root to selected node."
967
+ }
787
968
 
788
969
 
789
970
  class TextAreaValue(BaseModel):
@@ -794,6 +975,20 @@ class TextAreaTag(ControlTag):
794
975
  """ """
795
976
  tag: str = "TextArea"
796
977
  _value_class: Type[TextAreaValue] = TextAreaValue
978
+ _label_attr_name: str = "text"
979
+
980
+ def to_json_schema(self):
981
+ """
982
+ Converts the current TextAreaTag instance into a JSON Schema.
983
+
984
+ Returns:
985
+ dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
986
+ """
987
+ return {
988
+ "type": "string",
989
+ "description": f"Text for {self.to_name[0]}"
990
+ }
991
+
797
992
 
798
993
 
799
994
  class TimeSeriesValue(SpanSelection):
@@ -15,6 +15,7 @@ from pydantic import BaseModel
15
15
  from collections import defaultdict, OrderedDict
16
16
  from lxml import etree
17
17
  import xmljson
18
+ from jsf import JSF
18
19
 
19
20
  from label_studio_sdk._legacy.exceptions import (
20
21
  LSConfigParseException,
@@ -320,7 +321,7 @@ class LabelInterface:
320
321
  # 1. we should allow control.label to process custom payload outside of those strictly containing "label"
321
322
  # 2. we should be less open regarding the payload type and defining the strict typing elsewhere,
322
323
  # but likely that requires rewriting of how ControlTag.label() is working now
323
- if isinstance(payload, str):
324
+ if isinstance(payload, (str, int, float)):
324
325
  payload = {'label': payload}
325
326
  elif isinstance(payload, list):
326
327
  if len(payload) > 0:
@@ -331,6 +332,7 @@ class LabelInterface:
331
332
 
332
333
  if isinstance(payload, Dict):
333
334
  payload = [payload]
335
+
334
336
  for item in payload:
335
337
  regions.append(control.label(**item))
336
338
 
@@ -531,12 +533,26 @@ class LabelInterface:
531
533
  tree.task_loaded = True
532
534
 
533
535
  for obj in tree.objects:
534
- print(obj.value_is_variable, obj.value_name)
535
536
  if obj.value_is_variable and obj.value_name in task:
536
537
  obj.value = task.get(obj.value_name)
537
538
 
538
539
  return tree
539
540
 
541
+ def to_json_schema(self):
542
+ """
543
+ Converts the current LabelInterface instance into a JSON Schema.
544
+
545
+ Returns:
546
+ dict: A dictionary representing the JSON Schema.
547
+ """
548
+ return {
549
+ "type": "object",
550
+ "properties": {
551
+ name: control.to_json_schema() for name, control in self._controls.items()
552
+ },
553
+ "required": list(self._controls.keys())
554
+ }
555
+
540
556
  def parse(self, config_string: str) -> Tuple[Dict, Dict, Dict, etree._Element]:
541
557
  """Parses the received configuration string into dictionaries
542
558
  of ControlTags, ObjectTags, and Labels, along with an XML tree
@@ -755,7 +771,7 @@ class LabelInterface:
755
771
  return False
756
772
 
757
773
  # type of the region should match the tag name
758
- if control.tag.lower() != region["type"]:
774
+ if control.tag.lower() != region["type"].lower():
759
775
  return False
760
776
 
761
777
  # make sure that in config it connects to the same tag as
@@ -824,9 +840,67 @@ class LabelInterface:
824
840
 
825
841
  return task
826
842
 
827
- def generate_sample_annotation(self):
828
- """ """
829
- raise NotImplemented()
843
+ def _generate_sample_regions(self):
844
+ """ Generate an example of each control tag's JSON schema and validate it as a region"""
845
+ return self.create_regions({
846
+ control.name: JSF(control.to_json_schema()).generate()
847
+ for control in self.controls
848
+ })
849
+
850
+ def generate_sample_prediction(self) -> Optional[dict]:
851
+ """Generates a sample prediction that is valid for this label config.
852
+
853
+ Example:
854
+ {'model_version': 'sample model version',
855
+ 'score': 0.0,
856
+ 'result': [{'id': 'e7bd76e6-4e88-4eb3-b433-55e03661bf5d',
857
+ 'from_name': 'sentiment',
858
+ 'to_name': 'text',
859
+ 'type': 'choices',
860
+ 'value': {'choices': ['Neutral']}}]}
861
+
862
+ NOTE: `id` field in result is not required when importing predictions; it will be generated automatically.
863
+ NOTE: for each control tag, depends on tag.to_json_schema() being implemented correctly
864
+ """
865
+ prediction = PredictionValue(
866
+ model_version='sample model version',
867
+ result=self._generate_sample_regions()
868
+ )
869
+ prediction_dct = prediction.model_dump()
870
+ if self.validate_prediction(prediction_dct):
871
+ return prediction_dct
872
+ else:
873
+ logger.debug(f'Sample prediction {prediction_dct} failed validation for label config {self.config}')
874
+ return None
875
+
876
+ def generate_sample_annotation(self) -> Optional[dict]:
877
+ """Generates a sample annotation that is valid for this label config.
878
+
879
+ Example:
880
+ {'was_cancelled': False,
881
+ 'ground_truth': False,
882
+ 'lead_time': 0.0,
883
+ 'result_count': 0,
884
+ 'completed_by': -1,
885
+ 'result': [{'id': 'b05da11d-3ffc-4657-8b8d-f5bc37cd59ac',
886
+ 'from_name': 'sentiment',
887
+ 'to_name': 'text',
888
+ 'type': 'choices',
889
+ 'value': {'choices': ['Negative']}}]}
890
+
891
+ NOTE: `id` field in result is not required when importing predictions; it will be generated automatically.
892
+ NOTE: for each control tag, depends on tag.to_json_schema() being implemented correctly
893
+ """
894
+ annotation = AnnotationValue(
895
+ completed_by=-1, # annotator's user id
896
+ result=self._generate_sample_regions()
897
+ )
898
+ annotation_dct = annotation.model_dump()
899
+ if self.validate_annotation(annotation_dct):
900
+ return annotation_dct
901
+ else:
902
+ logger.debug(f'Sample annotation {annotation_dct} failed validation for label config {self.config}')
903
+ return None
830
904
 
831
905
  #####
832
906
  ##### COMPATIBILITY LAYER
@@ -10,16 +10,7 @@ from pydantic import BaseModel, Field
10
10
 
11
11
  class Region(BaseModel):
12
12
  """
13
- Class for Region Tag
14
-
15
- Attributes:
16
- -----------
17
- id: str
18
- The unique identifier of the region
19
- x: int
20
- The x coordinate of the region
21
- y: int
22
-
13
+ A Region is an item in the `result` list of a PredictionValue or AnnotationValue.
23
14
  """
24
15
 
25
16
  id: str = Field(default_factory=lambda: str(uuid4()))