clarifai 10.10.0__tar.gz → 10.11.0__tar.gz

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 (120) hide show
  1. {clarifai-10.10.0/clarifai.egg-info → clarifai-10.11.0}/PKG-INFO +2 -2
  2. clarifai-10.11.0/clarifai/__init__.py +1 -0
  3. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/model.py +7 -2
  4. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/dataset.py +21 -0
  5. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/deployment.py +2 -1
  6. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/input.py +27 -0
  7. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/model.py +1 -1
  8. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/export/inputs_annotations.py +8 -0
  9. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/multimodal.py +0 -2
  10. clarifai-10.10.0/clarifai/runners/dockerfile_template/Dockerfile.cpu.template → clarifai-10.11.0/clarifai/runners/dockerfile_template/Dockerfile.template +21 -11
  11. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/base_typed_model.py +6 -3
  12. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/model_run_locally.py +24 -9
  13. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/model_upload.py +101 -13
  14. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/utils/url_fetcher.py +19 -12
  15. {clarifai-10.10.0 → clarifai-10.11.0/clarifai.egg-info}/PKG-INFO +2 -2
  16. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai.egg-info/SOURCES.txt +1 -2
  17. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai.egg-info/requires.txt +1 -1
  18. {clarifai-10.10.0 → clarifai-10.11.0}/pyproject.toml +1 -1
  19. {clarifai-10.10.0 → clarifai-10.11.0}/requirements.txt +1 -1
  20. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_app.py +22 -11
  21. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_data_upload.py +9 -5
  22. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_eval.py +6 -5
  23. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_model_predict.py +26 -35
  24. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_model_train.py +6 -5
  25. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_rag.py +8 -2
  26. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_search.py +18 -4
  27. clarifai-10.10.0/clarifai/__init__.py +0 -1
  28. clarifai-10.10.0/clarifai/runners/dockerfile_template/Dockerfile.cuda.template +0 -83
  29. {clarifai-10.10.0 → clarifai-10.11.0}/LICENSE +0 -0
  30. {clarifai-10.10.0 → clarifai-10.11.0}/MANIFEST.in +0 -0
  31. {clarifai-10.10.0 → clarifai-10.11.0}/README.md +0 -0
  32. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/README.md +0 -0
  33. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/__init__.py +0 -0
  34. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/base.py +0 -0
  35. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/compute_cluster.py +0 -0
  36. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/deployment.py +0 -0
  37. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli/nodepool.py +0 -0
  38. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/cli.py +0 -0
  39. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/__init__.py +0 -0
  40. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/app.py +0 -0
  41. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/auth/__init__.py +0 -0
  42. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/auth/helper.py +0 -0
  43. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/auth/register.py +0 -0
  44. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/auth/stub.py +0 -0
  45. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/base.py +0 -0
  46. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/compute_cluster.py +0 -0
  47. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/lister.py +0 -0
  48. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/module.py +0 -0
  49. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/nodepool.py +0 -0
  50. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/search.py +0 -0
  51. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/user.py +0 -0
  52. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/client/workflow.py +0 -0
  53. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/base.py +0 -0
  54. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/dataset.py +0 -0
  55. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/input.py +0 -0
  56. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/model.py +0 -0
  57. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/rag.py +0 -0
  58. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/search.py +0 -0
  59. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/constants/workflow.py +0 -0
  60. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/__init__.py +0 -0
  61. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/export/__init__.py +0 -0
  62. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/__init__.py +0 -0
  63. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/base.py +0 -0
  64. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/features.py +0 -0
  65. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/image.py +0 -0
  66. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/loaders/README.md +0 -0
  67. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/loaders/__init__.py +0 -0
  68. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/loaders/coco_captions.py +0 -0
  69. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/loaders/coco_detection.py +0 -0
  70. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/loaders/imagenet_classification.py +0 -0
  71. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/loaders/xview_detection.py +0 -0
  72. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/text.py +0 -0
  73. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/datasets/upload/utils.py +0 -0
  74. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/errors.py +0 -0
  75. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/models/__init__.py +0 -0
  76. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/models/api.py +0 -0
  77. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/modules/README.md +0 -0
  78. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/modules/__init__.py +0 -0
  79. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/modules/css.py +0 -0
  80. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/modules/pages.py +0 -0
  81. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/modules/style.css +0 -0
  82. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/rag/__init__.py +0 -0
  83. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/rag/rag.py +0 -0
  84. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/rag/utils.py +0 -0
  85. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/__init__.py +0 -0
  86. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/__init__.py +0 -0
  87. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/model_class.py +0 -0
  88. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/model_runner.py +0 -0
  89. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/models/model_servicer.py +0 -0
  90. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/server.py +0 -0
  91. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/utils/__init__.py +0 -0
  92. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/utils/data_handler.py +0 -0
  93. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/utils/data_utils.py +0 -0
  94. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/runners/utils/loader.py +0 -0
  95. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/schema/search.py +0 -0
  96. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/urls/helper.py +0 -0
  97. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/__init__.py +0 -0
  98. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/cli.py +0 -0
  99. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/constants.py +0 -0
  100. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/evaluation/__init__.py +0 -0
  101. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/evaluation/helpers.py +0 -0
  102. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/evaluation/main.py +0 -0
  103. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/evaluation/testset_annotation_parser.py +0 -0
  104. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/logging.py +0 -0
  105. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/misc.py +0 -0
  106. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/utils/model_train.py +0 -0
  107. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/versions.py +0 -0
  108. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/workflows/__init__.py +0 -0
  109. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/workflows/export.py +0 -0
  110. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/workflows/utils.py +0 -0
  111. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai/workflows/validate.py +0 -0
  112. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai.egg-info/dependency_links.txt +0 -0
  113. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai.egg-info/entry_points.txt +0 -0
  114. {clarifai-10.10.0 → clarifai-10.11.0}/clarifai.egg-info/top_level.txt +0 -0
  115. {clarifai-10.10.0 → clarifai-10.11.0}/setup.cfg +0 -0
  116. {clarifai-10.10.0 → clarifai-10.11.0}/setup.py +0 -0
  117. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_auth.py +0 -0
  118. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_misc.py +0 -0
  119. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_modules.py +0 -0
  120. {clarifai-10.10.0 → clarifai-10.11.0}/tests/test_stub.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clarifai
