clarifai 11.6.4rc2__py3-none-any.whl → 11.6.6__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 (200) hide show
  1. clarifai/__init__.py +1 -1
  2. clarifai/cli/README.md +39 -0
  3. clarifai/cli/base.py +119 -118
  4. clarifai/cli/model.py +116 -86
  5. clarifai/client/app.py +1 -1
  6. clarifai/client/auth/helper.py +7 -2
  7. clarifai/client/base.py +35 -8
  8. clarifai/client/compute_cluster.py +1 -1
  9. clarifai/client/model.py +3 -1
  10. clarifai/client/model_client.py +20 -1
  11. clarifai/client/nodepool.py +4 -2
  12. clarifai/client/user.py +4 -2
  13. clarifai/runners/models/model_runner.py +55 -0
  14. clarifai/runners/server.py +23 -1
  15. clarifai/runners/utils/model_utils.py +4 -4
  16. clarifai/runners/utils/url_fetcher.py +51 -12
  17. clarifai/utils/cli.py +148 -13
  18. clarifai/utils/constants.py +5 -0
  19. clarifai/utils/misc.py +47 -0
  20. {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/METADATA +2 -2
  21. clarifai-11.6.6.dist-info/RECORD +127 -0
  22. {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/WHEEL +1 -1
  23. clarifai/__pycache__/__init__.cpython-311.pyc +0 -0
  24. clarifai/__pycache__/__init__.cpython-39.pyc +0 -0
  25. clarifai/__pycache__/errors.cpython-311.pyc +0 -0
  26. clarifai/__pycache__/errors.cpython-39.pyc +0 -0
  27. clarifai/__pycache__/versions.cpython-311.pyc +0 -0
  28. clarifai/__pycache__/versions.cpython-39.pyc +0 -0
  29. clarifai/cli/__pycache__/__init__.cpython-39.pyc +0 -0
  30. clarifai/cli/__pycache__/base.cpython-39.pyc +0 -0
  31. clarifai/cli/__pycache__/compute_cluster.cpython-39.pyc +0 -0
  32. clarifai/cli/__pycache__/deployment.cpython-39.pyc +0 -0
  33. clarifai/cli/__pycache__/model.cpython-39.pyc +0 -0
  34. clarifai/cli/__pycache__/nodepool.cpython-39.pyc +0 -0
  35. clarifai/cli/model_templates.py +0 -243
  36. clarifai/cli/pipeline_step_templates.py +0 -64
  37. clarifai/client/__pycache__/__init__.cpython-311.pyc +0 -0
  38. clarifai/client/__pycache__/__init__.cpython-39.pyc +0 -0
  39. clarifai/client/__pycache__/app.cpython-311.pyc +0 -0
  40. clarifai/client/__pycache__/app.cpython-39.pyc +0 -0
  41. clarifai/client/__pycache__/base.cpython-311.pyc +0 -0
  42. clarifai/client/__pycache__/base.cpython-39.pyc +0 -0
  43. clarifai/client/__pycache__/compute_cluster.cpython-311.pyc +0 -0
  44. clarifai/client/__pycache__/dataset.cpython-311.pyc +0 -0
  45. clarifai/client/__pycache__/dataset.cpython-39.pyc +0 -0
  46. clarifai/client/__pycache__/deployment.cpython-311.pyc +0 -0
  47. clarifai/client/__pycache__/deployment.cpython-39.pyc +0 -0
  48. clarifai/client/__pycache__/input.cpython-311.pyc +0 -0
  49. clarifai/client/__pycache__/input.cpython-39.pyc +0 -0
  50. clarifai/client/__pycache__/lister.cpython-311.pyc +0 -0
  51. clarifai/client/__pycache__/lister.cpython-39.pyc +0 -0
  52. clarifai/client/__pycache__/model.cpython-311.pyc +0 -0
  53. clarifai/client/__pycache__/model.cpython-39.pyc +0 -0
  54. clarifai/client/__pycache__/model_client.cpython-311.pyc +0 -0
  55. clarifai/client/__pycache__/model_client.cpython-39.pyc +0 -0
  56. clarifai/client/__pycache__/module.cpython-311.pyc +0 -0
  57. clarifai/client/__pycache__/nodepool.cpython-311.pyc +0 -0
  58. clarifai/client/__pycache__/runner.cpython-311.pyc +0 -0
  59. clarifai/client/__pycache__/search.cpython-311.pyc +0 -0
  60. clarifai/client/__pycache__/user.cpython-311.pyc +0 -0
  61. clarifai/client/__pycache__/workflow.cpython-311.pyc +0 -0
  62. clarifai/client/auth/__pycache__/__init__.cpython-311.pyc +0 -0
  63. clarifai/client/auth/__pycache__/__init__.cpython-39.pyc +0 -0
  64. clarifai/client/auth/__pycache__/helper.cpython-311.pyc +0 -0
  65. clarifai/client/auth/__pycache__/helper.cpython-39.pyc +0 -0
  66. clarifai/client/auth/__pycache__/register.cpython-311.pyc +0 -0
  67. clarifai/client/auth/__pycache__/register.cpython-39.pyc +0 -0
  68. clarifai/client/auth/__pycache__/stub.cpython-311.pyc +0 -0
  69. clarifai/client/auth/__pycache__/stub.cpython-39.pyc +0 -0
  70. clarifai/constants/__pycache__/base.cpython-311.pyc +0 -0
  71. clarifai/constants/__pycache__/base.cpython-39.pyc +0 -0
  72. clarifai/constants/__pycache__/dataset.cpython-311.pyc +0 -0
  73. clarifai/constants/__pycache__/dataset.cpython-39.pyc +0 -0
  74. clarifai/constants/__pycache__/input.cpython-311.pyc +0 -0
  75. clarifai/constants/__pycache__/input.cpython-39.pyc +0 -0
  76. clarifai/constants/__pycache__/model.cpython-311.pyc +0 -0
  77. clarifai/constants/__pycache__/model.cpython-39.pyc +0 -0
  78. clarifai/constants/__pycache__/search.cpython-311.pyc +0 -0
  79. clarifai/constants/__pycache__/workflow.cpython-311.pyc +0 -0
  80. clarifai/datasets/__pycache__/__init__.cpython-311.pyc +0 -0
  81. clarifai/datasets/__pycache__/__init__.cpython-39.pyc +0 -0
  82. clarifai/datasets/export/__pycache__/__init__.cpython-311.pyc +0 -0
  83. clarifai/datasets/export/__pycache__/__init__.cpython-39.pyc +0 -0
  84. clarifai/datasets/export/__pycache__/inputs_annotations.cpython-311.pyc +0 -0
  85. clarifai/datasets/export/__pycache__/inputs_annotations.cpython-39.pyc +0 -0
  86. clarifai/datasets/upload/__pycache__/__init__.cpython-311.pyc +0 -0
  87. clarifai/datasets/upload/__pycache__/__init__.cpython-39.pyc +0 -0
  88. clarifai/datasets/upload/__pycache__/base.cpython-311.pyc +0 -0
  89. clarifai/datasets/upload/__pycache__/base.cpython-39.pyc +0 -0
  90. clarifai/datasets/upload/__pycache__/features.cpython-311.pyc +0 -0
  91. clarifai/datasets/upload/__pycache__/features.cpython-39.pyc +0 -0
  92. clarifai/datasets/upload/__pycache__/image.cpython-311.pyc +0 -0
  93. clarifai/datasets/upload/__pycache__/image.cpython-39.pyc +0 -0
  94. clarifai/datasets/upload/__pycache__/multimodal.cpython-311.pyc +0 -0
  95. clarifai/datasets/upload/__pycache__/multimodal.cpython-39.pyc +0 -0
  96. clarifai/datasets/upload/__pycache__/text.cpython-311.pyc +0 -0
  97. clarifai/datasets/upload/__pycache__/text.cpython-39.pyc +0 -0
  98. clarifai/datasets/upload/__pycache__/utils.cpython-311.pyc +0 -0
  99. clarifai/datasets/upload/__pycache__/utils.cpython-39.pyc +0 -0
  100. clarifai/models/model_serving/README.md +0 -158
  101. clarifai/models/model_serving/__init__.py +0 -14
  102. clarifai/models/model_serving/cli/__init__.py +0 -12
  103. clarifai/models/model_serving/cli/_utils.py +0 -53
  104. clarifai/models/model_serving/cli/base.py +0 -14
  105. clarifai/models/model_serving/cli/build.py +0 -79
  106. clarifai/models/model_serving/cli/clarifai_clis.py +0 -33
  107. clarifai/models/model_serving/cli/create.py +0 -171
  108. clarifai/models/model_serving/cli/example_cli.py +0 -34
  109. clarifai/models/model_serving/cli/login.py +0 -26
  110. clarifai/models/model_serving/cli/upload.py +0 -179
  111. clarifai/models/model_serving/constants.py +0 -21
  112. clarifai/models/model_serving/docs/cli.md +0 -161
  113. clarifai/models/model_serving/docs/concepts.md +0 -229
  114. clarifai/models/model_serving/docs/dependencies.md +0 -11
  115. clarifai/models/model_serving/docs/inference_parameters.md +0 -139
  116. clarifai/models/model_serving/docs/model_types.md +0 -19
  117. clarifai/models/model_serving/model_config/__init__.py +0 -16
  118. clarifai/models/model_serving/model_config/base.py +0 -369
  119. clarifai/models/model_serving/model_config/config.py +0 -312
  120. clarifai/models/model_serving/model_config/inference_parameter.py +0 -129
  121. clarifai/models/model_serving/model_config/model_types_config/multimodal-embedder.yaml +0 -25
  122. clarifai/models/model_serving/model_config/model_types_config/text-classifier.yaml +0 -19
  123. clarifai/models/model_serving/model_config/model_types_config/text-embedder.yaml +0 -20
  124. clarifai/models/model_serving/model_config/model_types_config/text-to-image.yaml +0 -19
  125. clarifai/models/model_serving/model_config/model_types_config/text-to-text.yaml +0 -19
  126. clarifai/models/model_serving/model_config/model_types_config/visual-classifier.yaml +0 -22
  127. clarifai/models/model_serving/model_config/model_types_config/visual-detector.yaml +0 -32
  128. clarifai/models/model_serving/model_config/model_types_config/visual-embedder.yaml +0 -19
  129. clarifai/models/model_serving/model_config/model_types_config/visual-segmenter.yaml +0 -19
  130. clarifai/models/model_serving/model_config/output.py +0 -133
  131. clarifai/models/model_serving/model_config/triton/__init__.py +0 -14
  132. clarifai/models/model_serving/model_config/triton/serializer.py +0 -136
  133. clarifai/models/model_serving/model_config/triton/triton_config.py +0 -182
  134. clarifai/models/model_serving/model_config/triton/wrappers.py +0 -281
  135. clarifai/models/model_serving/repo_build/__init__.py +0 -14
  136. clarifai/models/model_serving/repo_build/build.py +0 -198
  137. clarifai/models/model_serving/repo_build/static_files/_requirements.txt +0 -2
  138. clarifai/models/model_serving/repo_build/static_files/base_test.py +0 -169
  139. clarifai/models/model_serving/repo_build/static_files/inference.py +0 -26
  140. clarifai/models/model_serving/repo_build/static_files/sample_clarifai_config.yaml +0 -25
  141. clarifai/models/model_serving/repo_build/static_files/test.py +0 -40
  142. clarifai/models/model_serving/repo_build/static_files/triton/model.py +0 -75
  143. clarifai/models/model_serving/utils.py +0 -23
  144. clarifai/runners/__pycache__/__init__.cpython-311.pyc +0 -0
  145. clarifai/runners/__pycache__/__init__.cpython-39.pyc +0 -0
  146. clarifai/runners/models/__pycache__/__init__.cpython-311.pyc +0 -0
  147. clarifai/runners/models/__pycache__/__init__.cpython-39.pyc +0 -0
  148. clarifai/runners/models/__pycache__/mcp_class.cpython-311.pyc +0 -0
  149. clarifai/runners/models/__pycache__/model_builder.cpython-311.pyc +0 -0
  150. clarifai/runners/models/__pycache__/model_builder.cpython-39.pyc +0 -0
  151. clarifai/runners/models/__pycache__/model_class.cpython-311.pyc +0 -0
  152. clarifai/runners/models/__pycache__/model_runner.cpython-311.pyc +0 -0
  153. clarifai/runners/models/__pycache__/openai_class.cpython-311.pyc +0 -0
  154. clarifai/runners/models/base_typed_model.py +0 -238
  155. clarifai/runners/models/model_upload.py +0 -607
  156. clarifai/runners/utils/__pycache__/__init__.cpython-311.pyc +0 -0
  157. clarifai/runners/utils/__pycache__/__init__.cpython-39.pyc +0 -0
  158. clarifai/runners/utils/__pycache__/code_script.cpython-311.pyc +0 -0
  159. clarifai/runners/utils/__pycache__/code_script.cpython-39.pyc +0 -0
  160. clarifai/runners/utils/__pycache__/const.cpython-311.pyc +0 -0
  161. clarifai/runners/utils/__pycache__/data_utils.cpython-311.pyc +0 -0
  162. clarifai/runners/utils/__pycache__/data_utils.cpython-39.pyc +0 -0
  163. clarifai/runners/utils/__pycache__/loader.cpython-311.pyc +0 -0
  164. clarifai/runners/utils/__pycache__/method_signatures.cpython-311.pyc +0 -0
  165. clarifai/runners/utils/__pycache__/model_utils.cpython-311.pyc +0 -0
  166. clarifai/runners/utils/__pycache__/openai_convertor.cpython-311.pyc +0 -0
  167. clarifai/runners/utils/__pycache__/serializers.cpython-311.pyc +0 -0
  168. clarifai/runners/utils/__pycache__/url_fetcher.cpython-311.pyc +0 -0
  169. clarifai/runners/utils/data_handler.py +0 -231
  170. clarifai/runners/utils/data_types/__pycache__/__init__.cpython-311.pyc +0 -0
  171. clarifai/runners/utils/data_types/__pycache__/__init__.cpython-39.pyc +0 -0
  172. clarifai/runners/utils/data_types/__pycache__/data_types.cpython-311.pyc +0 -0
  173. clarifai/runners/utils/data_types/__pycache__/data_types.cpython-39.pyc +0 -0
  174. clarifai/runners/utils/data_types.py +0 -471
  175. clarifai/runners/utils/temp.py +0 -59
  176. clarifai/schema/__pycache__/search.cpython-311.pyc +0 -0
  177. clarifai/urls/__pycache__/helper.cpython-311.pyc +0 -0
  178. clarifai/urls/__pycache__/helper.cpython-39.pyc +0 -0
  179. clarifai/utils/__pycache__/__init__.cpython-311.pyc +0 -0
  180. clarifai/utils/__pycache__/__init__.cpython-39.pyc +0 -0
  181. clarifai/utils/__pycache__/cli.cpython-39.pyc +0 -0
  182. clarifai/utils/__pycache__/config.cpython-311.pyc +0 -0
  183. clarifai/utils/__pycache__/config.cpython-39.pyc +0 -0
  184. clarifai/utils/__pycache__/constants.cpython-311.pyc +0 -0
  185. clarifai/utils/__pycache__/constants.cpython-39.pyc +0 -0
  186. clarifai/utils/__pycache__/logging.cpython-311.pyc +0 -0
  187. clarifai/utils/__pycache__/logging.cpython-39.pyc +0 -0
  188. clarifai/utils/__pycache__/misc.cpython-311.pyc +0 -0
  189. clarifai/utils/__pycache__/misc.cpython-39.pyc +0 -0
  190. clarifai/utils/__pycache__/model_train.cpython-311.pyc +0 -0
  191. clarifai/utils/__pycache__/protobuf.cpython-311.pyc +0 -0
  192. clarifai/utils/__pycache__/protobuf.cpython-39.pyc +0 -0
  193. clarifai/workflows/__pycache__/__init__.cpython-311.pyc +0 -0
  194. clarifai/workflows/__pycache__/export.cpython-311.pyc +0 -0
  195. clarifai/workflows/__pycache__/utils.cpython-311.pyc +0 -0
  196. clarifai/workflows/__pycache__/validate.cpython-311.pyc +0 -0
  197. clarifai-11.6.4rc2.dist-info/RECORD +0 -301
  198. {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/entry_points.txt +0 -0
  199. {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/licenses/LICENSE +0 -0
  200. {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/top_level.txt +0 -0
clarifai/cli/model.py CHANGED
@@ -5,7 +5,13 @@ import tempfile
5
5
  import click
6
6
 
7
7
  from clarifai.cli.base import cli, pat_display
8
- from clarifai.utils.cli import validate_context
8
+ from clarifai.utils.cli import (
9
+ check_ollama_installed,
10
+ check_requirements_installed,
11
+ customize_ollama_model,
12
+ parse_requirements,
13
+ validate_context,
14
+ )
9
15
  from clarifai.utils.constants import (
10
16
  DEFAULT_LOCAL_RUNNER_APP_ID,
11
17
  DEFAULT_LOCAL_RUNNER_COMPUTE_CLUSTER_CONFIG,
@@ -22,49 +28,6 @@ from clarifai.utils.logging import logger
22
28
  from clarifai.utils.misc import GitHubDownloader, clone_github_repo, format_github_repo_url
23
29
 
24
30
 
25
- def customize_ollama_model(model_path, model_name, port, context_length):
26
- """Customize the Ollama model name in the cloned template files.
27
- Args:
28
- model_path: Path to the cloned model directory
29
- model_name: The model name to set (e.g., 'llama3.1', 'mistral')
30
-
31
- """
32
- model_py_path = os.path.join(model_path, "1", "model.py")
33
-
34
- if not os.path.exists(model_py_path):
35
- logger.warning(f"Model file {model_py_path} not found, skipping model name customization")
36
- return
37
-
38
- try:
39
- # Read the model.py file
40
- with open(model_py_path, 'r') as file:
41
- content = file.read()
42
- if model_name:
43
- # Replace the default model name in the load_model method
44
- content = content.replace(
45
- 'self.model = os.environ.get("OLLAMA_MODEL_NAME", \'llama3.2\')',
46
- f'self.model = os.environ.get("OLLAMA_MODEL_NAME", \'{model_name}\')',
47
- )
48
-
49
- if port:
50
- # Replace the default port variable in the model.py file
51
- content = content.replace("PORT = '23333'", f"PORT = '{port}'")
52
-
53
- if context_length:
54
- # Replace the default context length variable in the model.py file
55
- content = content.replace(
56
- "context_length = '8192'", f"context_length = '{context_length}'"
57
- )
58
-
59
- # Write the modified content back to model.py
60
- with open(model_py_path, 'w') as file:
61
- file.write(content)
62
-
63
- except Exception as e:
64
- logger.error(f"Failed to customize Ollama model name in {model_py_path}: {e}")
65
- raise
66
-
67
-
68
31
  @cli.group(
69
32
  ['model'], context_settings={'max_content_width': shutil.get_terminal_size().columns - 10}
70
33
  )
@@ -121,15 +84,22 @@ def model():
121
84
  required=False,
122
85
  )
123
86
  def init(
124
- model_path, model_type_id, github_pat, github_url, toolkit, model_name, port, context_length
87
+ model_path,
88
+ model_type_id,
89
+ github_pat,
90
+ github_url,
91
+ toolkit,
92
+ model_name,
93
+ port,
94
+ context_length,
125
95
  ):
126
96
  """Initialize a new model directory structure.
127
97
 
128
- Creates the following structure in the specified directory:
129
- ├── 1/
130
- │ └── model.py
131
- ├── requirements.txt
132
- └── config.yaml
98
+ Creates the following structure in the specified directory:\n
99
+ ├── 1/\n
100
+ │ └── model.py\n
101
+ ├── requirements.txt\n
102
+ └── config.yaml\n
133
103
 
134
104
  If --github-repo is provided, the entire repository contents will be copied to the target
135
105
  directory instead of using default templates. The --github-pat option can be used for authentication
@@ -137,13 +107,13 @@ def init(
137
107
  branch to clone from.
138
108
 
139
109
  MODEL_PATH: Path where to create the model directory structure. If not specified, the current directory is used by default.
140
- model_type_id: Type of model to initialize. Options are 'mcp' for MCPModelClass or 'openai' for OpenAIModelClass.
141
- github_pat: GitHub Personal Access Token for cloning private repositories.
142
- github_url: GitHub repository URL or "user/repo" format or the github folder URL to clone a model files from. If provided, the entire contents of repo/folder will be copied to the target directory instead of using default templates.
143
- toolkit: Toolkit to use for model initialization. Currently supports 'ollama'.
144
- 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.).
145
- port: Port to run the Ollama server on. Defaults to 23333.
146
- context_length: Context length for the Ollama model. Defaults to 8192.
110
+ MODEL_TYPE_ID: Type of model to create. If not specified, defaults to "text-to-text" for text models.
111
+ GITHUB_PAT: GitHub Personal Access Token for authentication when cloning private repositories.
112
+ 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.
113
+ TOOLKIT: Toolkit to use for model initialization. Currently supports "ollama".
114
+ 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.).
115
+ PORT: Port to run the Ollama server on. Defaults to 23333.
116
+ CONTEXT_LENGTH: Context length for the Ollama model. Defaults to 8192.
147
117
  """
148
118
  # Resolve the absolute path
149
119
  model_path = os.path.abspath(model_path)
@@ -171,6 +141,11 @@ def init(
171
141
 
172
142
  # --toolkit option
173
143
  if toolkit == 'ollama':
144
+ if not check_ollama_installed():
145
+ logger.error(
146
+ "Ollama is not installed. Please install it from `https://ollama.com/` to use the Ollama toolkit."
147
+ )
148
+ raise click.Abort()
174
149
  github_url = DEFAULT_OLLAMA_MODEL_REPO
175
150
  branch = DEFAULT_OLLAMA_MODEL_REPO_BRANCH
176
151
 
@@ -251,11 +226,12 @@ def init(
251
226
  if (model_name or port or context_length) and (toolkit == 'ollama'):
252
227
  customize_ollama_model(model_path, model_name, port, context_length)
253
228
 
254
- logger.info("Model initialization complete with GitHub repository")
255
- logger.info("Next steps:")
256
- logger.info("1. Review the model configuration")
257
- logger.info("2. Install any required dependencies manually")
258
- logger.info("3. Test the model locally using 'clarifai model local-test'")
229
+ if github_url:
230
+ logger.info("Model initialization complete with GitHub repository")
231
+ logger.info("Next steps:")
232
+ logger.info("1. Review the model configuration")
233
+ logger.info("2. Install any required dependencies manually")
234
+ logger.info("3. Test the model locally using 'clarifai model local-test'")
259
235
 
260
236
  # Fall back to template-based initialization if no GitHub repo or if GitHub repo failed
261
237
  if not github_url:
@@ -548,8 +524,13 @@ def run_locally(model_path, port, mode, keep_env, keep_image, skip_dockerfile=Fa
548
524
  show_default=True,
549
525
  help="The number of threads to use. On community plan, the compute time allocation is drained at a rate proportional to the number of threads.",
550
526
  ) # pylint: disable=range-builtin-not-iterating
527
+ @click.option(
528
+ '--verbose',
529
+ is_flag=True,
530
+ help='Show detailed logs including Ollama server output. By default, Ollama logs are suppressed.',
531
+ )
551
532
  @click.pass_context
552
- def local_runner(ctx, model_path, pool_size):
533
+ def local_runner(ctx, model_path, pool_size, verbose):
553
534
  """Run the model as a local runner to help debug your model connected to the API or to
554
535
  leverage local compute resources manually. This relies on many variables being present in the env
555
536
  of the currently selected context. If they are not present then default values will be used to
@@ -591,7 +572,23 @@ def local_runner(ctx, model_path, pool_size):
591
572
  from clarifai.runners.server import serve
592
573
 
593
574
  validate_context(ctx)
594
- logger.info("Checking setup for local runner...")
575
+ builder = ModelBuilder(model_path, download_validation_only=True)
576
+ logger.info("> Checking local runner requirements...")
577
+ if not check_requirements_installed(model_path):
578
+ logger.error(f"Requirements not installed for model at {model_path}.")
579
+ raise click.Abort()
580
+
581
+ # Post check while running `clarifai model local-runner` we check if the toolkit is ollama
582
+ dependencies = parse_requirements(model_path)
583
+ if "ollama" in dependencies or builder.config.get('toolkit', {}).get('provider') == 'ollama':
584
+ logger.info("Verifying Ollama installation...")
585
+ if not check_ollama_installed():
586
+ logger.error(
587
+ "Ollama application is not installed. Please install it from `https://ollama.com/` to use the Ollama toolkit."
588
+ )
589
+ raise click.Abort()
590
+
591
+ logger.info("> Verifying local runner setup...")
595
592
  logger.info(f"Current context: {ctx.obj.current.name}")
596
593
  user_id = ctx.obj.current.user_id
597
594
  logger.info(f"Current user_id: {user_id}")
@@ -620,7 +617,7 @@ def local_runner(ctx, model_path, pool_size):
620
617
  compute_cluster = user.compute_cluster(compute_cluster_id)
621
618
  if compute_cluster.cluster_type != 'local-dev':
622
619
  raise ValueError(
623
- f"Compute cluster {user_id}/{compute_cluster_id} is not a local-runner compute cluster. Please create a local-runner compute cluster."
620
+ f"Compute cluster {user_id}/{compute_cluster_id} is not a compute cluster of type 'local-dev'. Please use a compute cluster of type 'local-dev'."
624
621
  )
625
622
  try:
626
623
  compute_cluster_id = ctx.obj.current.compute_cluster_id
@@ -630,7 +627,7 @@ def local_runner(ctx, model_path, pool_size):
630
627
  except ValueError:
631
628
  raise
632
629
  except Exception as e:
633
- logger.info(f"Failed to get compute cluster with ID {compute_cluster_id}: {e}")
630
+ logger.warning(f"Failed to get compute cluster with ID '{compute_cluster_id}':\n{e}")
634
631
  y = input(
635
632
  f"Compute cluster not found. Do you want to create a new compute cluster {user_id}/{compute_cluster_id}? (y/n): "
636
633
  )
@@ -659,7 +656,7 @@ def local_runner(ctx, model_path, pool_size):
659
656
  ctx.obj.current.CLARIFAI_NODEPOOL_ID = nodepool.id
660
657
  ctx.obj.to_yaml() # save to yaml file.
661
658
  except Exception as e:
662
- logger.info(f"Failed to get nodepool with ID {nodepool_id}: {e}")
659
+ logger.warning(f"Failed to get nodepool with ID '{nodepool_id}':\n{e}")
663
660
  y = input(
664
661
  f"Nodepool not found. Do you want to create a new nodepool {user_id}/{compute_cluster_id}/{nodepool_id}? (y/n): "
665
662
  )
@@ -687,7 +684,7 @@ def local_runner(ctx, model_path, pool_size):
687
684
  ctx.obj.current.CLARIFAI_APP_ID = app.id
688
685
  ctx.obj.to_yaml() # save to yaml file.
689
686
  except Exception as e:
690
- logger.info(f"Failed to get app with ID {app_id}: {e}")
687
+ logger.warning(f"Failed to get app with ID '{app_id}':\n{e}")
691
688
  y = input(f"App not found. Do you want to create a new app {user_id}/{app_id}? (y/n): ")
692
689
  if y.lower() != 'y':
693
690
  raise click.Abort()
@@ -710,7 +707,7 @@ def local_runner(ctx, model_path, pool_size):
710
707
  ctx.obj.current.CLARIFAI_MODEL_ID = model.id
711
708
  ctx.obj.to_yaml() # save to yaml file.
712
709
  except Exception as e:
713
- logger.info(f"Failed to get model with ID {model_id}: {e}")
710
+ logger.warning(f"Failed to get model with ID '{model_id}':\n{e}")
714
711
  y = input(
715
712
  f"Model not found. Do you want to create a new model {user_id}/{app_id}/models/{model_id}? (y/n): "
716
713
  )
@@ -730,11 +727,14 @@ def local_runner(ctx, model_path, pool_size):
730
727
  # mentions it's a local runner.
731
728
  model_versions = [v for v in model.list_versions()]
732
729
  if len(model_versions) == 0:
733
- logger.info("No model versions found. Creating a new version for local runner.")
730
+ logger.warning("No model versions found. Creating a new version for local runner.")
734
731
  version = model.create_version(pretrained_model_config={"local_dev": True}).model_version
735
- logger.info(f"Created model version {version.id}")
732
+ ctx.obj.current.CLARIFAI_MODEL_VERSION_ID = version.id
733
+ ctx.obj.to_yaml()
736
734
  else:
737
735
  version = model_versions[0].model_version
736
+ ctx.obj.current.CLARIFAI_MODEL_VERSION_ID = version.id
737
+ ctx.obj.to_yaml() # save to yaml file.
738
738
 
739
739
  logger.info(f"Current model version {version.id}")
740
740
 
@@ -759,12 +759,16 @@ def local_runner(ctx, model_path, pool_size):
759
759
  # ensure the deployment is using the latest version.
760
760
  if runner.worker.model.model_version.id != version.id:
761
761
  nodepool.delete_runners([runner_id])
762
- raise AttributeError("Deleted runner that was for an old model version ID.")
762
+ logger.warning("Deleted runner that was for an old model version ID.")
763
+ raise AttributeError(
764
+ "Runner deleted because it was associated with an outdated model version."
765
+ )
763
766
  except Exception as e:
764
- raise AttributeError("Runner not found in nodepool.") from e
767
+ logger.warning(f"Failed to get runner with ID '{runner_id}':\n{e}")
768
+ raise AttributeError("Runner not found in nodepool.")
765
769
  except AttributeError:
766
770
  logger.info(
767
- f"Create the local runner tying this\n {user_id}/{app_id}/models/{model.id} model (version: {version.id}) to the\n {user_id}/{compute_cluster_id}/{nodepool_id} nodepool."
771
+ f"Creating the local runner tying this '{user_id}/{app_id}/models/{model.id}' model (version: {version.id}) to the '{user_id}/{compute_cluster_id}/{nodepool_id}' nodepool."
768
772
  )
769
773
  runner = nodepool.create_runner(
770
774
  runner_config={
@@ -792,14 +796,17 @@ def local_runner(ctx, model_path, pool_size):
792
796
  # ensure the deployment is using the latest version.
793
797
  if deployment.worker.model.model_version.id != version.id:
794
798
  nodepool.delete_deployments([deployment_id])
795
- raise Exception("Deleted deployment that was for an old model version ID.")
799
+ logger.warning("Deleted deployment that was for an old model version ID.")
800
+ raise Exception(
801
+ "Deployment deleted because it was associated with an outdated model version."
802
+ )
796
803
  try:
797
804
  deployment_id = ctx.obj.current.deployment_id
798
805
  except AttributeError: # doesn't exist in context but does in API then update the context.
799
806
  ctx.obj.current.CLARIFAI_DEPLOYMENT_ID = deployment.id
800
807
  ctx.obj.to_yaml() # save to yaml file.
801
808
  except Exception as e:
802
- logger.info(f"Failed to get deployment with ID {deployment_id}: {e}")
809
+ logger.warning(f"Failed to get deployment with ID {deployment_id}:\n{e}")
803
810
  y = input(
804
811
  f"Deployment not found. Do you want to create a new deployment {user_id}/{compute_cluster_id}/{nodepool_id}/{deployment_id}? (y/n): "
805
812
  )
@@ -829,17 +836,14 @@ def local_runner(ctx, model_path, pool_size):
829
836
 
830
837
  logger.info(f"Current deployment_id: {deployment_id}")
831
838
 
832
- logger.info(
833
- f"Full url for the model: {ctx.obj.current.ui}/users/{user_id}/apps/{app_id}/models/{model.id}/versions/{version.id}"
834
- )
835
-
836
839
  # Now that we have all the context in ctx.obj, we need to update the config.yaml in
837
840
  # the model_path directory with the model object containing user_id, app_id, model_id, version_id
838
841
  config_file = os.path.join(model_path, 'config.yaml')
839
842
  if not os.path.exists(config_file):
840
- raise ValueError(
843
+ logger.error(
841
844
  f"config.yaml not found in {model_path}. Please ensure you are passing the correct directory."
842
845
  )
846
+ raise click.Abort()
843
847
  config = ModelBuilder._load_config(config_file)
844
848
  model_type_id = config.get('model', {}).get('model_type_id', DEFAULT_LOCAL_RUNNER_MODEL_TYPE)
845
849
  # The config.yaml doens't match what we created above.
@@ -857,6 +861,28 @@ def local_runner(ctx, model_path, pool_size):
857
861
  ModelBuilder._save_config(config_file, config)
858
862
 
859
863
  builder = ModelBuilder(model_path, download_validation_only=True)
864
+ if not check_requirements_installed(model_path):
865
+ logger.error(f"Requirements not installed for model at {model_path}.")
866
+ raise click.Abort()
867
+
868
+ # Post check while running `clarifai model local-runner` we check if the toolkit is ollama
869
+ if builder.config.get('toolkit', {}).get('provider') == 'ollama':
870
+ if not check_ollama_installed():
871
+ logger.error(
872
+ "Ollama is not installed. Please install it from `https://ollama.com/` to use the Ollama toolkit."
873
+ )
874
+ raise click.Abort()
875
+
876
+ try:
877
+ logger.info("Customizing Ollama model with provided parameters...")
878
+ customize_ollama_model(
879
+ model_path=model_path,
880
+ verbose=True if verbose else False,
881
+ )
882
+ except Exception as e:
883
+ logger.error(f"Failed to customize Ollama model: {e}")
884
+ raise click.Abort()
885
+
860
886
  # don't mock for local runner since you need the dependencies to run the code anyways.
861
887
  method_signatures = builder.get_method_signatures(mocking=False)
862
888
 
@@ -871,14 +897,17 @@ def local_runner(ctx, model_path, pool_size):
871
897
  base_url=ctx.obj.current.api_base,
872
898
  )
873
899
 
874
- logger.info("""\n
875
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
900
+ logger.info(f"""\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
876
901
  # About to start up the local runner in this terminal...
877
- # Here is a code snippet to call this model once it start from another terminal:
902
+ # Here is a code snippet to call this model once it start from another terminal:{snippet}
903
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
878
904
  """)
879
- logger.info(snippet)
880
905
 
881
- logger.info("Now starting the local runner...")
906
+ logger.info(
907
+ f"Playground: To chat with your model, visit:\n{ctx.obj.current.ui}/playground?model={model.id}__{version.id}&user_id={user_id}&app_id={app_id}"
908
+ )
909
+
910
+ logger.info("✅ Starting local runner...")
882
911
 
883
912
  # This reads the config.yaml from the model_path so we alter it above first.
884
913
  serve(
@@ -891,6 +920,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
891
920
  runner_id=runner_id,
892
921
  base_url=ctx.obj.current.api_base,
893
922
  pat=ctx.obj.current.pat,
923
+ context=ctx.obj.current,
894
924
  )
895
925
 
896
926
 
clarifai/client/app.py CHANGED
@@ -445,7 +445,7 @@ class App(Lister, BaseClient):
445
445
  response = self._grpc_request(self.STUB.PostModels, request)
446
446
  if response.status.code != status_code_pb2.SUCCESS:
447
447
  raise Exception(response.status)
448
- self.logger.info("\nModel created\n%s", response.status)
448
+ self.logger.info(f"Model with ID '{model_id}' is created:\n{response.status}")
449
449
  kwargs.update(
450
450
  {
451
451
  'model_id': model_id,
@@ -2,6 +2,7 @@ import os
2
2
  import urllib.request
3
3
  from typing import Any, Dict
4
4
 
5
+ import grpc
5
6
  from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
6
7
  from clarifai_grpc.grpc.api import resources_pb2, service_pb2_grpc
7
8
 
@@ -289,7 +290,11 @@ class ClarifaiAuthHelper:
289
290
  )
290
291
 
291
292
  def get_stub(self) -> service_pb2_grpc.V2Stub:
292
- """Get the API gRPC stub using the right channel based on the API endpoint base.
293
+ stub, channel = self.get_stub_and_channel()
294
+ return stub
295
+
296
+ def get_stub_and_channel(self) -> tuple[service_pb2_grpc.V2Stub, grpc.Channel]:
297
+ """Get the API gRPC stub and channel based on the API endpoint base.
293
298
 
294
299
  Returns:
295
300
  stub: The service_pb2_grpc.V2Stub stub for the API.
@@ -310,7 +315,7 @@ class ClarifaiAuthHelper:
310
315
  port = 80
311
316
  channel = ClarifaiChannel.get_insecure_grpc_channel(base=host, port=port)
312
317
  stub = service_pb2_grpc.V2Stub(channel)
313
- return stub
318
+ return stub, channel
314
319
 
315
320
  def get_async_stub(self) -> service_pb2_grpc.V2Stub:
316
321
  """Get the API gRPC async stub using the right channel based on the API endpoint base.
clarifai/client/base.py CHANGED
@@ -11,7 +11,7 @@ from clarifai.client.auth.helper import ClarifaiAuthHelper
11
11
  from clarifai.constants.base import COMPUTE_ORCHESTRATION_RESOURCES
12
12
  from clarifai.errors import ApiError, UserError
13
13
  from clarifai.utils.constants import CLARIFAI_PAT_ENV_VAR, CLARIFAI_SESSION_TOKEN_ENV_VAR
14
- from clarifai.utils.misc import get_from_dict_or_env
14
+ from clarifai.utils.misc import get_from_dict_env_or_config
15
15
 
16
16
 
17
17
  class BaseClient:
@@ -39,15 +39,42 @@ class BaseClient:
39
39
  def __init__(self, **kwargs):
40
40
  token, pat = "", ""
41
41
  try:
42
- pat = get_from_dict_or_env(key="pat", env_key=CLARIFAI_PAT_ENV_VAR, **kwargs)
42
+ pat = get_from_dict_env_or_config(key="pat", env_key=CLARIFAI_PAT_ENV_VAR, **kwargs)
43
43
  except UserError:
44
- token = get_from_dict_or_env(
45
- key="token", env_key=CLARIFAI_SESSION_TOKEN_ENV_VAR, **kwargs
46
- )
44
+ try:
45
+ token = get_from_dict_env_or_config(
46
+ key="token", env_key=CLARIFAI_SESSION_TOKEN_ENV_VAR, **kwargs
47
+ )
48
+ except UserError:
49
+ pass
47
50
  finally:
48
- assert token or pat, Exception(
49
- "Need 'pat' or 'token' in args or use one of the CLARIFAI_PAT or CLARIFAI_SESSION_TOKEN env vars"
50
- )
51
+ if not (token or pat):
52
+ raise UserError(
53
+ "Authentication Required. Please authenticate in one of the following ways:\n\n"
54
+ "- Pass your Personal Access Token ('pat') or session token ('token') as arguments to your function.\n"
55
+ "- Set the CLARIFAI_PAT or CLARIFAI_SESSION_TOKEN environment variables in your environment.\n"
56
+ "- Run `clarifai login` in your terminal to configure CLI authentication."
57
+ )
58
+
59
+ # Also try to get user_id and base from CLI config if not provided
60
+ if not kwargs.get('user_id'):
61
+ try:
62
+ user_id = get_from_dict_env_or_config(
63
+ key="user_id", env_key="CLARIFAI_USER_ID", **kwargs
64
+ )
65
+ kwargs['user_id'] = user_id
66
+ except UserError:
67
+ pass # user_id is optional for some use cases
68
+
69
+ if not kwargs.get('base'):
70
+ try:
71
+ base = get_from_dict_env_or_config(
72
+ key="base", env_key="CLARIFAI_API_BASE", **kwargs
73
+ )
74
+ kwargs['base'] = base
75
+ except UserError:
76
+ pass # base has a default value
77
+
51
78
  kwargs.update({'token': token, 'pat': pat})
52
79
 
53
80
  self.auth_helper = ClarifaiAuthHelper(**kwargs, validate=False)
@@ -165,7 +165,7 @@ class ComputeCluster(Lister, BaseClient):
165
165
  response = self._grpc_request(self.STUB.PostNodepools, request)
166
166
  if response.status.code != status_code_pb2.SUCCESS:
167
167
  raise Exception(response.status)
168
- self.logger.info("\nNodepool created\n%s", response.status)
168
+ self.logger.info(f"Nodepool with ID '{nodepool_id}' is created:\n{response.status}")
169
169
 
170
170
  dict_response = MessageToDict(response.nodepools[0], preserving_proto_field_name=True)
171
171
  kwargs = self.process_response_keys(dict_response, 'nodepool')
clarifai/client/model.py CHANGED
@@ -445,7 +445,9 @@ class Model(Lister, BaseClient):
445
445
  response = self._grpc_request(self.STUB.PostModelVersions, request)
446
446
  if response.status.code != status_code_pb2.SUCCESS:
447
447
  raise Exception(response.status)
448
- self.logger.info("\nModel Version created\n%s", response.status)
448
+ self.logger.info(
449
+ f"Model Version with ID '{response.model.model_version.id}' is created:\n{response.status}"
450
+ )
449
451
 
450
452
  kwargs.update({'app_id': self.app_id, 'user_id': self.user_id})
451
453
  dict_response = MessageToDict(response, preserving_proto_field_name=True)
@@ -76,7 +76,22 @@ class ModelClient:
76
76
  def __getattr__(self, name):
77
77
  if not self._defined:
78
78
  self.fetch()
79
- return self.__getattribute__(name)
79
+ try:
80
+ return self.__getattribute__(name)
81
+ except AttributeError as e:
82
+ # Provide helpful error message with available methods
83
+ available_methods = []
84
+ if self._method_signatures:
85
+ available_methods = list(self._method_signatures.keys())
86
+
87
+ error_msg = f"'{self.__class__.__name__}' object has no attribute '{name}'"
88
+
89
+ if available_methods:
90
+ error_msg += f". Available methods: {available_methods}"
91
+ raise AttributeError(error_msg) from e
92
+ else:
93
+ error_msg += ". This model is a non-pythonic model. Please use the old inference methods i.e. predict_by_url, predict_by_bytes, etc."
94
+ raise Exception(error_msg) from e
80
95
 
81
96
  def _fetch_signatures(self):
82
97
  '''
@@ -148,6 +163,10 @@ class ModelClient:
148
163
  self._define_compatability_functions()
149
164
  return
150
165
  if response.status.code != status_code_pb2.SUCCESS:
166
+ if response.outputs[0].status.description.startswith("cannot identify image file"):
167
+ raise Exception(
168
+ "Failed to fetch method signatures from model and backup method. This model is a non-pythonic model. Please use the old inference methods i.e. predict_by_url, predict_by_bytes, etc."
169
+ )
151
170
  raise Exception(f"Model failed with response {response!r}")
152
171
  self._method_signatures = signatures_from_json(response.outputs[0].data.text.raw)
153
172
 
@@ -195,7 +195,9 @@ class Nodepool(Lister, BaseClient):
195
195
  response = self._grpc_request(self.STUB.PostDeployments, request)
196
196
  if response.status.code != status_code_pb2.SUCCESS:
197
197
  raise Exception(response.status)
198
- self.logger.info("\nDeployment created\n%s", response.status)
198
+ self.logger.info(
199
+ f"Deployment with ID '{response.deployments[0].id}' is created:\n{response.status}"
200
+ )
199
201
 
200
202
  dict_response = MessageToDict(
201
203
  response.deployments[0], preserving_proto_field_name=True, use_integers_for_enums=True
@@ -324,7 +326,7 @@ class Nodepool(Lister, BaseClient):
324
326
  if response.status.code != status_code_pb2.SUCCESS:
325
327
  raise Exception(response.status)
326
328
  self.logger.info(
327
- "\nRunner created\n%s with id: %s", response.status, response.runners[0].id
329
+ f"Runner with ID '{response.runners[0].id}' is created:\n{response.status}"
328
330
  )
329
331
 
330
332
  dict_response = MessageToDict(response.runners[0], preserving_proto_field_name=True)
clarifai/client/user.py CHANGED
@@ -177,7 +177,7 @@ class User(Lister, BaseClient):
177
177
  response = self._grpc_request(self.STUB.PostApps, request)
178
178
  if response.status.code != status_code_pb2.SUCCESS:
179
179
  raise Exception(response.status)
180
- self.logger.info("\nApp created\n%s", response.status)
180
+ self.logger.info(f"App with ID '{app_id}' is created:\n{response.status}")
181
181
  return App.from_auth_helper(auth=self.auth_helper, app_id=app_id)
182
182
 
183
183
  def _process_compute_cluster_config(
@@ -255,7 +255,9 @@ class User(Lister, BaseClient):
255
255
  response = self._grpc_request(self.STUB.PostComputeClusters, request)
256
256
  if response.status.code != status_code_pb2.SUCCESS:
257
257
  raise Exception(response.status)
258
- self.logger.info("\nCompute Cluster created\n%s", response.status)
258
+ self.logger.info(
259
+ f"Compute Cluster with ID '{compute_cluster_id}' is created:\n{response.status}"
260
+ )
259
261
  return ComputeCluster.from_auth_helper(
260
262
  auth=self.auth_helper, compute_cluster_id=compute_cluster_id
261
263
  )