clarifai 11.8.4__py3-none-any.whl → 11.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.
clarifai/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "11.8.4"
1
+ __version__ = "11.9.0"
clarifai/cli/model.py CHANGED
@@ -29,6 +29,7 @@ from clarifai.utils.constants import (
29
29
  DEFAULT_LOCAL_RUNNER_NODEPOOL_ID,
30
30
  DEFAULT_OLLAMA_MODEL_REPO_BRANCH,
31
31
  DEFAULT_PYTHON_MODEL_REPO_BRANCH,
32
+ DEFAULT_SGLANG_MODEL_REPO_BRANCH,
32
33
  DEFAULT_TOOLKIT_MODEL_REPO,
33
34
  DEFAULT_VLLM_MODEL_REPO_BRANCH,
34
35
  )
@@ -76,15 +77,15 @@ def model():
76
77
  @click.option(
77
78
  '--toolkit',
78
79
  type=click.Choice(
79
- ['ollama', 'huggingface', 'lmstudio', 'vllm', 'python'], case_sensitive=False
80
+ ['ollama', 'huggingface', 'lmstudio', 'vllm', 'sglang', 'python'], case_sensitive=False
80
81
  ),
81
82
  required=False,
82
- help='Toolkit to use for model initialization. Currently supports "ollama", "huggingface", "lmstudio", "vllm" and "python".',
83
+ help='Toolkit to use for model initialization. Currently supports "ollama", "huggingface", "lmstudio", "vllm", "sglang" and "python".',
83
84
  )
84
85
  @click.option(
85
86
  '--model-name',
86
87
  required=False,
87
- help='Model name to configure when using --toolkit. For ollama toolkit, this sets the Ollama model to use (e.g., "llama3.1", "mistral", etc.). For vllm & huggingface toolkit, this sets the Hugging Face model repo_id (e.g., "unsloth/Llama-3.2-1B-Instruct").\n For lmstudio toolkit, this sets the LM Studio model name (e.g., "qwen/qwen3-4b-thinking-2507").\n',
88
+ help='Model name to configure when using --toolkit. For ollama toolkit, this sets the Ollama model to use (e.g., "llama3.1", "mistral", etc.). For vllm, sglang & huggingface toolkit, this sets the Hugging Face model repo_id (e.g., "unsloth/Llama-3.2-1B-Instruct").\n For lmstudio toolkit, this sets the LM Studio model name (e.g., "qwen/qwen3-4b-thinking-2507").\n',
88
89
  )