3
- Version: 10.10.0
3
+ Version: 10.11.0
4
4
  Summary: Clarifai Python SDK
5
5
  Home-page: https://github.com/Clarifai/clarifai-python
6
6
  Author: Clarifai
@@ -20,7 +20,7 @@ Classifier: Operating System :: OS Independent
20
20
  Requires-Python: >=3.8
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: clarifai-grpc>=10.9.11
23
+ Requires-Dist: clarifai-grpc>=10.10.2
24
24
  Requires-Dist: clarifai-protocol>=0.0.6
25
25
  Requires-Dist: numpy>=1.22.0
26
26
  Requires-Dist: tqdm>=4.65.0
@@ -0,0 +1 @@
1
+ __version__ = "10.11.0"
@@ -39,11 +39,16 @@ def upload(model_path, download_checkpoints, skip_dockerfile):
39
39
  type=click.Path(exists=True),
40
40
  required=True,
41
41
  help='Path to the model directory.')
42
- def test_locally(model_path):
42
+ @click.option(
43
+ '--keep_env',
44
+ is_flag=True,
45
+ help='Flag to keep the virtual environment after testing the model\
46
+ locally. Defaults to False, which will delete the virtual environment after testing.')
47
+ def test_locally(model_path, keep_env=False):
43
48
  """Test model locally."""
44
49
  try:
45
50
  from clarifai.runners.models import model_run_locally
46
- model_run_locally.main(model_path)
51
+ model_run_locally.main(model_path, keep_env=keep_env)
47
52
  click.echo(f"Model tested locally from {model_path}.")
48
53
  except Exception as e:
49
54
  click.echo(f"Failed to test model locally: {e}", err=True)
@@ -9,6 +9,7 @@ from typing import Dict, Generator, List, Optional, Tuple, Type, TypeVar, Union
9
9
 
10
10
  import requests
11
11
  from clarifai_grpc.grpc.api import resources_pb2, service_pb2
12
+ from clarifai_grpc.grpc.api.resources_pb2 import Input
12
13
  from clarifai_grpc.grpc.api.service_pb2 import MultiInputResponse
13
14
  from clarifai_grpc.grpc.api.status import status_code_pb2, status_pb2
14
15
  from google.protobuf.json_format import MessageToDict
@@ -190,6 +191,26 @@ class Dataset(Lister, BaseClient):
190
191
  }
191
192
  yield Dataset.from_auth_helper(self.auth_helper, **kwargs)
192
193
 
194
+ def list_inputs(self, page_no: int = None, per_page: int = None,
195
+ input_type: str = None) -> Generator[Input, None, None]:
196
+ """Lists all the inputs for the dataset.
197
+
198
+ Args:
199
+ page_no (int): The page number to list.
200
+ per_page (int): The number of items per page.
201
+ input_type (str): The type of input to list. Options: 'image', 'video', 'audio', 'text'.
202
+
203
+ Yields:
204
+ Input: Input objects in the dataset.
205
+
206
+ Example:
207
+ >>> from clarifai.client.dataset import Dataset
208
+ >>> dataset = Dataset(dataset_id='dataset_id', user_id='user_id', app_id='app_id')
209
+ >>> all_dataset_inputs = list(dataset.list_inputs())
210
+ """
211
+ return self.input_object.list_inputs(
212
+ dataset_id=self.id, page_no=page_no, per_page=per_page, input_type=input_type)
213
+
193
214
  def __iter__(self):
