clarifai 9.8.1__py3-none-any.whl → 9.9.0__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 (124) hide show
  1. clarifai/client/app.py +115 -14
  2. clarifai/client/base.py +11 -4
  3. clarifai/client/dataset.py +8 -3
  4. clarifai/client/input.py +34 -28
  5. clarifai/client/model.py +71 -2
  6. clarifai/client/module.py +4 -2
  7. clarifai/client/runner.py +161 -0
  8. clarifai/client/search.py +173 -0
  9. clarifai/client/user.py +110 -4
  10. clarifai/client/workflow.py +27 -2
  11. clarifai/constants/search.py +2 -0
  12. clarifai/datasets/upload/loaders/xview_detection.py +1 -1
  13. clarifai/models/model_serving/README.md +3 -3
  14. clarifai/models/model_serving/cli/deploy_cli.py +2 -3
  15. clarifai/models/model_serving/cli/repository.py +3 -5
  16. clarifai/models/model_serving/constants.py +1 -5
  17. clarifai/models/model_serving/docs/custom_config.md +5 -6
  18. clarifai/models/model_serving/docs/dependencies.md +5 -10
  19. clarifai/models/model_serving/examples/image_classification/age_vit/requirements.txt +1 -0
  20. clarifai/models/model_serving/examples/text_classification/xlm-roberta/requirements.txt +1 -0
  21. clarifai/models/model_serving/examples/text_to_image/sd-v1.5/requirements.txt +1 -0
  22. clarifai/models/model_serving/examples/text_to_text/bart-summarize/requirements.txt +1 -0
  23. clarifai/models/model_serving/examples/visual_detection/yolov5x/requirements.txt +1 -1
  24. clarifai/models/model_serving/examples/visual_embedding/vit-base/requirements.txt +1 -0
  25. clarifai/models/model_serving/examples/visual_segmentation/segformer-b2/requirements.txt +1 -0
  26. clarifai/models/model_serving/model_config/__init__.py +2 -0
  27. clarifai/models/model_serving/model_config/config.py +298 -0
  28. clarifai/models/model_serving/model_config/model_types_config/text-classifier.yaml +18 -0
  29. clarifai/models/model_serving/model_config/model_types_config/text-embedder.yaml +18 -0
  30. clarifai/models/model_serving/model_config/model_types_config/text-to-image.yaml +18 -0
  31. clarifai/models/model_serving/model_config/model_types_config/text-to-text.yaml +18 -0
  32. clarifai/models/model_serving/model_config/model_types_config/visual-classifier.yaml +18 -0
  33. clarifai/models/model_serving/model_config/model_types_config/visual-detector.yaml +28 -0
  34. clarifai/models/model_serving/model_config/model_types_config/visual-embedder.yaml +18 -0
  35. clarifai/models/model_serving/model_config/model_types_config/visual-segmenter.yaml +18 -0
  36. clarifai/models/model_serving/model_config/serializer.py +1 -1
  37. clarifai/models/model_serving/models/default_test.py +22 -21
  38. clarifai/models/model_serving/models/output.py +2 -2
  39. clarifai/models/model_serving/pb_model_repository.py +2 -5
  40. clarifai/runners/__init__.py +0 -0
  41. clarifai/runners/example.py +33 -0
  42. clarifai/schema/search.py +60 -0
  43. clarifai/utils/logging.py +53 -3
  44. clarifai/versions.py +1 -1
  45. clarifai/workflows/__init__.py +0 -0
  46. clarifai/workflows/export.py +68 -0
  47. clarifai/workflows/utils.py +59 -0
  48. clarifai/workflows/validate.py +67 -0
  49. {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/METADATA +20 -2
  50. {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/RECORD +102 -86
  51. clarifai_utils/client/app.py +115 -14
  52. clarifai_utils/client/base.py +11 -4
  53. clarifai_utils/client/dataset.py +8 -3
  54. clarifai_utils/client/input.py +34 -28
  55. clarifai_utils/client/model.py +71 -2
  56. clarifai_utils/client/module.py +4 -2
  57. clarifai_utils/client/runner.py +161 -0
  58. clarifai_utils/client/search.py +173 -0
  59. clarifai_utils/client/user.py +110 -4
  60. clarifai_utils/client/workflow.py +27 -2
  61. clarifai_utils/constants/search.py +2 -0
  62. clarifai_utils/datasets/upload/loaders/xview_detection.py +1 -1
  63. clarifai_utils/models/model_serving/README.md +3 -3
  64. clarifai_utils/models/model_serving/cli/deploy_cli.py +2 -3
  65. clarifai_utils/models/model_serving/cli/repository.py +3 -5
  66. clarifai_utils/models/model_serving/constants.py +1 -5
  67. clarifai_utils/models/model_serving/docs/custom_config.md +5 -6
  68. clarifai_utils/models/model_serving/docs/dependencies.md +5 -10
  69. clarifai_utils/models/model_serving/examples/image_classification/age_vit/requirements.txt +1 -0
  70. clarifai_utils/models/model_serving/examples/text_classification/xlm-roberta/requirements.txt +1 -0
  71. clarifai_utils/models/model_serving/examples/text_to_image/sd-v1.5/requirements.txt +1 -0
  72. clarifai_utils/models/model_serving/examples/text_to_text/bart-summarize/requirements.txt +1 -0
  73. clarifai_utils/models/model_serving/examples/visual_detection/yolov5x/requirements.txt +1 -1
  74. clarifai_utils/models/model_serving/examples/visual_embedding/vit-base/requirements.txt +1 -0
  75. clarifai_utils/models/model_serving/examples/visual_segmentation/segformer-b2/requirements.txt +1 -0
  76. clarifai_utils/models/model_serving/model_config/__init__.py +2 -0
  77. clarifai_utils/models/model_serving/model_config/config.py +298 -0
  78. clarifai_utils/models/model_serving/model_config/model_types_config/text-classifier.yaml +18 -0
  79. clarifai_utils/models/model_serving/model_config/model_types_config/text-embedder.yaml +18 -0
  80. clarifai_utils/models/model_serving/model_config/model_types_config/text-to-image.yaml +18 -0
  81. clarifai_utils/models/model_serving/model_config/model_types_config/text-to-text.yaml +18 -0
  82. clarifai_utils/models/model_serving/model_config/model_types_config/visual-classifier.yaml +18 -0
  83. clarifai_utils/models/model_serving/model_config/model_types_config/visual-detector.yaml +28 -0
  84. clarifai_utils/models/model_serving/model_config/model_types_config/visual-embedder.yaml +18 -0
  85. clarifai_utils/models/model_serving/model_config/model_types_config/visual-segmenter.yaml +18 -0
  86. clarifai_utils/models/model_serving/model_config/serializer.py +1 -1
  87. clarifai_utils/models/model_serving/models/default_test.py +22 -21
  88. clarifai_utils/models/model_serving/models/output.py +2 -2
  89. clarifai_utils/models/model_serving/pb_model_repository.py +2 -5
  90. clarifai_utils/runners/__init__.py +0 -0
  91. clarifai_utils/runners/example.py +33 -0
  92. clarifai_utils/schema/search.py +60 -0
  93. clarifai_utils/utils/logging.py +53 -3
  94. clarifai_utils/versions.py +1 -1
  95. clarifai_utils/workflows/__init__.py +0 -0
  96. clarifai_utils/workflows/export.py +68 -0
  97. clarifai_utils/workflows/utils.py +59 -0
  98. clarifai_utils/workflows/validate.py +67 -0
  99. clarifai/models/model_serving/envs/triton_conda-cp3.8-torch1.13.1-19f97078.yaml +0 -35
  100. clarifai/models/model_serving/envs/triton_conda-cp3.8-torch2.0.0-ce980f28.yaml +0 -51
  101. clarifai/models/model_serving/examples/image_classification/age_vit/triton_conda.yaml +0 -1
  102. clarifai/models/model_serving/examples/text_classification/xlm-roberta/triton_conda.yaml +0 -1
  103. clarifai/models/model_serving/examples/text_to_image/sd-v1.5/triton_conda.yaml +0 -1
  104. clarifai/models/model_serving/examples/text_to_text/bart-summarize/triton_conda.yaml +0 -1
  105. clarifai/models/model_serving/examples/visual_detection/yolov5x/triton_conda.yaml +0 -1
  106. clarifai/models/model_serving/examples/visual_embedding/vit-base/triton_conda.yaml +0 -1
  107. clarifai/models/model_serving/examples/visual_segmentation/segformer-b2/triton_conda.yaml +0 -1
  108. clarifai/models/model_serving/model_config/deploy.py +0 -75
  109. clarifai/models/model_serving/model_config/triton_config.py +0 -226
  110. clarifai_utils/models/model_serving/envs/triton_conda-cp3.8-torch1.13.1-19f97078.yaml +0 -35
  111. clarifai_utils/models/model_serving/envs/triton_conda-cp3.8-torch2.0.0-ce980f28.yaml +0 -51
  112. clarifai_utils/models/model_serving/examples/image_classification/age_vit/triton_conda.yaml +0 -1
  113. clarifai_utils/models/model_serving/examples/text_classification/xlm-roberta/triton_conda.yaml +0 -1
  114. clarifai_utils/models/model_serving/examples/text_to_image/sd-v1.5/triton_conda.yaml +0 -1
  115. clarifai_utils/models/model_serving/examples/text_to_text/bart-summarize/triton_conda.yaml +0 -1
  116. clarifai_utils/models/model_serving/examples/visual_detection/yolov5x/triton_conda.yaml +0 -1
  117. clarifai_utils/models/model_serving/examples/visual_embedding/vit-base/triton_conda.yaml +0 -1
  118. clarifai_utils/models/model_serving/examples/visual_segmentation/segformer-b2/triton_conda.yaml +0 -1
  119. clarifai_utils/models/model_serving/model_config/deploy.py +0 -75
  120. clarifai_utils/models/model_serving/model_config/triton_config.py +0 -226
  121. {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/LICENSE +0 -0
  122. {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/WHEEL +0 -0
  123. {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/entry_points.txt +0 -0
  124. {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/top_level.txt +0 -0
@@ -29,13 +29,18 @@ ClarifaiDatasetType = TypeVar('ClarifaiDatasetType', VisualClassificationDataset
29
29
  class Dataset(Lister, BaseClient):
30
30
  """Dataset is a class that provides access to Clarifai API endpoints related to Dataset information."""
31
31
 
32
- def __init__(self, url_init: str = "", dataset_id: str = "", **kwargs):
32
+ def __init__(self,
33
+ url_init: str = "",
34
+ dataset_id: str = "",
35
+ base_url: str = "https://api.clarifai.com",
36
+ **kwargs):
33
37
  """Initializes a Dataset object.
34
38
 
35
39
  Args:
36
40
  url_init (str): The URL to initialize the dataset object.
37
41
  dataset_id (str): The Dataset ID within the App to interact with.
38
- **kwargs: Additional keyword arguments to be passed to the ClarifaiAuthHelper.
42
+ base_url (str): Base API url. Default "https://api.clarifai.com"
43
+ **kwargs: Additional keyword arguments to be passed to the Dataset.
39
44
  """
40
45
  if url_init != "" and dataset_id != "":
41
46
  raise UserError("You can only specify one of url_init or dataset_id.")
@@ -51,7 +56,7 @@ class Dataset(Lister, BaseClient):
51
56
  self.chunk_size = 128 # limit max protos in a req
52
57
  self.task = None # Upload dataset type
53
58
  self.input_object = Inputs(user_id=self.user_id, app_id=self.app_id)
54
- BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id)
59
+ BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id, base=base_url)
55
60
  Lister.__init__(self)
56
61
 
57
62
  def _concurrent_annot_upload(self, annots: List[List[resources_pb2.Annotation]]
@@ -23,12 +23,18 @@ from clarifai.utils.misc import BackoffIterator, Chunker
23
23
  class Inputs(Lister, BaseClient):
24
24
  """Inputs is a class that provides access to Clarifai API endpoints related to Input information."""
25
25
 
26
- def __init__(self, user_id: str = "", app_id: str = "", logger_level: str = "INFO", **kwargs):
26
+ def __init__(self,
27
+ user_id: str = "",
28
+ app_id: str = "",
29
+ logger_level: str = "INFO",
30
+ base_url: str = "https://api.clarifai.com",
31
+ **kwargs):
27
32
  """Initializes an Input object.
28
33
 
29
34
  Args:
30
35
  user_id (str): A user ID for authentication.
31
36
  app_id (str): An app ID for the application to interact with.
37
+ base_url (str): Base API url. Default "https://api.clarifai.com"
32
38
  **kwargs: Additional keyword arguments to be passed to the Input
33
39
  """
34
40
  self.user_id = user_id
@@ -36,7 +42,7 @@ class Inputs(Lister, BaseClient):
36
42
  self.kwargs = {**kwargs}
37
43
  self.input_info = resources_pb2.Input(**self.kwargs)
38
44
  self.logger = get_logger(logger_level=logger_level, name=__name__)
39
- BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id)
45
+ BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id, base=base_url)
40
46
  Lister.__init__(self)
41
47
 
42
48
  def _get_proto(self,
@@ -121,8 +127,8 @@ class Inputs(Lister, BaseClient):
121
127
  Input: An Input object for the specified input ID.
122
128
 
123
129
  Example:
124
- >>> from clarifai.client.input import Input
125
- >>> input_obj = Input()
130
+ >>> from clarifai.client.input import Inputs
131
+ >>> input_obj = Inputs()
126
132
  >>> input_proto = input_obj.get_input_from_url(input_id = 'demo', image_url='https://samples.clarifai.com/metro-north.jpg')
127
133
  """
128
134
  if not any((image_url, video_url, audio_url, text_url)):
@@ -163,8 +169,8 @@ class Inputs(Lister, BaseClient):
163
169
  Input: An Input object for the specified input ID.
164
170
 
165
171
  Example:
166
- >>> from clarifai.client.input import Input
167
- >>> input_obj = Input()
172
+ >>> from clarifai.client.input import Inputs
173
+ >>> input_obj = Inputs()
168
174
  >>> input_proto = input_obj.get_input_from_file(input_id = 'demo', video_file='file_path')
169
175
  """
170
176
  if not any((image_file, video_file, audio_file, text_file)):
@@ -205,8 +211,8 @@ class Inputs(Lister, BaseClient):
205
211
  Input: An Input object for the specified input ID.
206
212
 
207
213
  Example:
208
- >>> from clarifai.client.input import Input
209
- >>> input_obj = Input()
214
+ >>> from clarifai.client.input import Inputs
215
+ >>> input_obj = Inputs()
210
216
  >>> image = open('demo.jpg', 'rb').read()
211
217
  >>> video = open('demo.mp4', 'rb').read()
212
218
  >>> input_proto = input_obj.get_input_from_bytes(input_id = 'demo',image_bytes =image, video_bytes=video)
@@ -240,8 +246,8 @@ class Inputs(Lister, BaseClient):
240
246
  list of Input: A list of Input objects for the specified folder.
241
247
 
242
248
  Example:
243
- >>> from clarifai.client.input import Input
244
- >>> input_obj = Input()
249
+ >>> from clarifai.client.input import Inputs
250
+ >>> input_obj = Inputs()
245
251
  >>> input_protos = input_obj.get_image_inputs_from_folder(folder_path='demo_folder')
246
252
  """
247
253
  input_protos = []
@@ -270,8 +276,8 @@ class Inputs(Lister, BaseClient):
270
276
  Text: An Input object for the specified input ID.
271
277
 
272
278
  Example:
273
- >>> from clarifai.client.input import Input
274
- >>> input_obj = Input()
279
+ >>> from clarifai.client.input import Inputs
280
+ >>> input_obj = Inputs()
275
281
  >>> input_protos = input_obj.get_text_input(input_id = 'demo', raw_text = 'This is a test')
276
282
  """
277
283
  text_pb = resources_pb2.Text(raw=raw_text)
@@ -296,8 +302,8 @@ class Inputs(Lister, BaseClient):
296
302
  inputs: List of inputs
297
303
 
298
304
  Example:
299
- >>> from clarifai.client.input import Input
300
- >>> input_obj = Input()
305
+ >>> from clarifai.client.input import Inputs
306
+ >>> input_obj = Inputs()
301
307
  >>> input_protos = input_obj.get_inputs_from_csv(csv_path='filepath', input_type='text', csv_type='raw')
302
308
  """
303
309
  input_protos = []
@@ -357,8 +363,8 @@ class Inputs(Lister, BaseClient):
357
363
  list of Input: A list of Input objects for the specified folder.
358
364
 
359
365
  Example:
360
- >>> from clarifai.client.input import Input
361
- >>> input_obj = Input()
366
+ >>> from clarifai.client.input import Inputs
367
+ >>> input_obj = Inputs()
362
368
  >>> input_protos = input_obj.get_text_inputs_from_folder(folder_path='demo_folder')
363
369
  """
364
370
  input_protos = []
@@ -385,8 +391,8 @@ class Inputs(Lister, BaseClient):
385
391
  An annotation object for the specified input ID.
386
392
 
387
393
  Example:
388
- >>> from clarifai.client.input import Input
389
- >>> input_obj = Input()
394
+ >>> from clarifai.client.input import Inputs
395
+ >>> input_obj = Inputs()
390
396
  >>> input_obj.get_annotation_proto(input_id='demo', label='demo', annotations=[x_min, y_min, x_max, y_max])
391
397
  """
392
398
  if not isinstance(annotations, list):
@@ -424,8 +430,8 @@ class Inputs(Lister, BaseClient):
424
430
  An annotation object for the specified input ID.
425
431
 
426
432
  Example:
427
- >>> from clarifai.client.input import Input
428
- >>> input_obj = Input()
433
+ >>> from clarifai.client.input import Inputs
434
+ >>> input_obj = Inputs()
429
435
  >>> input_obj.get_mask_proto(input_id='demo', label='demo', polygons=[[[x,y],...,[x,y]],...])
430
436
  """
431
437
  if not isinstance(polygons, list):
@@ -471,8 +477,8 @@ class Inputs(Lister, BaseClient):
471
477
  input_job_id: job id for the upload request.
472
478
 
473
479
  Example:
474
- >>> from clarifai.client.input import Input
475
- >>> input_obj = Input(user_id = 'user_id', app_id = 'demo_app')
480
+ >>> from clarifai.client.input import Inputs
481
+ >>> input_obj = Inputs(user_id = 'user_id', app_id = 'demo_app')
476
482
  >>> input_obj.upload_from_url(input_id='demo', image_url='https://samples.clarifai.com/metro-north.jpg')
477
483
  """
478
484
  input_pb = self.get_input_from_url(input_id, image_url, video_url, audio_url, text_url,
@@ -501,8 +507,8 @@ class Inputs(Lister, BaseClient):
501
507
  input_job_id: job id for the upload request.
502
508
 
503
509
  Example:
504
- >>> from clarifai.client.input import Input
505
- >>> input_obj = Input(user_id = 'user_id', app_id = 'demo_app')
510
+ >>> from clarifai.client.input import Inputs
511
+ >>> input_obj = Inputs(user_id = 'user_id', app_id = 'demo_app')
506
512
  >>> input_obj.upload_from_file(input_id='demo', audio_file='demo.mp3')
507
513
  """
508
514
  input_pb = self.get_input_from_file(input_id, image_file, video_file, audio_file, text_file,
@@ -531,8 +537,8 @@ class Inputs(Lister, BaseClient):
531
537
  input_job_id: job id for the upload request.
532
538
 
533
539
  Example:
534
- >>> from clarifai.client.input import Input
535
- >>> input_obj = Input(user_id = 'user_id', app_id = 'demo_app')
540
+ >>> from clarifai.client.input import Inputs
541
+ >>> input_obj = Inputs(user_id = 'user_id', app_id = 'demo_app')
536
542
  >>> image = open('demo.jpg', 'rb').read()
537
543
  >>> input_obj.upload_from_bytes(input_id='demo', image_bytes=image)
538
544
  """
@@ -553,8 +559,8 @@ class Inputs(Lister, BaseClient):
553
559
  input_job_id (str): job id for the upload request.
554
560
 
555
561
  Example:
556
- >>> from clarifai.client.input import Input
557
- >>> input_obj = Input(user_id = 'user_id', app_id = 'demo_app')
562
+ >>> from clarifai.client.input import Inputs
563
+ >>> input_obj = Inputs(user_id = 'user_id', app_id = 'demo_app')
558
564
  >>> input_obj.upload_text(input_id = 'demo', raw_text = 'This is a test')
559
565
  """
560
566
  input_pb = self._get_proto(
@@ -5,6 +5,7 @@ from typing import Dict, List
5
5
  from clarifai_grpc.grpc.api import resources_pb2, service_pb2
6
6
  from clarifai_grpc.grpc.api.resources_pb2 import Input
7
7
  from clarifai_grpc.grpc.api.status import status_code_pb2
8
+ from google.protobuf.json_format import MessageToDict
8
9
 
9
10
  from clarifai.client.base import BaseClient
10
11
  from clarifai.client.lister import Lister
@@ -22,6 +23,7 @@ class Model(Lister, BaseClient):
22
23
  model_id: str = "",
23
24
  model_version: Dict = {'id': ""},
24
25
  output_config: Dict = {'min_value': 0},
26
+ base_url: str = "https://api.clarifai.com",
25
27
  **kwargs):
26
28
  """Initializes a Model object.
27
29
 
@@ -34,7 +36,8 @@ class Model(Lister, BaseClient):
34
36
  max_concepts (int): The maximum number of concepts to return.
35
37
  select_concepts (list[Concept]): The concepts to select.
36
38
  sample_ms (int): The number of milliseconds to sample.
37
- **kwargs: Additional keyword arguments to be passed to the ClarifaiAuthHelper.
39
+ base_url (str): Base API url. Default "https://api.clarifai.com"
40
+ **kwargs: Additional keyword arguments to be passed to the Model.
38
41
  """
39
42
  if url_init != "" and model_id != "":
40
43
  raise UserError("You can only specify one of url_init or model_id.")
@@ -49,9 +52,75 @@ class Model(Lister, BaseClient):
49
52
  'output_info': {'output_config': output_config}}
50
53
  self.model_info = resources_pb2.Model(**self.kwargs)
51
54
  self.logger = get_logger(logger_level="INFO")
52
- BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id)
55
+ BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id, base=base_url)
53
56
  Lister.__init__(self)
54
57
 
58
+ def create_model_version(self, **kwargs) -> 'Model':
59
+ """Creates a model version for the Model.
60
+
61
+ Args:
62
+ **kwargs: Additional keyword arguments to be passed to Model Version.
63
+ - description (str): The description of the model version.
64
+ - concepts (list[Concept]): The concepts to associate with the model version.
65
+ - output_info (resources_pb2.OutputInfo(): The output info to associate with the model version.
66
+
67
+ Returns:
68
+ Model: A Model object for the specified model ID.
69
+
70
+ Example:
71
+ >>> from clarifai.client.model import Model
72
+ >>> model = Model("model_url")
73
+ or
74
+ >>> model = Model(model_id='model_id', user_id='user_id', app_id='app_id')
75
+ >>> model_version = model.create_model_version(description='model_version_description')
76
+ """
77
+ request = service_pb2.PostModelVersionsRequest(
78
+ user_app_id=self.user_app_id,
79
+ model_id=self.id,
80
+ model_versions=[resources_pb2.ModelVersion(**kwargs)])
81
+
82
+ response = self._grpc_request(self.STUB.PostModelVersions, request)
83
+ if response.status.code != status_code_pb2.SUCCESS:
84
+ raise Exception(response.status)
85
+ self.logger.info("\nModel Version created\n%s", response.status)
86
+
87
+ kwargs.update({'app_id': self.app_id, 'user_id': self.user_id})
88
+ dict_response = MessageToDict(response, preserving_proto_field_name=True)
89
+ kwargs = self.process_response_keys(dict_response['model'], 'model')
90
+
91
+ return Model(**kwargs)
92
+
93
+ def list_versions(self) -> List['Model']:
94
+ """Lists all the versions for the model.
95
+
96
+ Returns:
97
+ List[Model]: A list of Model objects for the versions of the model.
98
+
99
+ Example:
100
+ >>> from clarifai.client.model import Model
101
+ >>> model = Model("model_url") # Example URL: https://clarifai.com/clarifai/main/models/general-image-recognition
102
+ or
103
+ >>> model = Model(model_id='model_id', user_id='user_id', app_id='app_id')
104
+ >>> all_model_versions = model.list_versions()
105
+ """
106
+ request_data = dict(
107
+ user_app_id=self.user_app_id,
108
+ model_id=self.id,
109
+ per_page=self.default_page_size,
110
+ )
111
+ all_model_versions_info = list(
112
+ self.list_all_pages_generator(self.STUB.ListModelVersions,
113
+ service_pb2.ListModelVersionsRequest, request_data))
114
+
115
+ for model_version_info in all_model_versions_info:
116
+ model_version_info['id'] = model_version_info['model_version_id']
117
+ del model_version_info['model_version_id']
118
+
119
+ return [
120
+ Model(model_id=self.id, **dict(self.kwargs, model_version=model_version_info))
121
+ for model_version_info in all_model_versions_info
122
+ ]
123
+
55
124
  def predict(self, inputs: List[Input]):
56
125
  """Predicts the model based on the given inputs.
57
126
 
@@ -16,6 +16,7 @@ class Module(Lister, BaseClient):
16
16
  url_init: str = "",
17
17
  module_id: str = "",
18
18
  module_version: Dict = {'id': ""},
19
+ base_url: str = "https://api.clarifai.com",
19
20
  **kwargs):
20
21
  """Initializes a Module object.
21
22
 
@@ -23,7 +24,8 @@ class Module(Lister, BaseClient):
23
24
  url_init (str): The URL to initialize the module object.
24
25
  module_id (str): The Module ID to interact with.
25
26
  module_version (dict): The Module Version to interact with.
26
- **kwargs: Additional keyword arguments to be passed to the ClarifaiAuthHelper.
27
+ base_url (str): Base API url. Default "https://api.clarifai.com"
28
+ **kwargs: Additional keyword arguments to be passed to the Module.
27
29
  """
28
30
  if url_init != "" and module_id != "":
29
31
  raise UserError("You can only specify one of url_init or module_id.")
@@ -38,7 +40,7 @@ class Module(Lister, BaseClient):
38
40
  self.kwargs = {**kwargs, 'id': module_id, 'module_version': module_version}
39
41
  self.module_info = resources_pb2.Module(**self.kwargs)
40
42
  self.logger = get_logger(logger_level="INFO")
41
- BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id)
43
+ BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id, base=base_url)
42
44
  Lister.__init__(self)
43
45
 
44
46
  def list_versions(self) -> List['Module']:
@@ -0,0 +1,161 @@
1
+ # Copyright 2023 Clarifai, Inc.
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ """Interface to Clarifai Runners API."""
14
+
15
+ import os
16
+
17
+ from clarifai_grpc.grpc.api import resources_pb2, service_pb2
18
+ from clarifai_grpc.grpc.api.status import status_code_pb2, status_pb2
19
+ from google.protobuf import json_format
20
+
21
+ from clarifai.client.base import BaseClient
22
+ from clarifai.errors import UserError
23
+ from clarifai.utils.logging import get_logger
24
+
25
+
26
+ class Runner(BaseClient):
27
+ """Base class for remote inference runners. This should be subclassed with the run_input method
28
+ implemented to process each input in the request.
29
+
30
+ Then on the subclass call start() to start the run loop.
31
+ """
32
+
33
+ def __init__(self,
34
+ runner_id: str,
35
+ user_id: str = "",
36
+ check_runner_exists: bool = True,
37
+ base_url: str = "https://api.clarifai.com",
38
+ **kwargs) -> None:
39
+ """
40
+ Args:
41
+ runner_id (str): the id of the runner to use. Create the runner in the Clarifai API first
42
+ user_id (str): Clarifai User ID
43
+ base_url (str): Base API url. Default "https://api.clarifai.com"
44
+ """
45
+ user_id = user_id or os.environ.get("CLARIFAI_USER_ID", "")
46
+
47
+ if user_id == "":
48
+ raise UserError(
49
+ "Set CLARIFAI_USER_ID as environment variables or pass user_id as input arguments")
50
+
51
+ self.runner_id = runner_id
52
+ self.logger = get_logger("INFO", __name__)
53
+ self.kwargs = {**kwargs, 'id': runner_id, 'user_id': user_id}
54
+ self.runner_info = resources_pb2.Runner(**self.kwargs)
55
+ BaseClient.__init__(self, user_id=self.user_id, app_id="", base=base_url)
56
+
57
+ # Check that the runner exists.
58
+ if check_runner_exists:
59
+ request = service_pb2.GetRunnerRequest(user_app_id=self.user_app_id, runner_id=runner_id)
60
+ response = self._grpc_request(self.STUB.GetRunner, request)
61
+ if response.status.code != status_code_pb2.SUCCESS:
62
+ raise Exception(
63
+ f"""Error getting runner, are you use this is a valid runner id {runner_id} at the user_id
64
+ {self.user_app_id.user_id}.
65
+ Error: {response.status.description}""")
66
+
67
+ def start(self):
68
+ """Start the run loop. This will ask the Clarifai API for work, and when it gets work, it will run
69
+ the model on the inputs and post the results back to the Clarifai API. It will then ask for more
70
+ work again.
71
+ """
72
+ self._long_poll_loop()
73
+
74
+ def _run(self, request: service_pb2.PostModelOutputsRequest) -> service_pb2.MultiOutputResponse:
75
+ """Run the model on the given request. You shouldn't need to override this method, see run_input
76
+ for the implementation to process each input in the request.
77
+
78
+ Args:
79
+ request: service_pb2.PostModelOutputsRequest - the request to run the model on
80
+
81
+ Returns:
82
+ service_pb2.MultiOutputResponse - the response from the model's run_input implementation.
83
+ """
84
+ outputs = []
85
+ # TODO: parallelize this
86
+ for inp in request.inputs:
87
+ # TODO: handle errors
88
+ outputs.append(self.run_input(inp))
89
+
90
+ return service_pb2.MultiOutputResponse(
91
+ status=status_pb2.Status(
92
+ code=status_code_pb2.SUCCESS,
93
+ description="Success",
94
+ ),
95
+ outputs=outputs,
96
+ )
97
+
98
+ def run_input(self, input: resources_pb2.Input) -> resources_pb2.Output:
99
+ """Run the model on the given input in the request. This is the method you should override to
100
+ process each input in the request.
101
+
102
+ Args:
103
+ input: resources_pb2.Input - the input to run the model on
104
+
105
+ Returns:
106
+ resources_pb2.Output - the response from the model's run_input implementation.
107
+ """
108
+ raise NotImplementedError("run_input() not implemented")
109
+
110
+ def _long_poll_loop(self):
111
+ """This method will long poll for work, and when it gets work, it will run the model on the inputs
112
+ and post the results back to the Clarifai API. It will then long poll again for more work.
113
+ """
114
+ c = 0
115
+ # TODO: handle more errors within this loop so it never stops.
116
+ # TODO: perhaps have multiple processes running this loop to handle more work.
117
+ while True:
118
+ # Long poll waiting for work.
119
+ self.logger.info("Loop iteration: {}".format(c))
120
+ request = service_pb2.ListRunnerItemsRequest(
121
+ user_app_id=self.user_app_id, runner_id=self.runner_id)
122
+ work_response = self._grpc_request(self.STUB.ListRunnerItems, request)
123
+ if work_response.status.code == status_code_pb2.RUNNER_NEEDS_RETRY:
124
+ c += 1
125
+ continue # immediate restart the long poll
126
+ if work_response.status.code != status_code_pb2.SUCCESS:
127
+ raise Exception("Error getting work: {}".format(work_response.status.description))
128
+ if len(work_response.items) == 0:
129
+ self.logger.info("No work to do. Waiting...")
130
+ continue
131
+
132
+ # We have work to do. Run the model on the inputs.
133
+ for item in work_response.items:
134
+ if not item.HasField('post_model_outputs_request'):
135
+ raise Exception("Unexpected work item type: {}".format(item))
136
+ self.logger.info(
137
+ f"Working on item: {item.id} with inputs {len(item.post_model_outputs_request.inputs)}"
138
+ )
139
+ result = self._run(item.post_model_outputs_request)
140
+
141
+ request = service_pb2.PostRunnerItemOutputsRequest(
142
+ user_app_id=self.user_app_id,
143
+ item_id=item.id,
144
+ runner_id=self.runner_id,
145
+ runner_item_outputs=[service_pb2.RunnerItemOutput(multi_output_response=result)])
146
+ result_response = self._grpc_request(self.STUB.PostRunnerItemOutputs, request)
147
+ if result_response.status.code != status_code_pb2.SUCCESS:
148
+ raise Exception(
149
+ json_format.MessageToJson(result_response, preserving_proto_field_name=True))
150
+ # raise Exception("Error posting result: {}".format(result_response.status.description))
151
+
152
+ def __getattr__(self, name):
153
+ return getattr(self.runner_info, name)
154
+
155
+ def __str__(self):
156
+ init_params = [param for param in self.kwargs.keys()]
157
+ attribute_strings = [
158
+ f"{param}={getattr(self.runner_info, param)}" for param in init_params
159
+ if hasattr(self.runner_info, param)
160
+ ]
161
+ return f"Runner Details: \n{', '.join(attribute_strings)}\n"
@@ -0,0 +1,173 @@
1
+ from typing import Any, Callable, Dict, Generator
2
+
3
+ from clarifai_grpc.grpc.api import resources_pb2, service_pb2
4
+ from clarifai_grpc.grpc.api.status import status_code_pb2
5
+ from google.protobuf.json_format import MessageToDict
6
+ from google.protobuf.struct_pb2 import Struct
7
+ from schema import SchemaError
8
+
9
+ from clarifai.client.base import BaseClient
10
+ from clarifai.client.input import Inputs
11
+ from clarifai.client.lister import Lister
12
+ from clarifai.constants.search import DEFAULT_SEARCH_METRIC, DEFAULT_TOP_K
13
+ from clarifai.errors import UserError
14
+ from clarifai.schema.search import get_schema
15
+
16
+
17
+ class Search(Lister, BaseClient):
18
+
19
+ def __init__(self,
20
+ user_id,
21
+ app_id,
22
+ top_k: int = DEFAULT_TOP_K,
23
+ metric: str = DEFAULT_SEARCH_METRIC):
24
+ """Initialize the Search object.
25
+
26
+ Args:
27
+ user_id (str): User ID.
28
+ app_id (str): App ID.
29
+ top_k (int, optional): Top K results to retrieve. Defaults to 10.
30
+ metric (str, optional): Similarity metric (either 'cosine' or 'euclidean'). Defaults to 'cosine'.
31
+
32
+ Raises:
33
+ UserError: If the metric is not 'cosine' or 'euclidean'.
34
+ """
35
+ if metric not in ["cosine", "euclidean"]:
36
+ raise UserError("Metric should be either cosine or euclidean")
37
+
38
+ self.user_id = user_id
39
+ self.app_id = app_id
40
+ self.metric_distance = dict(cosine="COSINE_DISTANCE", euclidean="EUCLIDEAN_DISTANCE")[metric]
41
+ self.data_proto = resources_pb2.Data()
42
+
43
+ self.inputs = Inputs(user_id=self.user_id, app_id=self.app_id)
44
+ self.rank_filter_schema = get_schema()
45
+ BaseClient.__init__(self, user_id=self.user_id, app_id=self.app_id)
46
+ Lister.__init__(self, page_size=top_k)
47
+
48
+ def _get_annot_proto(self, **kwargs):
49
+ """Get an Annotation proto message based on keyword arguments.
50
+
51
+ Args:
52
+ **kwargs: Keyword arguments specifying the resource.
53
+
54
+ Returns:
55
+ resources_pb2.Annotation: An Annotation proto message.
56
+ """
57
+ if not kwargs:
58
+ return resources_pb2.Annotation()
59
+
60
+ self.data_proto = resources_pb2.Data()
61
+ for key, value in kwargs.items():
62
+ if key == "image_bytes":
63
+ image_proto = self.inputs.get_input_from_bytes("", image_bytes=value).data.image
64
+ self.data_proto.image.CopyFrom(image_proto)
65
+
66
+ elif key == "image_url":
67
+ image_proto = self.inputs.get_input_from_url("", image_url=value).data.image
68
+ self.data_proto.image.CopyFrom(image_proto)
69
+
70
+ elif key == "concepts":
71
+ for concept in value:
72
+ concept_proto = resources_pb2.Concept(**concept)
73
+ self.data_proto.concepts.add().CopyFrom(concept_proto)
74
+
75
+ elif key == "text_raw":
76
+ text_proto = self.inputs.get_input_from_bytes(
77
+ "", text_bytes=bytes(value, 'utf-8')).data.text
78
+ self.data_proto.text.CopyFrom(text_proto)
79
+
80
+ elif key == "metadata":
81
+ metadata_struct = Struct()
82
+ metadata_struct.update(value)
83
+ self.data_proto.metadata.CopyFrom(metadata_struct)
84
+
85
+ elif key == "geo_point":
86
+ geo_point_proto = self._get_geo_point_proto(value["longitude"], value["latitude"],
87
+ value["geo_limit"])
88
+ self.data_proto.geo.CopyFrom(geo_point_proto)
89
+
90
+ else:
91
+ raise UserError(f"kwargs contain key that is not supported: {key}")
92
+ return resources_pb2.Annotation(data=self.data_proto)
93
+
94
+ def _get_geo_point_proto(self, longitude: float, latitude: float,
95
+ geo_limit: float) -> resources_pb2.Geo:
96
+ """Get a GeoPoint proto message based on geographical data.
97
+
98
+ Args:
99
+ longitude (float): Longitude coordinate.
100
+ latitude (float): Latitude coordinate.
101
+ geo_limit (float): Geographical limit.
102
+
103
+ Returns:
104
+ resources_pb2.Geo: A Geo proto message.
105
+ """
106
+ return resources_pb2.Geo(
107
+ geo_point=resources_pb2.GeoPoint(longitude=longitude, latitude=latitude),
108
+ geo_limit=resources_pb2.GeoLimit(type="withinKilometers", value=geo_limit))
109
+
110
+ def list_all_pages_generator(
111
+ self, endpoint: Callable[..., Any], proto_message: Any,
112
+ request_data: Dict[str, Any]) -> Generator[Dict[str, Any], None, None]:
113
+ """Lists all pages of a resource.
114
+
115
+ Args:
116
+ endpoint (Callable): The endpoint to call.
117
+ proto_message (Any): The proto message to use.
118
+ request_data (dict): The request data to use.
119
+
120
+ Yields:
121
+ response_dict: The next item in the listing.
122
+ """
123
+ page = 1
124
+ request_data['pagination'] = service_pb2.Pagination(page=page, per_page=self.default_page_size)
125
+ while True:
126
+ request_data['pagination'].page = page
127
+ response = self._grpc_request(endpoint, proto_message(**request_data))
128
+ dict_response = MessageToDict(response, preserving_proto_field_name=True)
129
+ if response.status.code != status_code_pb2.SUCCESS:
130
+ raise Exception(f"Listing failed with response {response!r}")
131
+
132
+ if 'hits' not in list(dict_response.keys()):
133
+ break
134
+ page += 1
135
+ yield response
136
+
137
+ def query(self, ranks=[{}], filters=[{}]):
138
+ """Perform a query with rank and filters.
139
+
140
+ Args:
141
+ ranks (List[Dict], optional): List of rank parameters. Defaults to [{}].
142
+ filters (List[Dict], optional): List of filter parameters. Defaults to [{}].
143
+
144
+ Returns:
145
+ Generator[Dict[str, Any], None, None]: A generator of query results.
146
+ """
147
+ try:
148
+ self.rank_filter_schema.validate(ranks)
149
+ self.rank_filter_schema.validate(filters)
150
+ except SchemaError as err:
151
+ raise UserError(f"Invalid rank or filter input: {err}")
152
+
153
+ rank_annot_proto, filters_annot_proto = [], []
154
+ for rank_dict in ranks:
155
+ rank_annot_proto.append(self._get_annot_proto(**rank_dict))
156
+ for filter_dict in filters:
157
+ filters_annot_proto.append(self._get_annot_proto(**filter_dict))
158
+
159
+ all_ranks = [resources_pb2.Rank(annotation=rank_annot) for rank_annot in rank_annot_proto]
160
+ all_filters = [
161
+ resources_pb2.Filter(annotation=filter_annot) for filter_annot in filters_annot_proto
162
+ ]
163
+
164
+ request_data = dict(
165
+ user_app_id=self.user_app_id,
166
+ searches=[
167
+ resources_pb2.Search(
168
+ query=resources_pb2.Query(ranks=all_ranks, filters=all_filters),
169
+ metric=self.metric_distance)
170
+ ])
171
+
172
+ return self.list_all_pages_generator(self.STUB.PostAnnotationsSearches,
173
+ service_pb2.PostAnnotationsSearchesRequest, request_data)