89
90
  @click.option(
90
91
  '--port',
@@ -129,8 +130,8 @@ def init(
129
130
  MODEL_TYPE_ID: Type of model to create. If not specified, defaults to "text-to-text" for text models.\n
130
131
  GITHUB_PAT: GitHub Personal Access Token for authentication when cloning private repositories.\n
131
132
  GITHUB_URL: GitHub repository URL or "repo" format to clone a repository from. If provided, the entire repository contents will be copied to the target directory instead of using default templates.\n
132
- TOOLKIT: Toolkit to use for model initialization. Currently supports "ollama", "huggingface", "lmstudio", "vllm" and "python".\n
133
- MODEL_NAME: Model name to configure when using --toolkit. For ollama toolkit, this sets the Ollama model to use (e.g., "llama3.1", "mistral", etc.). For vllm & huggingface toolkit, this sets the Hugging Face model repo_id (e.g., "Qwen/Qwen3-4B-Instruct-2507"). For lmstudio toolkit, this sets the LM Studio model name (e.g., "qwen/qwen3-4b-thinking-2507").\n
133
+ TOOLKIT: Toolkit to use for model initialization. Currently supports "ollama", "huggingface", "lmstudio", "vllm", "sglang" and "python".\n
134
+ MODEL_NAME: Model name to configure when using --toolkit. For ollama toolkit, this sets the Ollama model to use (e.g., "llama3.1", "mistral", etc.). For vllm, sglang & huggingface toolkit, this sets the Hugging Face model repo_id (e.g., "Qwen/Qwen3-4B-Instruct-2507"). For lmstudio toolkit, this sets the LM Studio model name (e.g., "qwen/qwen3-4b-thinking-2507").\n
134
135
  PORT: Port to run the (Ollama/lmstudio) server on. Defaults to 23333.\n
135
136
  CONTEXT_LENGTH: Context length for the (Ollama/lmstudio) model. Defaults to 8192.\n
136
137
  """
@@ -183,6 +184,9 @@ def init(
183
184
  elif toolkit == 'vllm':
184
185
  github_url = DEFAULT_TOOLKIT_MODEL_REPO
185
186
  branch = DEFAULT_VLLM_MODEL_REPO_BRANCH
187
+ elif toolkit == 'sglang':
188
+ github_url = DEFAULT_TOOLKIT_MODEL_REPO
189
+ branch = DEFAULT_SGLANG_MODEL_REPO_BRANCH
186
190
  elif toolkit == 'python':
187
191
  github_url = DEFAULT_TOOLKIT_MODEL_REPO
188
192
  branch = DEFAULT_PYTHON_MODEL_REPO_BRANCH
@@ -320,7 +324,9 @@ def init(
320
324
  if (user_id or model_name or port or context_length) and (toolkit == 'lmstudio'):
321
325
  customize_lmstudio_model(model_path, user_id, model_name, port, context_length)
322
326
 
323
- if (user_id or model_name) and (toolkit == 'huggingface' or toolkit == 'vllm'):
327
+ if (user_id or model_name) and (
328
+ toolkit == 'huggingface' or toolkit == 'vllm' or toolkit == 'sglang'
329
+ ):
324
330
  # Update the config.yaml file with the provided model name
325
331
  customize_huggingface_model(model_path, user_id, model_name)
326
332
 
@@ -1010,6 +1016,7 @@ def local_runner(ctx, model_path, pool_size, verbose):
1010
1016
  logger.info("Customizing Ollama model with provided parameters...")
1011
1017
  customize_ollama_model(
1012
1018
  model_path=model_path,
1019
+ user_id=user_id,
1013
1020
  verbose=True if verbose else False,
1014
1021
  )
1015
1022
  except Exception as e:
@@ -2,11 +2,14 @@ from clarifai.client.app import App
2
2
  from clarifai.client.auth.register import V2Stub
3
3
  from clarifai.client.auth.stub import create_stub
4
4
  from clarifai.client.base import BaseClient
5
+ from clarifai.client.compute_cluster import ComputeCluster
5
6
  from clarifai.client.dataset import Dataset
7
+ from clarifai.client.deployment import Deployment
6
8
  from clarifai.client.input import Inputs
7
9
  from clarifai.client.lister import Lister
8
10
  from clarifai.client.model import Model
9
11
  from clarifai.client.module import Module
12
+ from clarifai.client.nodepool import Nodepool
10
13
  from clarifai.client.pipeline import Pipeline
11
14
  from clarifai.client.pipeline_step import PipelineStep
12
15
  from clarifai.client.search import Search
@@ -28,4 +31,7 @@ __all__ = [
28
31
  'Inputs',
29
32
  'BaseClient',
30
33
  'Search',
34
+ 'ComputeCluster',
35
+ 'Nodepool',
36
+ 'Deployment',
31
37
  ]
@@ -11,6 +11,7 @@ from clarifai.constants.model import MAX_MODEL_PREDICT_INPUTS
11
11
  from clarifai.errors import UserError
12
12
  from clarifai.runners.utils import code_script, method_signatures
13
13
  from clarifai.runners.utils.method_signatures import (
14
+ RESERVED_PARAM_WITH_PROTO,
14
15
  CompatibilitySerializer,
15
16
  deserialize,
16
17
  get_stream_from_signature,
@@ -204,6 +205,9 @@ class ModelClient:
204
205
 
205
206
  def bind_f(method_name, method_argnames, call_func, async_call_func):
206
207
  def sync_f(*args, **kwargs):
208
+ # Extract with_proto parameter if present
209
+ with_proto = kwargs.pop(RESERVED_PARAM_WITH_PROTO, False)
210
+
207
211
  if len(args) > len(method_argnames):
208
212
  raise TypeError(
209
213
  f"{method_name}() takes {len(method_argnames)} positional arguments but {len(args)} were given"
@@ -221,7 +225,7 @@ class ModelClient:
221
225
  )
222
226
  if is_batch_input_valid and (not is_openai_chat_format(batch_inputs)):
223
227
  # If the batch input is valid, call the function with the batch inputs and the method name
224
- return call_func(batch_inputs, method_name)
228
+ return call_func(batch_inputs, method_name, with_proto=with_proto)
225
229
 
226
230
  for name, arg in zip(
227
231
  method_argnames, args
@@ -229,10 +233,13 @@ class ModelClient:
229
233
  if name in kwargs:
230
234
  raise TypeError(f"Multiple values for argument {name}")
231
235
  kwargs[name] = arg
232
- return call_func(kwargs, method_name)
236
+ return call_func(kwargs, method_name, with_proto=with_proto)
233
237
 
234
238
  async def async_f(*args, **kwargs):
235
239
  # Async version to call the async function
240
+ # Extract with_proto parameter if present
241
+ with_proto = kwargs.pop(RESERVED_PARAM_WITH_PROTO, False)
242
+
236
243
  if len(args) > len(method_argnames):
237
244
  raise TypeError(
238
245
  f"{method_name}() takes {len(method_argnames)} positional arguments but {len(args)} were given"
@@ -249,7 +256,9 @@ class ModelClient:
249
256
  )
250
257
  if is_batch_input_valid and (not is_openai_chat_format(batch_inputs)):
251
258
  # If the batch input is valid, call the function with the batch inputs and the method name
252
- return async_call_func(batch_inputs, method_name)
259
+ return async_call_func(
260
+ batch_inputs, method_name, with_proto=with_proto
261
+ )
253
262
 
254
263
  for name, arg in zip(
255
264
  method_argnames, args
@@ -258,7 +267,7 @@ class ModelClient:
258
267
  raise TypeError(f"Multiple values for argument {name}")
259
268
  kwargs[name] = arg
260
269
 
261
- return async_call_func(kwargs, method_name)
270
+ return async_call_func(kwargs, method_name, with_proto=with_proto)
262
271
 
263
272
  class MethodWrapper:
264
273
  def __call__(self, *args, **kwargs):
@@ -364,6 +373,7 @@ class ModelClient:
364
373
  self,
365
374
  inputs, # TODO set up functions according to fetched signatures?
366
375
  method_name: str = 'predict',
376
+ with_proto: bool = False,
367
377
  ) -> Any:
368
378
  input_signature = self._method_signatures[method_name].input_fields
369
379
  output_signature = self._method_signatures[method_name].output_fields
@@ -385,9 +395,12 @@ class ModelClient:
385
395
  outputs = []
386
396
  for output in response.outputs:
387
397
  outputs.append(deserialize(output.data, output_signature, is_output=True))
388
- if batch_input:
389
- return outputs
390
- return outputs[0]
398
+
399
+ result = outputs if batch_input else outputs[0]
400
+
401
+ if with_proto:
402
+ return result, response
403
+ return result
391
404
 
392
405
  def _predict_by_proto(
393
406
  self,
@@ -448,15 +461,17 @@ class ModelClient:
448
461
  self,
449
462
  inputs,
450
463
  method_name: str = 'predict',
464
+ with_proto: bool = False,
451
465
  ) -> Any:
452
466
  """Asynchronously process inputs and make predictions.
453
467
 
454
468
  Args:
455
469
  inputs: Input data to process
456
470
  method_name (str): Name of the method to call
471
+ with_proto (bool): If True, return both the processed result and the raw protobuf response
457
472
 
458
473
  Returns:
459
- Processed prediction results
474
+ Processed prediction results, optionally with protobuf response
460
475
  """
461
476
  # method_name is set to 'predict' by default, this is because to replicate the input and output signature behaviour of sync to async predict.
462
477
  input_signature = self._method_signatures[method_name].input_fields
@@ -477,7 +492,11 @@ class ModelClient:
477
492
  for output in response.outputs:
478
493
  outputs.append(deserialize(output.data, output_signature, is_output=True))
479
494
 
480
- return outputs if batch_input else outputs[0]
495
+ result = outputs if batch_input else outputs[0]
496
+
497
+ if with_proto:
498
+ return result, response
499
+ return result
481
500
 
482
501
  async def _async_predict_by_proto(
483
502
  self,
@@ -551,6 +570,7 @@ class ModelClient:
551
570
  self,
552
571
  inputs, # TODO set up functions according to fetched signatures?
553
572
  method_name: str = 'generate',
573
+ with_proto: bool = False,
554
574
  ) -> Any:
555
575
  input_signature = self._method_signatures[method_name].input_fields
556
576
  output_signature = self._method_signatures[method_name].output_fields
@@ -572,10 +592,13 @@ class ModelClient:
572
592
  outputs = []
573
593
  for output in response.outputs:
574
594
  outputs.append(deserialize(output.data, output_signature, is_output=True))
575
- if batch_input:
576
- yield outputs
595
+
596
+ result = outputs if batch_input else outputs[0]
597
+
598
+ if with_proto:
599
+ yield result, response
577
600
  else:
578
- yield outputs[0]
601
+ yield result
579
602
 
580
603
  def _generate_by_proto(
581
604
  self,
@@ -641,6 +664,7 @@ class ModelClient:
641
664
  self,
642
665
  inputs,
643
666
  method_name: str = 'generate',
667
+ with_proto: bool = False,
644
668
  ) -> Any:
645
669
  # method_name is set to 'generate' by default, this is because to replicate the input and output signature behaviour of sync to async generate.
646
670
  input_signature = self._method_signatures[method_name].input_fields
@@ -654,18 +678,21 @@ class ModelClient:
654
678
  proto_inputs = []
655
679
  for input in inputs:
656
680
  proto = resources_pb2.Input()
657
- serialize(input, input_signature, proto.data)
658
- proto_inputs.append(proto)
681
+ serialize(input, input_signature, proto.data)
682
+ proto_inputs.append(proto)
659
683
  response_stream = self._async_generate_by_proto(proto_inputs, method_name)
660
684
 
661
685
  async for response in response_stream:
662
686
  outputs = []
663
687
  for output in response.outputs:
664
688
  outputs.append(deserialize(output.data, output_signature, is_output=True))
665
- if batch_input:
666
- yield outputs
689
+
690
+ result = outputs if batch_input else outputs[0]
691
+
692
+ if with_proto:
693
+ yield result, response
667
694
  else:
668
- yield outputs[0]
695
+ yield result
669
696
 
670
697
  async def _async_generate_by_proto(
671
698
  self,
@@ -734,6 +761,7 @@ class ModelClient:
734
761
  self,
735
762
  inputs,
736
763
  method_name: str = 'stream',
764
+ with_proto: bool = False,
737
765
  ) -> Any:
738
766
  input_signature = self._method_signatures[method_name].input_fields
739
767
  output_signature = self._method_signatures[method_name].output_fields
@@ -775,7 +803,12 @@ class ModelClient:
775
803
 
776
804
  for response in response_stream:
777
805
  assert len(response.outputs) == 1, 'streaming methods must have exactly one output'
778
- yield deserialize(response.outputs[0].data, output_signature, is_output=True)
806
+ result = deserialize(response.outputs[0].data, output_signature, is_output=True)
807
+
808
+ if with_proto:
809
+ yield result, response
810
+ else:
811
+ yield result
779
812
 
780
813
  def _req_iterator(
781
814
  self,
@@ -843,6 +876,7 @@ class ModelClient:
843
876
  self,
844
877
  inputs,
845
878
  method_name: str = 'stream',
879
+ with_proto: bool = False,
846
880
  ) -> Any:
847
881
  # method_name is set to 'stream' by default, this is because to replicate the input and output signature behaviour of sync to async stream.
848
882
  input_signature = self._method_signatures[method_name].input_fields
@@ -885,7 +919,12 @@ class ModelClient:
885
919
 
886
920
  async for response in response_stream:
887
921
  assert len(response.outputs) == 1, 'streaming methods must have exactly one output'
888
- yield deserialize(response.outputs[0].data, output_signature, is_output=True)
922
+ result = deserialize(response.outputs[0].data, output_signature, is_output=True)
923
+
924
+ if with_proto:
925
+ yield result, response
926
+ else:
927
+ yield result
889
928
 
890
929
  async def _async_stream_by_proto(
891
930
  self,
@@ -8,7 +8,7 @@ import subprocess
8
8
  import sys
9
9
  import tarfile
10
10
  import time
11
- import webbrowser
11
+ import uuid
12
12
  from string import Template
13
13
  from typing import Any, Dict, Literal, Optional
14
14
  from unittest.mock import MagicMock
@@ -18,8 +18,10 @@ from clarifai_grpc.grpc.api import resources_pb2, service_pb2
18
18
  from clarifai_grpc.grpc.api.status import status_code_pb2
19
19
  from google.protobuf import json_format
20
20
 
21
+ from clarifai.client import Model, Nodepool
21
22
  from clarifai.client.base import BaseClient
22
23
  from clarifai.client.user import User
24
+ from clarifai.errors import UserError
23
25
  from clarifai.runners.models.model_class import ModelClass
24
26
  from clarifai.runners.utils import code_script
25
27
  from clarifai.runners.utils.const import (
@@ -41,7 +43,9 @@ from clarifai.runners.utils.loader import HuggingFaceLoader
41
43
  from clarifai.runners.utils.method_signatures import signatures_to_yaml
42
44
  from clarifai.urls.helper import ClarifaiUrlHelper
43
45
  from clarifai.utils.logging import logger
44
- from clarifai.versions import CLIENT_VERSION
46
+ from clarifai.versions import get_latest_version_from_pypi
47
+
48
+ CLARIFAI_LATEST_VERSION = get_latest_version_from_pypi()
45
49
 
46
50
  # 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
47
51
  # List of dependencies to look for
@@ -65,6 +69,88 @@ def is_related(object_class, main_class):
65
69
  return False
66
70
 
67
71
 
72
+ def get_user_input(prompt, required=True, default=None):
73
+ """Get user input with optional default value."""
74
+ if default:
75
+ prompt = f"{prompt} [{default}]: "
76
+ else:
77
+ prompt = f"{prompt}: "
78
+
79
+ while True:
80
+ value = input(prompt).strip()
81
+ if not value and default:
82
+ return default
83
+ if not value and required:
84
+ print("❌ This field is required. Please enter a value.")
85
+ continue
86
+ return value
87
+
88
+
89
+ def get_yes_no_input(prompt, default=None):
90
+ """Get yes/no input from user."""
91
+ if default is not None:
92
+ prompt = f"{prompt} [{'Y/n' if default else 'y/N'}]: "
93
+ else:
94
+ prompt = f"{prompt} [y/n]: "
95
+
96
+ while True:
97
+ response = input(prompt).strip().lower()
98
+ if not response and default is not None:
99
+ return default
100
+ if response in ['y', 'yes']:
101
+ return True
102
+ if response in ['n', 'no']:
103
+ return False
104
+ print("❌ Please enter 'y' or 'n'.")
105
+
106
+
107
+ def select_compute_option(user_id: str):
108
+ """
109
+ Dynamically list compute-clusters and node-pools that belong to `user_id`
110
+ and return a dict with nodepool_id, compute_cluster_id, cluster_user_id.
111
+ """
112
+ user = User(user_id=user_id) # PAT / BASE URL are picked from env-vars
113
+ clusters = list(user.list_compute_clusters())
114
+ if not clusters:
115
+ print("❌ No compute clusters found for this user.")
116
+ return None
117
+ print("\n🖥️ Available Compute Clusters:")
118
+ for idx, cc in enumerate(clusters, 1):
119
+ desc = getattr(cc, "description", "") or "No description"
120
+ print(f"{idx}. {cc.id} – {desc}")
121
+ while True:
122
+ try:
123
+ sel = int(input("Select compute cluster (number): ")) - 1
124
+ if 0 <= sel < len(clusters):
125
+ cluster = clusters[sel]
126
+ break
127
+ print("❌ Invalid selection.")
128
+ except ValueError:
129
+ print("❌ Please enter a number.")
130
+ nodepools = list(cluster.list_nodepools())
131
+ if not nodepools:
132
+ print("❌ No nodepools in selected cluster.")
133
+ return None
134
+ print("\n📦 Available Nodepools:")
135
+ for idx, np in enumerate(nodepools, 1):
136
+ desc = getattr(np, "description", "") or "No description"
137
+ print(f"{idx}. {np.id} – {desc}")
138
+ while True:
139
+ try:
140
+ sel = int(input("Select nodepool (number): ")) - 1
141
+ if 0 <= sel < len(nodepools):
142
+ nodepool = nodepools[sel]
143
+ break
144
+ print("❌ Invalid selection.")
145
+ except ValueError:
146
+ print("❌ Please enter a number.")
147
+ return {
148
+ "nodepool_id": nodepool.id,
149
+ "compute_cluster_id": cluster.id,
150
+ "cluster_user_id": getattr(cluster, "user_id", user_id),
151
+ }
152
+
153
+
68
154
  class ModelBuilder:
69
155
  DEFAULT_CHECKPOINT_SIZE = 50 * 1024**3 # 50 GiB
70
156
 
@@ -1068,26 +1154,28 @@ class ModelBuilder:
1068
1154
  break
1069
1155
  if 'clarifai' not in dependencies:
1070
1156
  raise Exception(
1071
- f"clarifai not found in requirements.txt, please add clarifai to the requirements.txt file with a fixed version. Current version is clarifai=={CLIENT_VERSION}"
1157
+ f"clarifai not found in requirements.txt, please add clarifai to the requirements.txt file with a fixed version. Current version is clarifai=={CLARIFAI_LATEST_VERSION}"
1072
1158
  )
1073
1159
  clarifai_version = dependencies['clarifai']
1074
1160
  if not clarifai_version:
1075
1161
  logger.warn(
1076
- f"clarifai version not found in requirements.txt, using the latest version {CLIENT_VERSION}"
1162
+ f"clarifai version not found in requirements.txt, using the latest version {CLARIFAI_LATEST_VERSION}"
1077
1163
  )
1078
- clarifai_version = CLIENT_VERSION
1164
+ clarifai_version = CLARIFAI_LATEST_VERSION
1079
1165
  lines = []
1080
1166
  with open(os.path.join(self.folder, 'requirements.txt'), 'r') as file:
1081
1167
  for line in file:
1082
1168
  # if the line without whitespace is "clarifai"
1083
1169
  dependency, version = self._match_req_line(line)
1084
1170
  if dependency and dependency == "clarifai":
1085
- lines.append(line.replace("clarifai", f"clarifai=={CLIENT_VERSION}"))
1171
+ lines.append(
1172
+ line.replace("clarifai", f"clarifai=={CLARIFAI_LATEST_VERSION}")
1173
+ )
1086
1174
  else:
1087
1175
  lines.append(line)
1088
1176
  with open(os.path.join(self.folder, 'requirements.txt'), 'w') as file:
1089
1177
  file.writelines(lines)
1090
- logger.warn(f"Updated requirements.txt to have clarifai=={CLIENT_VERSION}")
1178
+ logger.warn(f"Updated requirements.txt to have clarifai=={CLARIFAI_LATEST_VERSION}")
1091
1179
 
1092
1180
  # Replace placeholders with actual values
1093
1181
  dockerfile_content = dockerfile_template.safe_substitute(
@@ -1502,6 +1590,7 @@ class ModelBuilder:
1502
1590
  self.model_version_id = response.model_version_id
1503
1591
  logger.info(f"Created Model Version ID: {self.model_version_id}")
1504
1592
  logger.info(f"Full url to that version is: {self.model_ui_url}")
1593
+ is_uploaded = False
1505
1594
  try:
1506
1595
  is_uploaded = self.monitor_model_build()
1507
1596
  if is_uploaded:
@@ -1516,15 +1605,21 @@ class ModelBuilder:
1516
1605
  colorize=True,
1517
1606
  )
1518
1607
  logger.info("""\n
1519
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1608
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1520
1609
  # Here is a code snippet to use this model:
1521
1610
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1522
1611
  """)
1523
1612
  logger.info(snippet)
1613
+ logger.info("""\n
1614
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1615
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1616
+ """)
1524
1617
  finally:
1525
1618
  if os.path.exists(self.tar_file):
1526
1619
  logger.debug(f"Cleaning up upload file: {self.tar_file}")
1527
1620
  os.remove(self.tar_file)
1621
+ if is_uploaded:
1622
+ return self.model_version_id
1528
1623
 
1529
1624
  def model_version_stream_upload_iterator(self, model_version_proto, file_path):
1530
1625
  yield self.init_upload_model_version(model_version_proto, file_path)
@@ -1682,204 +1777,257 @@ def upload_model(folder, stage, skip_dockerfile, pat=None, base_url=None):
1682
1777
 
1683
1778
  # Ask user if they want to deploy the model
1684
1779
  if model_version is not None: # if it comes back None then it failed.
1685
- deploy_model = input("Do you want to deploy the model? (y/n): ")
1686
- if deploy_model.lower() != 'y':
1780
+ if get_yes_no_input("\n🔶 Do you want to deploy the model?", True):
1781
+ # Setup deployment for the uploaded model
1782
+ setup_deployment_for_model(builder)
1783
+ else:
1687
1784
  logger.info("Model uploaded successfully. Skipping deployment setup.")
1688
1785
  return
1689
1786
 
1690
- # Setup deployment for the uploaded model
1691
- setup_deployment_for_model(builder)
1787
+
1788
+ def deploy_model(
1789
+ model_url=None,
1790
+ model_id=None,
1791
+ app_id=None,
1792
+ user_id=None,
1793
+ deployment_id=None,
1794
+ model_version_id=None,
1795
+ nodepool_id=None,
1796
+ compute_cluster_id=None,
1797
+ cluster_user_id=None,
1798
+ min_replicas=0,
1799
+ max_replicas=5,
1800
+ ):
1801
+ """
1802
+ Deploy a model on Clarifai platform.
1803
+ Args:
1804
+ model_url (str): The full Clarifai model URL (optional if model_id is provided).
1805
+ model_id (str): The ID of the model to be deployed (optional if model_url is provided).
1806
+ app_id (str): The application ID where the model resides.
1807
+ user_id (str): The user ID who owns the model.
1808
+ deployment_id (str): The ID for the new deployment.
1809
+ model_version_id (str): The version ID of the model to deploy. If not provided, the latest version will be used.
1810
+ nodepool_id (str): The ID of the nodepool where the deployment will be created.
1811
+ compute_cluster_id (str): The ID of the compute cluster to use for deployment.
1812
+ cluster_user_id (str): The user ID that owns the compute cluster.
1813
+ min_replicas (int): Minimum number of replicas for autoscaling.
1814
+ max_replicas (int): Maximum number of replicas for autoscaling.
1815
+ """
1816
+ if model_url and model_id:
1817
+ raise UserError("You can only specify one of url or model_id.")
1818
+ if not model_url and not model_id:
1819
+ raise UserError("You must specify one of url or model_id.")
1820
+ if model_url:
1821
+ user_id, app_id, _, model_id, _ = ClarifaiUrlHelper.split_clarifai_url(model_url)
1822
+ if not model_version_id:
1823
+ model = Model(model_id=model_id, app_id=app_id, user_id=user_id)
1824
+ model_versions = [v for v in model.list_versions()]
1825
+ if not model_versions:
1826
+ raise UserError(f"No versions found for model {model_id}.")
1827
+ if len(model_versions) > 1:
1828
+ # model_version_id = model_versions[len(model_versions) - 1].model_version.id # Use the first version
1829
+ model_version_id = model_versions[0].model_version.id # latest version
1830
+
1831
+ # Construct the full deployment config
1832
+ deployment_config = {
1833
+ "deployment": {
1834
+ "id": deployment_id,
1835
+ "user_id": user_id,
1836
+ "description": "Model deployment created to test Model upload",
1837
+ "autoscale_config": {
1838
+ "min_replicas": min_replicas,
1839
+ "max_replicas": max_replicas,
1840
+ "traffic_history_seconds": 600,
1841
+ "scale_down_delay_seconds": 300,
1842
+ "scale_to_zero_delay_seconds": 3600,
1843
+ "scale_up_delay_seconds": 300,
1844
+ },
1845
+ "worker": {
1846
+ "model": {
1847
+ "id": model_id,
1848
+ "model_version": {
1849
+ "id": model_version_id,
1850
+ },
1851
+ "user_id": user_id,
1852
+ "app_id": app_id,
1853
+ }
1854
+ },
1855
+ "scheduling_choice": 4, # "performance"
1856
+ "nodepools": [
1857
+ {
1858
+ "id": nodepool_id,
1859
+ "compute_cluster": {
1860
+ "id": compute_cluster_id,
1861
+ "user_id": cluster_user_id,
1862
+ },
1863
+ }
1864
+ ],
1865
+ "visibility": {"gettable": 50},
1866
+ }
1867
+ }
1868
+
1869
+ try:
1870
+ # Instantiate Nodepool and create the deployment
1871
+ nodepool = Nodepool(nodepool_id=nodepool_id, user_id=user_id)
1872
+ deployment = nodepool.create_deployment(
1873
+ deployment_id=deployment_id, deployment_config=deployment_config
1874
+ )
1875
+
1876
+ print(
1877
+ f"✅ Deployment '{deployment_id}' successfully created for model '{model_id}' with version '{model_version_id}'."
1878
+ )
1879
+ return True
1880
+ except Exception as e:
1881
+ print(f"❌ Failed to create deployment '{deployment_id}': {e}")
1882
+ return False
1692
1883
 
1693
1884
 
1694
1885
  def setup_deployment_for_model(builder):
1695
1886
  """
1696
1887
  Set up deployment for a model after upload.
1697
1888
 
1698
- :param builder: The ModelBuilder instance that has uploaded the model.
1889
+ Args:
1890
+ builder: The ModelBuilder instance that has uploaded the model.
1699
1891
  """
1700
1892
 
1893
+ print("\n🚀 Model Deployment")
1894
+ state = {
1895
+ 'uploaded': True,
1896
+ 'deployed': False,
1897
+ }
1701
1898
  model = builder.config.get('model')
1702
- user_id = model.get('user_id')
1703
- app_id = model.get('app_id')
1704
- model_id = model.get('id')
1705
-
1706
- # Set up the API client with the user's credentials
1707
- user = User(user_id=user_id, pat=builder.client.pat, base_url=builder.client.base)
1708
-
1709
- # Step 1: Check for available compute clusters and let user choose or create a new one
1710
- logger.info("Checking for available compute clusters...")
1711
- compute_clusters = list(user.list_compute_clusters())
1899
+ state.update(
1900
+ {
1901
+ 'user_id': model.get('user_id'),
1902
+ 'app_id': model.get('app_id'),
1903
+ 'model_id': model.get('id'),
1904
+ 'model_version_id': builder.model_version_id,
1905
+ }
1906
+ )
1712
1907
 
1713
- compute_cluster = None
1714
- if compute_clusters:
1715
- logger.info("Available compute clusters:")
1716
- for i, cc in enumerate(compute_clusters):
1717
- logger.info(
1718
- f"{i + 1}. {cc.id} ({cc.description if hasattr(cc, 'description') else 'No description'})"
1719
- )
1908
+ # Select compute options
1909
+ compute_config = select_compute_option(user_id=state['user_id'])
1720
1910
 
1721
- choice = input(
1722
- f"Choose a compute cluster (1-{len(compute_clusters)}) or 'n' to create a new one: "
1723
- )
1724
- if choice.lower() == 'n':
1725
- create_new_cc = True
1726
- else:
1727
- try:
1728
- idx = int(choice) - 1
1729
- if 0 <= idx < len(compute_clusters):
1730
- compute_cluster = compute_clusters[idx]
1731
- create_new_cc = False
1732
- else:
1733
- logger.info("Invalid choice. Creating a new compute cluster.")
1734
- create_new_cc = True
1735
- except ValueError:
1736
- logger.info("Invalid choice. Creating a new compute cluster.")
1737
- create_new_cc = True
1738
- else:
1739
- logger.info("No compute clusters found.")
1740
- create_new_cc = True
1741
-
1742
- if create_new_cc:
1743
- # Provide URL to create a new compute cluster
1744
- url_helper = ClarifaiUrlHelper()
1745
- compute_cluster_url = f"{url_helper.ui}/settings/compute/new"
1746
- logger.info(f"Please create a new compute cluster by visiting: {compute_cluster_url}")
1747
-
1748
- # Ask if they want to open the URL in browser
1749
- open_browser = input(
1750
- "Do you want to open the compute cluster creation page in your browser? (y/n): "
1911
+ # Get deployment configuration
1912
+ print("\n⌨️ Enter Deployment Configuration:")
1913
+ deployment_id = get_user_input(
1914
+ "Enter deployment ID", default=f"deploy-{state['model_id']}-{uuid.uuid4().hex[:6]}"
1915
+ )
1916
+ min_replicas = int(get_user_input("Enter minimum replicas", default="1"))
1917
+ max_replicas = int(get_user_input("Enter maximum replicas", default="5"))
1918
+
1919
+ print("\n⏳ Deploying model...")
1920
+
1921
+ # Retry logic for deployment
1922
+ max_retries = 1
1923
+ for attempt in range(max_retries):
1924
+ success = deploy_model(
1925
+ model_id=state['model_id'],
1926
+ app_id=state['app_id'],
1927
+ user_id=state['user_id'],
1928
+ deployment_id=deployment_id,
1929
+ model_version_id=state['model_version_id'],
1930
+ nodepool_id=compute_config['nodepool_id'],
1931
+ compute_cluster_id=compute_config['compute_cluster_id'],
1932
+ cluster_user_id=compute_config['cluster_user_id'],
1933
+ min_replicas=min_replicas,
1934
+ max_replicas=max_replicas,
1751
1935
  )
1752
- if open_browser.lower() == 'y':
1753
- try:
1754
- webbrowser.open(compute_cluster_url)
1755
- except Exception as e:
1756
- logger.error(f"Failed to open browser: {e}")
1757
1936
 
1758
- input("After creating the compute cluster, press Enter to continue...")
1937
+ if success:
1938
+ state.update(
1939
+ {
1940
+ 'deployed': True,
1941
+ 'deployment_id': deployment_id,
1942
+ 'nodepool_id': compute_config['nodepool_id'],
1943
+ }
1944
+ )
1945
+ print("Model deployed successfully! You can test it now.")
1946
+ time.sleep(2) # Give some time for the deployment to stabilize
1947
+ elif attempt < max_retries - 1:
1948
+ if get_yes_no_input("Deployment failed. Do you want to retry?", True):
1949
+ continue
1759
1950
 
1760
- # Re-fetch the compute clusters list after user has created one
1761
- logger.info("Re-checking for available compute clusters...")
1762
- compute_clusters = list(user.list_compute_clusters())
1951
+ if get_yes_no_input("\n🗑️ Do you want to backtrack and clean up?", True):
1952
+ backtrack_workflow(state)
1763
1953
 
1764
- if not compute_clusters:
1765
- logger.info(
1766
- "No compute clusters found. Please make sure you have created a compute cluster and try again."
1767
- )
1768
- return
1769
1954
 
1770
- # Show the updated list and let user choose
1771
- logger.info("Available compute clusters:")
1772
- for i, cc in enumerate(compute_clusters):
1773
- logger.info(
1774
- f"{i + 1}. {cc.id} ({cc.description if hasattr(cc, 'description') else 'No description'})"
1775
- )
1955
+ def delete_model_deployment(deployment_id, user_id, nodepool_id=None):
1956
+ """
1957
+ Delete a model deployment on Clarifai platform.
1776
1958
 
1777
- choice = input(f"Choose a compute cluster (1-{len(compute_clusters)}): ")
1778
- try:
1779
- idx = int(choice) - 1
1780
- if 0 <= idx < len(compute_clusters):
1781
- compute_cluster = compute_clusters[idx]
1782
- else:
1783
- logger.info("Invalid choice. Aborting deployment setup.")
1784
- return
1785
- except ValueError:
1786
- logger.info("Invalid choice. Aborting deployment setup.")
1787
- return
1959
+ Args:
1960
+ deployment_id (str): The ID of the deployment to be deleted.
1961
+ nodepool_id (str): The ID of the nodepool where the deployment resides.
1962
+ user_id (str): The Clarifai user ID (usually owner of the deployment).
1963
+ """
1788
1964
 
1789
- # Step 2: Check for available nodepools and let user choose or create a new one
1790
- logger.info(f"Checking for available nodepools in compute cluster '{compute_cluster.id}'...")
1791
- nodepools = list(compute_cluster.list_nodepools())
1965
+ # Instantiate the Nodepool object with given IDs
1966
+ nodepool = Nodepool(nodepool_id=nodepool_id, user_id=user_id)
1967
+ # The delete_deployments method expects a list of deployment IDs
1968
+ try:
1969
+ nodepool.delete_deployments([deployment_id])
1970
+ print(f"✅ Deployment '{deployment_id}' has been successfully deleted.")
1971
+ return True
1972
+ except Exception as e:
1973
+ print(f"❌ Failed to delete deployment '{deployment_id}': {e}")
1974
+ return False
1792
1975
 
1793
- nodepool = None
1794
- if nodepools:
1795
- logger.info("Available nodepools:")
1796
- for i, np in enumerate(nodepools):
1797
- logger.info(
1798
- f"{i + 1}. {np.id} ({np.description if hasattr(np, 'description') else 'No description'})"
1799
- )
1800
1976
 
1801
- choice = input(f"Choose a nodepool (1-{len(nodepools)}) or 'n' to create a new one: ")
1802
- if choice.lower() == 'n':
1803
- create_new_np = True
1804
- else:
1805
- try:
1806
- idx = int(choice) - 1
1807
- if 0 <= idx < len(nodepools):
1808
- nodepool = nodepools[idx]
1809
- create_new_np = False
1810
- else:
1811
- logger.info("Invalid choice. Creating a new nodepool.")
1812
- create_new_np = True
1813
- except ValueError:
1814
- logger.info("Invalid choice. Creating a new nodepool.")
1815
- create_new_np = True
1816
- else:
1817
- logger.info("No nodepools found in this compute cluster.")
1818
- create_new_np = True
1819
-
1820
- if create_new_np:
1821
- # Provide URL to create a new nodepool
1822
- url_helper = ClarifaiUrlHelper()
1823
- nodepool_url = f"{url_helper.ui}/settings/compute/{compute_cluster.id}/nodepools/new"
1824
- logger.info(f"Please create a new nodepool by visiting: {nodepool_url}")
1825
-
1826
- # Ask if they want to open the URL in browser
1827
- open_browser = input(
1828
- "Do you want to open the nodepool creation page in your browser? (y/n): "
1829
- )
1830
- if open_browser.lower() == 'y':
1831
- try:
1832
- webbrowser.open(nodepool_url)
1833
- except Exception as e:
1834
- logger.error(f"Failed to open browser: {e}")
1977
+ def delete_model_version(
1978
+ model_url=None, model_id=None, app_id=None, user_id=None, model_version_id=None
1979
+ ):
1980
+ """
1981
+ Delete a specific version of a model on Clarifai platform.
1982
+ Args:
1983
+ model_url (str): The full Clarifai model URL (optional if model_id is provided).
1984
+ model_id (str): The ID of the model (optional if model_url is provided).
1985
+ app_id (str): The ID of the application the model belongs to.
1986
+ user_id (str): The ID of the user who owns the model.
1987
+ model_version_id (str): The ID of the model version to be deleted.
1988
+ """
1989
+ if not model_version_id:
1990
+ raise UserError("You must specify a model_version_id to delete.")
1991
+ if model_url and model_id:
1992
+ raise UserError("You can only specify one of url or model_id.")
1993
+ if not model_url and not model_id:
1994
+ raise UserError("You must specify one of url or model_id.")
1995
+ if model_url:
1996
+ user_id, app_id, _, model_id, _ = ClarifaiUrlHelper.split_clarifai_url(model_url)
1997
+ model = Model(model_id=model_id, app_id=app_id, user_id=user_id)
1998
+ try:
1999
+ model.delete_version(version_id=model_version_id)
2000
+ print(f" Model version '{model_version_id}' successfully deleted.")
2001
+ return True
2002
+ except Exception as e:
2003
+ print(f"❌ Failed to delete model version '{model_version_id}': {e}")
2004
+ return False
1835
2005
 
1836
- input("After creating the nodepool, press Enter to continue...")
1837
2006
 
1838
- # Re-fetch the nodepools list after user has created one
1839
- logger.info(
1840
- f"Re-checking for available nodepools in compute cluster '{compute_cluster.id}'..."
1841
- )
1842
- nodepools = list(compute_cluster.list_nodepools())
2007
+ def backtrack_workflow(state):
2008
+ """Handle backtracking when operations fail."""
2009
+ print("\n🔄 Starting backtrack process...")
1843
2010
 
1844
- if not nodepools:
1845
- logger.info(
1846
- "No nodepools found. Please make sure you have created a nodepool in the selected compute cluster and try again."
2011
+ # Delete deployment if it was created
2012
+ if state.get('deployed') and state.get('deployment_id'):
2013
+ if get_yes_no_input("Do you want to delete the deployment?", True):
2014
+ success = delete_model_deployment(
2015
+ deployment_id=state['deployment_id'],
2016
+ user_id=state['user_id'],
2017
+ nodepool_id=state.get('nodepool_id'),
1847
2018
  )
1848
- return
1849
-
1850
- # Show the updated list and let user choose
1851
- logger.info("Available nodepools:")
1852
- for i, np in enumerate(nodepools):
1853
- logger.info(
1854
- f"{i + 1}. {np.id} ({np.description if hasattr(np, 'description') else 'No description'})"
2019
+ if success:
2020
+ state['deployed'] = False
2021
+
2022
+ # Delete model version if it was uploaded
2023
+ if state.get('uploaded') and state.get('model_version_id'):
2024
+ if get_yes_no_input("Do you want to delete the model version?", False):
2025
+ success = delete_model_version(
2026
+ model_id=state['model_id'],
2027
+ app_id=state['app_id'],
2028
+ user_id=state['user_id'],
2029
+ model_version_id=state['model_version_id'],
1855
2030
  )
1856
-
1857
- choice = input(f"Choose a nodepool (1-{len(nodepools)}): ")
1858
- try:
1859
- idx = int(choice) - 1
1860
- if 0 <= idx < len(nodepools):
1861
- nodepool = nodepools[idx]
1862
- else:
1863
- logger.info("Invalid choice. Aborting deployment setup.")
1864
- return
1865
- except ValueError:
1866
- logger.info("Invalid choice. Aborting deployment setup.")
1867
- return
1868
-
1869
- # Step 3: Help create a new deployment by providing URL
1870
- # Provide URL to create a new deployment
1871
- url_helper = ClarifaiUrlHelper()
1872
- deployment_url = f"{url_helper.ui}/compute/deployments/create?computeClusterId={compute_cluster.id}&nodePoolId={nodepool.id}"
1873
- logger.info(f"Please create a new deployment by visiting: {deployment_url}")
1874
-
1875
- # Ask if they want to open the URL in browser
1876
- open_browser = input(
1877
- "Do you want to open the deployment creation page in your browser? (y/n): "
1878
- )
1879
- if open_browser.lower() == 'y':
1880
- try:
1881
- webbrowser.open(deployment_url)
1882
- except Exception as e:
1883
- logger.error(f"Failed to open browser: {e}")
1884
-
1885
- logger.info("After creating the deployment, your model will be ready for inference!")
2031
+ if success:
2032
+ state['uploaded'] = False
2033
+ state['model_version_id'] = None
@@ -23,6 +23,9 @@ from clarifai.runners.utils.serializers import (
23
23
  TupleSerializer,
24
24
  )
25
25
 
26
+ # Reserved parameter name for protobuf response access
27
+ RESERVED_PARAM_WITH_PROTO = 'with_proto'
28
+
26
29
 
27
30
  def build_function_signature(func):
28
31
  '''
@@ -45,6 +48,12 @@ def build_function_signature(func):
45
48
  input_sigs = []
46
49
  input_streaming = []
47
50
  for p in sig.parameters.values():
51
+ # Validate that user methods don't use reserved parameter names
52
+ if p.name == RESERVED_PARAM_WITH_PROTO:
53
+ raise ValueError(
54
+ f"Parameter name '{RESERVED_PARAM_WITH_PROTO}' is reserved and cannot be used in model methods. "
55
+ f"This parameter is automatically added by the framework to provide access to protobuf responses."
56
+ )
48
57
  model_type_field, _, streaming = build_variable_signature(p.name, p.annotation, p.default)
49
58
  input_sigs.append(model_type_field)
50
59
  input_streaming.append(streaming)
@@ -66,6 +66,7 @@ DEFAULT_OLLAMA_MODEL_REPO_BRANCH = "ollama"
66
66
  DEFAULT_HF_MODEL_REPO_BRANCH = "huggingface"
67
67
  DEFAULT_LMSTUDIO_MODEL_REPO_BRANCH = "lmstudio"
68
68
  DEFAULT_VLLM_MODEL_REPO_BRANCH = "vllm"
69
+ DEFAULT_SGLANG_MODEL_REPO_BRANCH = "sglang"
69
70
  DEFAULT_PYTHON_MODEL_REPO_BRANCH = "python"
70
71
 
71
72
  STATUS_OK = "200 OK"
clarifai/versions.py CHANGED
@@ -1,3 +1,4 @@
1
+ import importlib.util
1
2
  import os
2
3
 
3
4
  from clarifai import __version__
@@ -7,3 +8,25 @@ OS_VER = os.sys.platform
7
8
  PYTHON_VERSION = '.'.join(
8
9
  map(str, [os.sys.version_info.major, os.sys.version_info.minor, os.sys.version_info.micro])
9
10
  )
11
+
12
+
13
+ def get_latest_version_from_pypi():
14
+ """
15
+ Fetch the latest version of the clarifai package from PyPI.
16
+
17
+ Returns:
18
+ str: The latest version string, or None if the request fails.
19
+ """
20
+ # Check if requests is installed
21
+ if importlib.util.find_spec("requests") is None:
22
+ return None
23
+
24
+ try:
25
+ import requests
26
+
27
+ response = requests.get("https://pypi.org/pypi/clarifai/json", timeout=5)
28
+ if response.status_code == 200:
29
+ return response.json().get("info", {}).get("version")
30
+ return None
31
+ except Exception:
32
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clarifai
3
- Version: 11.8.4
3
+ Version: 11.9.0
4
4
  Home-page: https://github.com/Clarifai/clarifai-python
5
5
  Author: Clarifai
6
6
  Author-email: support@clarifai.com
@@ -19,8 +19,8 @@ Classifier: Operating System :: OS Independent
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: clarifai-grpc>=11.8.2
23
- Requires-Dist: clarifai-protocol>=0.0.32
22
+ Requires-Dist: clarifai-grpc>=11.9.8
23
+ Requires-Dist: clarifai-protocol>=0.0.33
24
24
  Requires-Dist: numpy>=1.22.0
25
25
  Requires-Dist: tqdm>=4.65.0
26
26
  Requires-Dist: PyYAML>=6.0.1
@@ -1,14 +1,14 @@
1
- clarifai/__init__.py,sha256=l80v8IYM70DFAKN9bwXRrDCRPmv0ykn84eJo3--0MWA,23
1
+ clarifai/__init__.py,sha256=SXY1e-dnGDUvRtF27y_ABoyLFsY29JXPVQsa1fJZTDU,23
2
2
  clarifai/cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  clarifai/errors.py,sha256=GXa6D4v_L404J83jnRNFPH7s-1V9lk7w6Ws99f1g-AY,2772
4
- clarifai/versions.py,sha256=ecSuEB_nOL2XSoYHDw2n23XUbm_KPOGjudMXmQrGdS8,224
4
+ clarifai/versions.py,sha256=E3_bdZmzlWko0Y0XSSpxDyNAU_7JRL533YsWv_ZrrEQ,843
5
5
  clarifai/cli/README.md,sha256=Rm0Sk61u7PrWQBgA6idv0R6ldJGhnVHSlIF2rVzRk8g,3428
6
6
  clarifai/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  clarifai/cli/__main__.py,sha256=7nPbLW7Jr2shkgMPvnxpn4xYGMvIcnqluJ69t9w4H_k,74
8
8
  clarifai/cli/base.py,sha256=FQEEmi3a9_LBOmM_-X4EYdpAmDK1UljTxrHOIIsOZbM,10696
9
9
  clarifai/cli/compute_cluster.py,sha256=8Xss0Obrp6l1XuxJe0luOqU_pf8vXGDRi6jyIe8qR6k,2282
10
10
  clarifai/cli/deployment.py,sha256=9C4I6_kyMxRkWl6h681wc79-3mAtDHtTUaxRv05OZMs,4262
11
- clarifai/cli/model.py,sha256=BN-hjSdpgju3kh6edi-0uBNVaroPxuSuMkgeevIQPPk,52722
11
+ clarifai/cli/model.py,sha256=_tYdWdCpeXgbBZ-232DRPXYixci6o6p0Ig0LIH_vjwc,53004
12
12
  clarifai/cli/nodepool.py,sha256=H6OIdUW_EiyDUwZogzEDoYmVwEjLMsgoDlPyE7gjIuU,4245
13
13
  clarifai/cli/pipeline.py,sha256=bH7pnJGfjQIu_Y-f_zwKBFdPDTsBEEDtA2Oz9P6-Zj0,17129
14
14
  clarifai/cli/pipeline_step.py,sha256=dvoC2vAsDcxOCy88VV0X42PG22_7JSu9sfBVsk-Cix4,6133
@@ -16,7 +16,7 @@ clarifai/cli/templates/__init__.py,sha256=HbMlZuYOMyVJde73ijNAevmSRUpIttGlHdwyO4
16
16
  clarifai/cli/templates/model_templates.py,sha256=OU3qgYXSITo5qp0mkRiisXOMNhj6wNKWlZUFnAFZfGE,10090
17
17
  clarifai/cli/templates/pipeline_step_templates.py,sha256=w1IJghF_4wWyEmHR1925N0hpGKocy3G7ezhxTH-0XCc,1716
18
18
  clarifai/cli/templates/pipeline_templates.py,sha256=iLVxkmd0usc7jervTZTFzLwRVVF_623RszGW-oIuPDw,4234
19
- clarifai/client/__init__.py,sha256=MWEG_jTGn6UWbGCznsZxObJ5h65k2igD1462qID2pgI,840
19
+ clarifai/client/__init__.py,sha256=l3KOOnD7S6FmSLLwkbZB0FjvOWbWjqdSnnM1Pt56nCY,1051
20
20
  clarifai/client/app.py,sha256=8_Y5esptlFSGW2WS0uly9IBop37hJVpxnhs-vBIZrtc,47205
21
21
  clarifai/client/base.py,sha256=eztsqJyQkaXAUNVBBhAhUNlDwgHTOzjPL1KcjblqWGU,12088
22
22
  clarifai/client/compute_cluster.py,sha256=q-JuBai4pKpSeDAn-P61IKyGjizX-IV-O5huWAYx5DU,10279
@@ -25,7 +25,7 @@ clarifai/client/deployment.py,sha256=QBf0tzkKBEpzNgmOEmWUJMOlVWdFEFc70Y44o8y75Gs
25
25
  clarifai/client/input.py,sha256=jpX47qwn7aUBBIEuSSLHF5jk70XaWEh0prD065c9b-E,51205
26
26
  clarifai/client/lister.py,sha256=1YEm2suNxPaJO4x9V5szgD_YX6N_00vgSO-7m0HagY8,2208
27
27
  clarifai/client/model.py,sha256=uOnR1GPOJUoNWDmd_Ja9XJq8oRPa1frTxBQ_ypQXc8Q,93518
28
- clarifai/client/model_client.py,sha256=8N8dRqb5zfFCNxq-jc-YSL19tgS8PpevnxY69G2YzCE,38280
28
+ clarifai/client/model_client.py,sha256=bO9gMIvGRPSrOUKvjMXJmW4-1tb8tHYiHzvaJCZanIk,39609
29
29
  clarifai/client/module.py,sha256=pTcTmR48-VQRCEj3PJK_ZT5YTsYfZDbEjxwJ43rcLMM,4753
30
30
  clarifai/client/nodepool.py,sha256=QDJOMOYrZAG962u-MZWjXOZifjWK8hDgS2zoUn59dZU,16751
31
31
  clarifai/client/pipeline.py,sha256=y4swF2LsTag8l_-CT1jN4RXEwp_YYYkO6P_n97Cq1kg,15961
@@ -77,7 +77,7 @@ clarifai/runners/dockerfile_template/Dockerfile.template,sha256=nEnIMqzhAXDbd0Ht
77
77
  clarifai/runners/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  clarifai/runners/models/dummy_openai_model.py,sha256=LfAJu173uTEYsN6xpaJ6TyimohyTooX16H6YYk9qVLs,8636
79
79
  clarifai/runners/models/mcp_class.py,sha256=RdKn7rW4vYol0VRDZiLTSMfkqjLhO1ijXAQ0Rq0Jfnw,6647
80
- clarifai/runners/models/model_builder.py,sha256=8sgjI9l-4B4-8LxE7gD2POBw5t1ME86Qbb41ZD6qBi8,80827
80
+ clarifai/runners/models/model_builder.py,sha256=5c-3d6J1FnN-5LPFKP2yxuAdPY_LjdHQCkNrqAsBdMA,86050
81
81
  clarifai/runners/models/model_class.py,sha256=Ndh437BNMkpFBo6B108GuKL8sGYaGnSplZ6FxOgd_v8,20010
82
82
  clarifai/runners/models/model_run_locally.py,sha256=6-6WjEKc0ba3gAv4wOLdMs2XOzS3b-2bZHJS0wdVqJY,20088
83
83
  clarifai/runners/models/model_runner.py,sha256=jzq72S0Fz3C0rt9OSyZxok2EN6f_-K5z-0TmupdiQP4,14421
@@ -94,7 +94,7 @@ clarifai/runners/utils/code_script.py,sha256=-6IgNruIMTYLKJG8EqVWSaZR7lFRBoQ4ufJ
94
94
  clarifai/runners/utils/const.py,sha256=MK7lTzzJKbOiyiUtG_jlJXfz_xNKMn5LjkQ9vjbttXE,1538
95
95
  clarifai/runners/utils/data_utils.py,sha256=HRpMYR2O0OiDpXXhOManLHTeomC4bFnXMHVAiT_12yE,20856
96
96
  clarifai/runners/utils/loader.py,sha256=hQwdRKRLgrunDzLTiqjz2qVdxiL8WQ04PUSwrh-JWZ0,12077
97
- clarifai/runners/utils/method_signatures.py,sha256=qdHaO8ZIgP6BBXXMhMPhcQ46dse-XMP2t4VJCNG7O3Q,18335
97
+ clarifai/runners/utils/method_signatures.py,sha256=wOQnt3WVTnLBg0fhq2AxIb-2zdorYZ5aZPqlIwAybz8,18825
98
98
  clarifai/runners/utils/model_utils.py,sha256=MCYhV_00anptIO1Qtxgzn3DlGI2UHV0ESFT3wLNcAMM,6413
99
99
  clarifai/runners/utils/openai_convertor.py,sha256=ZlIrvvfHttD_DavLvmKZdL8gNq_TQvQtZVnYamwdWz4,8248
100
100
  clarifai/runners/utils/pipeline_validation.py,sha256=GFgFbrlS0UvGXcYTS84CyX7hIVU2CUNVQiMnCAjNfhw,6444
@@ -107,7 +107,7 @@ clarifai/urls/helper.py,sha256=z6LnLGgLHxD8scFtyRdxqYIRJNhxqPkfLe53UtTLUBY,11727
107
107
  clarifai/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
108
  clarifai/utils/cli.py,sha256=3PhMdb84mjrwcxkRuhxX71UXBgh-Lcave7xURJf5inE,16548
109
109
  clarifai/utils/config.py,sha256=dENYtcWW7Il5MInvIaYe0MZn0hW1fbIb0Lzk8rQ_geQ,7671
110
- clarifai/utils/constants.py,sha256=2LGYqi55-YaHLfFNLZSrZNuChJ8rm7_voBysSFtoQF8,2541
110
+ clarifai/utils/constants.py,sha256=zSQXQh8oFXuiZLWdnvm424g_llVY0QnyjKLBx3PSf2Y,2585
111
111
  clarifai/utils/hashing.py,sha256=z2hHt4sDvGyqNbnOay0F2i3K_PjyX-J24IEytszyYsA,3761
112
112
  clarifai/utils/logging.py,sha256=0we53uTqUvzrulC86whu-oeWNxn1JjJL0OQ98Bwf9vo,15198
113
113
  clarifai/utils/misc.py,sha256=ATj4RR6S06GeLE0X4tMU4bmTz4Sz4j2WemTddsnSfMI,23458
@@ -122,9 +122,9 @@ clarifai/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
122
122
  clarifai/workflows/export.py,sha256=HvUYG9N_-UZoRR0-_tdGbZ950_AeBqawSppgUxQebR0,1913
123
123
  clarifai/workflows/utils.py,sha256=ESL3INcouNcLKCh-nMpfXX-YbtCzX7tz7hT57_RGQ3M,2079
124
124
  clarifai/workflows/validate.py,sha256=UhmukyHkfxiMFrPPeBdUTiCOHQT5-shqivlBYEyKTlU,2931
125
- clarifai-11.8.4.dist-info/licenses/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
126
- clarifai-11.8.4.dist-info/METADATA,sha256=mNT5gLQe6QyrWy_kKhF-XCSAqR2F932MDYyTEy8aPQ4,23193
127
- clarifai-11.8.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
128
- clarifai-11.8.4.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
129
- clarifai-11.8.4.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
130
- clarifai-11.8.4.dist-info/RECORD,,
125
+ clarifai-11.9.0.dist-info/licenses/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
126
+ clarifai-11.9.0.dist-info/METADATA,sha256=yfVt_BCrRW5q3GB519QmYxbqwJgbAGIx_xLWQJVzUoA,23193
127
+ clarifai-11.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
128
+ clarifai-11.9.0.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
129
+ clarifai-11.9.0.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
130
+ clarifai-11.9.0.dist-info/RECORD,,