194
215
  return iter(DatasetExportReader(archive_url=self.archive_zip()))
195
216
 
@@ -49,7 +49,8 @@ class Deployment(Lister, BaseClient):
49
49
  Returns:
50
50
  resources_pb2.RunnerSelector: A RunnerSelector object for the given deployment_id.
51
51
  """
52
- return resources_pb2.RunnerSelector(deployment_id=deployment_id, user_id=user_id)
52
+ return resources_pb2.RunnerSelector(
53
+ deployment=resources_pb2.Deployment(id=deployment_id, user_id=user_id))
53
54
 
54
55
  def __getattr__(self, name):
55
56
  return getattr(self.deployment_info, name)
@@ -867,6 +867,33 @@ class Inputs(Lister, BaseClient):
867
867
  raise Exception(response.status)
868
868
  self.logger.info("\nInputs Deleted\n%s", response.status)
869
869
 
870
+ def delete_annotations(self, input_ids: List[str], annotation_ids: List[str] = []) -> None:
871
+ """Delete list of annotations of input objects from the app.
872
+
873
+ Args:
874
+ input_ids (Input): List of input objects for which annotations to delete.
875
+ annotation_ids (List[str]): List of annotation ids to delete.
876
+
877
+ Example:
878
+ >>> from clarifai.client.user import User
879
+ >>> input_obj = User(user_id="user_id").app(app_id="app_id").inputs()
880
+ >>> input_obj.delete_annotations(input_ids=['input_id_1', 'input_id_2'])
881
+
882
+ Note:
883
+ 'annotation_ids' are optional but if the are provided, the number and order in
884
+ 'annotation_ids' and 'input_ids' should match
885
+ """
886
+ if not isinstance(input_ids, list):
887
+ raise UserError("input_ids must be a list of input ids")
888
+ if annotation_ids and len(input_ids) != len(annotation_ids):
889
+ raise UserError("Number of provided annotation_ids and input_ids should match.")
890
+ request = service_pb2.DeleteAnnotationsRequest(
891
+ user_app_id=self.user_app_id, ids=annotation_ids, input_ids=input_ids)
892
+ response = self._grpc_request(self.STUB.DeleteAnnotations, request)
893
+ if response.status.code != status_code_pb2.SUCCESS:
894
+ raise Exception(response.status)
895
+ self.logger.info("\nAnnotations Deleted\n%s", response.status)
896
+
870
897
  def download_inputs(self, inputs: List[Input]) -> List[bytes]:
871
898
  """Download list of input objects from the app.
872
899
 
@@ -743,7 +743,7 @@ class Model(Lister, BaseClient):
743
743
  file_bytes = f.read()
744
744
 
745
745
  return self.generate_by_bytes(
746
- filepath=file_bytes,
746
+ input_bytes=file_bytes,
747
747
  input_type=input_type,
748
748
  compute_cluster_id=compute_cluster_id,
749
749
  nodepool_id=nodepool_id,
@@ -165,6 +165,14 @@ class InputAnnotationDownloader:
165
165
  def _save_annotation_to_archive(self, new_archive: zipfile.ZipFile, annot_data: List[Dict],
166
166
  file_name: str) -> None:
167
167
  """Gets the annotation response bytestring (from requests) and append to zip file."""
168
+ # Fill zero values for missing bounding box keys
169
+ for annot in annot_data:
170
+ if annot.get('regionInfo') and annot['regionInfo'].get('boundingBox'):
171
+ bbox = annot['regionInfo']['boundingBox']
172
+ bbox.setdefault('topRow', 0)
173
+ bbox.setdefault('leftCol', 0)
174
+ bbox.setdefault('bottomRow', 0)
175
+ bbox.setdefault('rightCol', 0)
168
176
  # Serialize the dictionary to a JSON string
169
177
  json_str = json.dumps(annot_data)
170
178
  # Convert the JSON string to bytes
@@ -6,7 +6,6 @@ from google.protobuf.struct_pb2 import Struct
6
6
 
7
7
  from clarifai.client.input import Inputs
8
8
  from clarifai.datasets.upload.base import ClarifaiDataLoader, ClarifaiDataset
9
- from clarifai.utils.misc import get_uuid
10
9
 
11
10
 
12
11
  class MultiModalDataset(ClarifaiDataset):
@@ -36,7 +35,6 @@ class MultiModalDataset(ClarifaiDataset):
36
35
  image_bytes = data_item.image_bytes
37
36
  text = data_item.text
38
37
  labels = data_item.labels if isinstance(data_item.labels, list) else [data_item.labels]
39
- id = get_uuid(8)
40
38
  input_id = f"{self.dataset_id}-{id}" if data_item.id is None else f"{self.dataset_id}-{str(data_item.id)}"
41
39
  if data_item.metadata is not None:
42
40
  metadata.update(data_item.metadata)
@@ -1,16 +1,16 @@
1
- ARG PYTHON_VERSION=${PYTHON_VERSION}
2
- FROM public.ecr.aws/docker/library/python:${PYTHON_VERSION}-slim-bookworm as build
1
+ FROM --platform=$TARGETPLATFORM ${BASE_IMAGE} as build
3
2
 
4
- # Set the working directory to /app
5
- WORKDIR /app
3
+ ENV DEBIAN_FRONTEND=noninteractive
6
4
 
5
+ #############################
6
+ # User specific requirements
7
+ #############################
7
8
  COPY requirements.txt .
8
- # Install requirements and cleanup before leaving this line.
9
- # Note(zeiler): this could be in a future template as {{model_python_deps}}
10
- RUN python -m pip install -r requirements.txt && rm -rf /root/.cache
11
9
 
12
- # Install Clarifai SDK
13
- RUN python -m pip install clarifai
10
+ # Install requirements and clarifai package and cleanup before leaving this line.
11
+ # Note(zeiler): this could be in a future template as {{model_python_deps}}
12
+ RUN pip install --no-cache-dir -r requirements.txt && \
13
+ pip install --no-cache-dir clarifai
14
14
 
15
15
  # These will be set by the templaing system.
16
16
  ENV CLARIFAI_PAT=${CLARIFAI_PAT}
@@ -20,12 +20,22 @@ ENV CLARIFAI_NODEPOOL_ID=${CLARIFAI_NODEPOOL_ID}
20
20
  ENV CLARIFAI_COMPUTE_CLUSTER_ID=${CLARIFAI_COMPUTE_CLUSTER_ID}
21
21
  ENV CLARIFAI_API_BASE=${CLARIFAI_API_BASE}
22
22
 
23
+ # Set the NUMBA cache dir to /tmp
24
+ ENV NUMBA_CACHE_DIR=/tmp/numba_cache
25
+ ENV HOME=/tmp
26
+
27
+ # Set the working directory to /app
28
+ WORKDIR /app
29
+
23
30
  # Copy the current folder into /app/model_dir that the SDK will expect.
31
+ # Note(zeiler): would be nice to exclude checkpoints in case they were pre-downloaded.
24
32
  COPY . /app/model_dir/${name}
25
33
 
26
34
  # Add the model directory to the python path.
27
- ENV PYTHONPATH "${PYTHONPATH}:/app/model_dir/${name}"
35
+ ENV PYTHONPATH=${PYTHONPATH}:/app/model_dir/${name}
36
+
37
+ ENTRYPOINT ["python", "-m", "clarifai.runners.server"]
28
38
 
29
39
  # Finally run the clarifai entrypoint to start the runner loop and local dev server.
30
40
  # Note(zeiler): we may want to make this a clarifai CLI call.
31
- CMD ["python", "-m", "clarifai.runners.server", "--model_path", "/app/model_dir/${name}"]
41
+ CMD ["--model_path", "/app/model_dir/main"]
@@ -24,7 +24,8 @@ class AnyAnyModel(ModelRunner):
24
24
  list_input_dict = [
25
25
  InputDataHandler.from_proto(input).to_python() for input in input_request.inputs
26
26
  ]
27
- inference_params = json_format.MessageToDict(input_request.model.output_info.params)
27
+ inference_params = json_format.MessageToDict(
28
+ input_request.model.model_version.output_info.params)
28
29
 
29
30
  return list_input_dict, inference_params
30
31
 
@@ -141,7 +142,8 @@ class VisualInputModel(AnyAnyModel):
141
142
  list_input_dict = [
142
143
  InputDataHandler.from_proto(input).image(format="np") for input in input_request.inputs
143
144
  ]
144
- inference_params = json_format.MessageToDict(input_request.model.output_info.params)
145
+ inference_params = json_format.MessageToDict(
146
+ input_request.model.model_version.output_info.params)
145
147
 
146
148
  return list_input_dict, inference_params
147
149
 
@@ -181,7 +183,8 @@ class TextInputModel(AnyAnyModel):
181
183
  def parse_input_request(
182
184
  self, input_request: service_pb2.PostModelOutputsRequest) -> Tuple[List[Dict], Dict]:
183
185
  list_input_text = [InputDataHandler.from_proto(input).text for input in input_request.inputs]
184
- inference_params = json_format.MessageToDict(input_request.model.output_info.params)
186
+ inference_params = json_format.MessageToDict(
187
+ input_request.model.model_version.output_info.params)
185
188
 
186
189
  return list_input_text, inference_params
187
190
 
@@ -1,3 +1,4 @@
1
+ import hashlib
1
2
  import importlib.util
2
3
  import inspect
3
4
  import os
@@ -13,6 +14,7 @@ from clarifai_grpc.grpc.api.status import status_code_pb2, status_pb2
13
14
  from clarifai_protocol import BaseRunner
14
15
 
15
16
  from clarifai.runners.models.model_upload import ModelUploader
17
+ from clarifai.runners.utils.url_fetcher import ensure_urls_downloaded
16
18
  from clarifai.utils.logging import logger
17
19
 
18
20
 
@@ -24,17 +26,26 @@ class ModelRunLocally:
24
26
 
25
27
  def create_temp_venv(self):
26
28
  """Create a temporary virtual environment."""
27
- logger.info("Creating temporary virtual environment...")
28
- temp_dir = tempfile.mkdtemp()
29
+ with open(self.requirements_file, "r") as f:
30
+ requirements_hash = hashlib.md5(f.read().encode('utf-8')).hexdigest()
31
+
32
+ temp_dir = os.path.join(tempfile.gettempdir(), str(requirements_hash))
29
33
  venv_dir = os.path.join(temp_dir, "venv")
30
- venv.create(venv_dir, with_pip=True)
34
+
35
+ if os.path.exists(temp_dir):
36
+ logger.info(f"Using previous virtual environment at {temp_dir}")
37
+ use_existing_venv = True
38
+ else:
39
+ logger.info("Creating temporary virtual environment...")
40
+ use_existing_venv = False
41
+ venv.create(venv_dir, with_pip=True)
42
+ logger.info(f"Created temporary virtual environment at {venv_dir}")
31
43
 
32
44
  self.venv_dir = venv_dir
33
45
  self.temp_dir = temp_dir
34
46
  self.python_executable = os.path.join(venv_dir, "bin", "python")
35
47
 
36
- logger.info(f"Created temporary virtual environment at {venv_dir}")
37
- return venv_dir, temp_dir
48
+ return use_existing_venv
38
49
 
39
50
  def install_requirements(self):
40
51
  """Install the dependencies from requirements.txt and Clarifai."""
@@ -102,6 +113,8 @@ class ModelRunLocally:
102
113
  """Perform inference using the runner."""
103
114
  request = self._build_request()
104
115
 
116
+ ensure_urls_downloaded(request)
117
+
105
118
  try:
106
119
  return runner.predict(request)
107
120
  except Exception as e:
@@ -175,16 +188,18 @@ class ModelRunLocally:
175
188
  shutil.rmtree(self.temp_dir)
176
189
 
177
190
 
178
- def main(model_path, run_model_server=False):
191
+ def main(model_path, run_model_server=False, keep_env=False):
179
192
 
180
193
  manager = ModelRunLocally(model_path)
181
- manager.create_temp_venv()
194
+ use_existing_env = manager.create_temp_venv()
182
195
 
183
196
  try:
184
- manager.install_requirements()
197
+ if not use_existing_env:
198
+ manager.install_requirements()
185
199
  if run_model_server:
186
200
  manager.run_model_server()
187
201
  else:
188
202
  manager.test_model()
189
203
  finally:
190
- manager.clean_up()
204
+ if not keep_env:
205
+ manager.clean_up()
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import re
2
3
  import time
3
4
  from string import Template
4
5
 
@@ -23,6 +24,44 @@ def _clear_line(n: int = 1) -> None:
23
24
 
24
25
  class ModelUploader:
25
26
  DEFAULT_PYTHON_VERSION = 3.11
27
+ DEFAULT_TORCH_VERSION = '2.4.0'
28
+ DEFAULT_CUDA_VERSION = '124'
29
+ # List of available torch images for matrix
30
+ '''
31
+ python_version: ['3.8', '3.9', '3.10', '3.11']
32
+ torch_version: ['2.0.0', '2.1.0', '2.2.0', '2.3.0', '2.4.0', '2.4.1', '2.5.0']
33
+ cuda_version: ['124']
34
+ '''
35
+ AVAILABLE_TORCH_IMAGES = [
36
+ '2.0.0-py3.8-cuda124',
37
+ '2.0.0-py3.9-cuda124',
38
+ '2.0.0-py3.10-cuda124',
39
+ '2.0.0-py3.11-cuda124',
40
+ '2.1.0-py3.8-cuda124',
41
+ '2.1.0-py3.9-cuda124',
42
+ '2.1.0-py3.10-cuda124',
43
+ '2.1.0-py3.11-cuda124',
44
+ '2.2.0-py3.8-cuda124',
45
+ '2.2.0-py3.9-cuda124',
46
+ '2.2.0-py3.10-cuda124',
47
+ '2.2.0-py3.11-cuda124',
48
+ '2.3.0-py3.8-cuda124',
49
+ '2.3.0-py3.9-cuda124',
50
+ '2.3.0-py3.10-cuda124',
51
+ '2.3.0-py3.11-cuda124',
52
+ '2.4.0-py3.8-cuda124',
53
+ '2.4.0-py3.9-cuda124',
54
+ '2.4.0-py3.10-cuda124',
55
+ '2.4.0-py3.11-cuda124',
56
+ '2.4.1-py3.8-cuda124',
57
+ '2.4.1-py3.9-cuda124',
58
+ '2.4.1-py3.10-cuda124',
59
+ '2.4.1-py3.11-cuda124',
60
+ ]
61
+ AVAILABLE_PYTHON_IMAGES = ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
62
+ PYTHON_BASE_IMAGE = 'public.ecr.aws/clarifai-models/python-base:{python_version}'
63
+ TORCH_BASE_IMAGE = 'public.ecr.aws/clarifai-models/torch:{torch_version}-py{python_version}-cuda{cuda_version}'
64
+
26
65
  CONCEPTS_REQUIRED_MODEL_TYPE = [
27
66
  'visual-classifier', 'visual-detector', 'visual-segmenter', 'text-classifier'
28
67
  ]
@@ -87,7 +126,7 @@ class ModelUploader:
87
126
  user_id = model.get('user_id')
88
127
  app_id = model.get('app_id')
89
128
 
90
- base = os.environ.get('CLARIFAI_API_BASE', 'https://api-dev.clarifai.com')
129
+ base = os.environ.get('CLARIFAI_API_BASE', 'https://api.clarifai.com')
91
130
 
92
131
  self._client = BaseClient(user_id=user_id, app_id=app_id, base=base)
93
132
 
@@ -144,18 +183,46 @@ class ModelUploader:
144
183
  )
145
184
  return self.client.STUB.PostModels(request)
146
185
 
186
+ def _parse_requirements(self):
187
+ # parse the user's requirements.txt to determine the proper base image to build on top of, based on the torch and other large dependencies and it's versions
188
+ # List of dependencies to look for
189
+ dependencies = [
190
+ 'torch',
191
+ ]
192
+ # Escape dependency names for regex
193
+ dep_pattern = '|'.join(map(re.escape, dependencies))
194
+ # All possible version specifiers
195
+ version_specifiers = '==|>=|<=|!=|~=|>|<'
196
+ # Compile a regex pattern with verbose mode for readability
197
+ pattern = re.compile(r"""
198
+ ^\s* # Start of line, optional whitespace
199
+ (?P<dependency>""" + dep_pattern + r""") # Dependency name
200
+ \s* # Optional whitespace
201
+ (?P<specifier>""" + version_specifiers + r""")? # Optional version specifier
202
+ \s* # Optional whitespace
203
+ (?P<version>[^\s;]+)? # Optional version (up to space or semicolon)
204
+ """, re.VERBOSE)
205
+
206
+ deendencies_version = {}
207
+ with open(os.path.join(self.folder, 'requirements.txt'), 'r') as file:
208
+ for line in file:
209
+ # Skip empty lines and comments
210
+ line = line.strip()
211
+ if not line or line.startswith('#'):
212
+ continue
213
+ match = pattern.match(line)
214
+ if match:
215
+ dependency = match.group('dependency')
216
+ version = match.group('version')
217
+ deendencies_version[dependency] = version if version else None
218
+ return deendencies_version
219
+
147
220
  def create_dockerfile(self):
148
- num_accelerators = self.inference_compute_info.num_accelerators
149
- if num_accelerators:
150
- dockerfile_template = os.path.join(
151
- os.path.dirname(os.path.dirname(__file__)),
152
- 'dockerfile_template',
153
- 'Dockerfile.cuda.template',
154
- )
155
- else:
156
- dockerfile_template = os.path.join(
157
- os.path.dirname(os.path.dirname(__file__)), 'dockerfile_template',
158
- 'Dockerfile.cpu.template')
221
+ dockerfile_template = os.path.join(
222
+ os.path.dirname(os.path.dirname(__file__)),
223
+ 'dockerfile_template',
224
+ 'Dockerfile.template',
225
+ )
159
226
 
160
227
  with open(dockerfile_template, 'r') as template_file:
161
228
  dockerfile_template = template_file.read()
@@ -166,6 +233,11 @@ class ModelUploader:
166
233
  build_info = self.config.get('build_info', {})
167
234
  if 'python_version' in build_info:
168
235
  python_version = build_info['python_version']
236
+ if python_version not in self.AVAILABLE_PYTHON_IMAGES:
237
+ logger.error(
238
+ f"Python version {python_version} not supported, please use one of the following versions: {self.AVAILABLE_PYTHON_IMAGES}"
239
+ )
240
+ return
169
241
  logger.info(
170
242
  f"Using Python version {python_version} from the config file to build the Dockerfile")
171
243
  else:
@@ -174,10 +246,26 @@ class ModelUploader:
174
246
  )
175
247
  python_version = self.DEFAULT_PYTHON_VERSION
176
248
 
249
+ base_image = self.PYTHON_BASE_IMAGE.format(python_version=python_version)
250
+
251
+ # Parse the requirements.txt file to determine the base image
252
+ dependencies = self._parse_requirements()
253
+ if 'torch' in dependencies and dependencies['torch']:
254
+ torch_version = dependencies['torch']
255
+
256
+ for image in self.AVAILABLE_TORCH_IMAGES:
257
+ if torch_version in image and f'py{python_version}' in image:
258
+ base_image = self.TORCH_BASE_IMAGE.format(
259
+ torch_version=torch_version,
260
+ python_version=python_version,
261
+ cuda_version=self.DEFAULT_CUDA_VERSION)
262
+ logger.info(f"Using Torch version {torch_version} base image to build the Docker image")
263
+ break
264
+
177
265
  # Replace placeholders with actual values
178
266
  dockerfile_content = dockerfile_template.safe_substitute(
179
- PYTHON_VERSION=python_version,
180
267
  name='main',
268
+ BASE_IMAGE=base_image,
181
269
  )
182
270
 
183
271
  # Write Dockerfile
@@ -6,25 +6,32 @@ from clarifai.utils.logging import logger
6
6
 
7
7
 
8
8
  def download_input(input):
9
+ _download_input_data(input.data)
10
+ if input.data.parts:
11
+ for i in range(len(input.data.parts)):
12
+ _download_input_data(input.data.parts[i].data)
13
+
14
+
15
+ def _download_input_data(input_data):
9
16
  """
10
17
  This function will download any urls that are not already bytes.
11
18
  """
12
- if input.data.image.url and not input.data.image.base64:
19
+ if input_data.image.url and not input_data.image.base64:
13
20
  # Download the image
14
- with fsspec.open(input.data.image.url, 'rb') as f:
15
- input.data.image.base64 = f.read()
16
- if input.data.video.url and not input.data.video.base64:
21
+ with fsspec.open(input_data.image.url, 'rb') as f:
22
+ input_data.image.base64 = f.read()
23
+ if input_data.video.url and not input_data.video.base64:
17
24
  # Download the video
18
- with fsspec.open(input.data.video.url, 'rb') as f:
19
- input.data.video.base64 = f.read()
20
- if input.data.audio.url and not input.data.audio.base64:
25
+ with fsspec.open(input_data.video.url, 'rb') as f:
26
+ input_data.video.base64 = f.read()
27
+ if input_data.audio.url and not input_data.audio.base64:
21
28
  # Download the audio
22
- with fsspec.open(input.data.audio.url, 'rb') as f:
23
- input.data.audio.base64 = f.read()
24
- if input.data.text.url and not input.data.text.raw:
29
+ with fsspec.open(input_data.audio.url, 'rb') as f:
30
+ input_data.audio.base64 = f.read()
31
+ if input_data.text.url and not input_data.text.raw:
25
32
  # Download the text
26
- with fsspec.open(input.data.text.url, 'r') as f:
27
- input.data.text.raw = f.read()
33
+ with fsspec.open(input_data.text.url, 'r') as f:
34
+ input_data.text.raw = f.read()
28
35
 
29
36
 
30
37
  def ensure_urls_downloaded(request, max_threads=128):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clarifai
3
- Version: 10.10.0
3
+ Version: 10.11.0
4
4
  Summary: Clarifai Python SDK
5
5
  Home-page: https://github.com/Clarifai/clarifai-python
6
6
  Author: Clarifai
@@ -20,7 +20,7 @@ Classifier: Operating System :: OS Independent
20
20
  Requires-Python: >=3.8
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: clarifai-grpc>=10.9.11
23
+ Requires-Dist: clarifai-grpc>=10.10.2
24
24
  Requires-Dist: clarifai-protocol>=0.0.6
25
25
  Requires-Dist: numpy>=1.22.0
26
26
  Requires-Dist: tqdm>=4.65.0
@@ -74,8 +74,7 @@ clarifai/rag/rag.py
74
74
  clarifai/rag/utils.py
75
75
  clarifai/runners/__init__.py
76
76
  clarifai/runners/server.py
77
- clarifai/runners/dockerfile_template/Dockerfile.cpu.template
78
- clarifai/runners/dockerfile_template/Dockerfile.cuda.template
77
+ clarifai/runners/dockerfile_template/Dockerfile.template
79
78
  clarifai/runners/models/__init__.py
80
79
  clarifai/runners/models/base_typed_model.py
81
80
  clarifai/runners/models/model_class.py
@@ -1,4 +1,4 @@
1
- clarifai-grpc>=10.9.11
1
+ clarifai-grpc>=10.10.2
2
2
  clarifai-protocol>=0.0.6
3
3
  numpy>=1.22.0
4
4
  tqdm>=4.65.0
@@ -1,2 +1,2 @@
1
1
  [tool.pytest.ini_options]
2
- markers = ["requires_secrets: mark a test as requiring secrets to run"]
2
+ markers = ["requires_secrets: mark a test as requiring secrets to run", "coverage_only: mark a test as required to run for coverage purpose only"]
@@ -1,4 +1,4 @@
1
- clarifai-grpc>=10.9.11
1
+ clarifai-grpc>=10.10.2
2
2
  clarifai-protocol>=0.0.6
3
3
  numpy>=1.22.0
4
4
  tqdm>=4.65.0
@@ -28,21 +28,32 @@ OBJECT_CONCEPT_ID = 'food'
28
28
  PREDICATE = "hypernym"
29
29
 
30
30
  CLARIFAI_PAT = os.environ["CLARIFAI_PAT"]
31
+ CLARIFAI_API_BASE = os.environ.get("CLARIFAI_API_BASE", "https://api.clarifai.com")
31
32
 
32
33
 
33
34
  @pytest.fixture
34
35
  def create_app():
35
- return App(user_id=CREATE_APP_USER_ID, app_id=CREATE_APP_ID, pat=CLARIFAI_PAT)
36
+ return App(
37
+ user_id=CREATE_APP_USER_ID,
38
+ app_id=CREATE_APP_ID,
39
+ pat=CLARIFAI_PAT,
40
+ base_url=CLARIFAI_API_BASE)
36
41
 
37
42
 
38
43
  @pytest.fixture
39
44
  def app():
40
- return App(user_id=MAIN_APP_USER_ID, app_id=MAIN_APP_ID, pat=CLARIFAI_PAT)
45
+ return App(
46
+ user_id=MAIN_APP_USER_ID, app_id=MAIN_APP_ID, pat=CLARIFAI_PAT, base_url=CLARIFAI_API_BASE)
47
+
48
+
49
+ @pytest.fixture
50
+ def create_client():
51
+ return User(user_id=CREATE_APP_USER_ID, pat=CLARIFAI_PAT, base_url=CLARIFAI_API_BASE)
41
52
 
42
53
 
43
54
  @pytest.fixture
44
55
  def client():
45
- return User(user_id=MAIN_APP_USER_ID, pat=CLARIFAI_PAT)
56
+ return User(user_id=MAIN_APP_USER_ID, pat=CLARIFAI_PAT, base_url=CLARIFAI_API_BASE)
46
57
 
47
58
 
48
59
  @pytest.mark.requires_secrets
@@ -60,7 +71,7 @@ class TestApp:
60
71
 
61
72
  def test_list_models(self, app):
62
73
  all_models = list(app.list_models(page_no=1))
63
- assert len(all_models) == 15 #default per_page is 15
74
+ assert len(all_models) >= 15 #default per_page is 16
64
75
 
65
76
  def test_list_workflows(self, app):
66
77
  all_workflows = list(app.list_workflows(page_no=1, per_page=10))
@@ -68,7 +79,7 @@ class TestApp:
68
79
 
69
80
  def test_list_modules(self, app):
70
81
  all_modules = list(app.list_modules())
71
- assert len(all_modules) == 1
82
+ assert len(all_modules) >= 0
72
83
 
73
84
  def test_list_installed_module_versions(self, app):
74
85
  all_installed_module_versions = list(app.list_installed_module_versions())
@@ -95,8 +106,8 @@ class TestApp:
95
106
  assert len(versions) == 1 #test for list_versions
96
107
  assert workflow.id == General_Workflow_ID and workflow.app_id == MAIN_APP_ID and workflow.user_id == MAIN_APP_USER_ID
97
108
 
98
- def test_create_app(self):
99
- app = User(user_id=CREATE_APP_USER_ID, pat=CLARIFAI_PAT).create_app(app_id=CREATE_APP_ID)
109
+ def test_create_app(self, create_client):
110
+ app = create_client.create_app(app_id=CREATE_APP_ID)
100
111
  assert app.id == CREATE_APP_ID and app.user_id == CREATE_APP_USER_ID
101
112
 
102
113
  def test_create_search(self, create_app):
@@ -149,9 +160,9 @@ class TestApp:
149
160
  all_concept_relations = list(create_app.search_concept_relations(show_tree=True))
150
161
  assert len(all_concept_relations) == 1
151
162
 
152
- def test_patch_app(self, caplog):
163
+ def test_patch_app(self, create_client, caplog):
153
164
  with caplog.at_level(logging.INFO):
154
- User(user_id=CREATE_APP_USER_ID).patch_app(
165
+ create_client.patch_app(
155
166
  app_id=CREATE_APP_ID,
156
167
  action='overwrite',
157
168
  default_language='en',
@@ -221,7 +232,7 @@ class TestApp:
221
232
  # client.delete_runner(CREATE_RUNNER_ID)
222
233
  # assert "SUCCESS" in caplog.text
223
234
 
224
- def test_delete_app(self, caplog):
235
+ def test_delete_app(self, caplog, create_client):
225
236
  with caplog.at_level(logging.INFO):
226
- User(user_id=CREATE_APP_USER_ID).delete_app(CREATE_APP_ID)
237
+ create_client.delete_app(CREATE_APP_ID)
227
238
  assert "SUCCESS" in caplog.text