clarifai 11.5.0__py3-none-any.whl → 11.5.2__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 +1 -1
- clarifai/cli/model.py +9 -0
- clarifai/client/app.py +2 -2
- clarifai/client/auth/helper.py +23 -21
- clarifai/runners/__init__.py +2 -2
- clarifai/runners/dockerfile_template/Dockerfile.template +6 -2
- clarifai/runners/models/mcp_class.py +14 -6
- clarifai/runners/models/model_builder.py +306 -10
- clarifai/runners/utils/code_script.py +1 -1
- clarifai/runners/utils/const.py +3 -3
- clarifai/workflows/export.py +2 -5
- {clarifai-11.5.0.dist-info → clarifai-11.5.2.dist-info}/METADATA +3 -1
- {clarifai-11.5.0.dist-info → clarifai-11.5.2.dist-info}/RECORD +17 -17
- {clarifai-11.5.0.dist-info → clarifai-11.5.2.dist-info}/WHEEL +0 -0
- {clarifai-11.5.0.dist-info → clarifai-11.5.2.dist-info}/entry_points.txt +0 -0
- {clarifai-11.5.0.dist-info → clarifai-11.5.2.dist-info}/licenses/LICENSE +0 -0
- {clarifai-11.5.0.dist-info → clarifai-11.5.2.dist-info}/top_level.txt +0 -0
clarifai/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "11.5.
|
1
|
+
__version__ = "11.5.2"
|
clarifai/cli/model.py
CHANGED
@@ -520,6 +520,10 @@ def local_dev(ctx, model_path):
|
|
520
520
|
|
521
521
|
try:
|
522
522
|
runner = nodepool.runner(runner_id)
|
523
|
+
# ensure the deployment is using the latest version.
|
524
|
+
if runner.worker.model.model_version.id != version.id:
|
525
|
+
nodepool.delete_runners([runner_id])
|
526
|
+
raise AttributeError("Deleted runner that was for an old model version ID.")
|
523
527
|
except Exception as e:
|
524
528
|
raise AttributeError("Runner not found in nodepool.") from e
|
525
529
|
except AttributeError:
|
@@ -549,6 +553,10 @@ def local_dev(ctx, model_path):
|
|
549
553
|
deployment_id = DEFAULT_LOCAL_DEV_DEPLOYMENT_ID
|
550
554
|
try:
|
551
555
|
deployment = nodepool.deployment(deployment_id)
|
556
|
+
# ensure the deployment is using the latest version.
|
557
|
+
if deployment.worker.model.model_version.id != version.id:
|
558
|
+
nodepool.delete_deployments([deployment_id])
|
559
|
+
raise Exception("Deleted deployment that was for an old model version ID.")
|
552
560
|
try:
|
553
561
|
deployment_id = ctx.obj.current.deployment_id
|
554
562
|
except AttributeError: # doesn't exist in context but does in API then update the context.
|
@@ -576,6 +584,7 @@ def local_dev(ctx, model_path):
|
|
576
584
|
},
|
577
585
|
}
|
578
586
|
],
|
587
|
+
"deploy_latest_version": True,
|
579
588
|
}
|
580
589
|
},
|
581
590
|
)
|
clarifai/client/app.py
CHANGED
@@ -470,8 +470,8 @@ class App(Lister, BaseClient):
|
|
470
470
|
model = self.model(
|
471
471
|
model_id=node['model']['model_id'],
|
472
472
|
model_version={"id": node['model'].get('model_version_id', "")},
|
473
|
-
user_id=node['model'].get('user_id',
|
474
|
-
app_id=node['model'].get('app_id',
|
473
|
+
user_id=node['model'].get('user_id', self.user_app_id.user_id),
|
474
|
+
app_id=node['model'].get('app_id', self.user_app_id.app_id),
|
475
475
|
)
|
476
476
|
except Exception as e:
|
477
477
|
if "Model does not exist" in str(e):
|
clarifai/client/auth/helper.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import urllib.request
|
3
3
|
from typing import Any, Dict
|
4
|
-
from urllib.parse import urlparse
|
5
4
|
|
6
5
|
from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
|
7
6
|
from clarifai_grpc.grpc.api import resources_pb2, service_pb2_grpc
|
@@ -42,28 +41,31 @@ def https_cache(cache: dict, url: str) -> str:
|
|
42
41
|
elif url.startswith("http://"):
|
43
42
|
url = url.replace("http://", "")
|
44
43
|
cache[url] = HTTP
|
45
|
-
elif url
|
44
|
+
elif url.endswith(":443"):
|
45
|
+
# If it ends with :443 then we know it's https.
|
46
|
+
# trim it off the right
|
47
|
+
url = url[:-4]
|
48
|
+
cache[url] = HTTPS
|
49
|
+
elif url.find('.clarifai.com') >= 0:
|
46
50
|
# We know our endpoints are https.
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
if ":" not in url:
|
59
|
-
raise Exception(
|
60
|
-
"When providing an insecure url it must have both host:port format"
|
61
|
-
)
|
62
|
-
else:
|
51
|
+
cache[url] = HTTPS
|
52
|
+
elif url not in cache:
|
53
|
+
# need to test ones that we don't have in the cache yet.
|
54
|
+
try: # make request to https endpoint.
|
55
|
+
urllib.request.urlopen("https://%s/v2/auth/methods" % url, timeout=5)
|
56
|
+
cache[url] = HTTPS # cache it.
|
57
|
+
except Exception as e:
|
58
|
+
if "SSL" in str(e): # if ssl error then we know it's http.
|
59
|
+
cache[url] = HTTP
|
60
|
+
# For http urls we need host:port format.
|
61
|
+
if ":" not in url:
|
63
62
|
raise Exception(
|
64
|
-
"
|
65
|
-
|
66
|
-
|
63
|
+
"When providing an insecure url it must have both host:port format"
|
64
|
+
)
|
65
|
+
else:
|
66
|
+
raise Exception(
|
67
|
+
"Could not get a valid response from url: %s, is the API running there?" % url
|
68
|
+
) from e
|
67
69
|
return url
|
68
70
|
|
69
71
|
|
clarifai/runners/__init__.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
from .models.
|
1
|
+
from .models.mcp_class import MCPModelClass
|
2
2
|
from .models.model_class import ModelClass
|
3
3
|
from .models.model_runner import ModelRunner
|
4
4
|
from .models.openai_class import OpenAIModelClass
|
5
5
|
|
6
6
|
__all__ = [
|
7
7
|
"ModelRunner",
|
8
|
-
"ModelBuilder",
|
9
8
|
"ModelClass",
|
9
|
+
"MCPModelClass",
|
10
10
|
"OpenAIModelClass",
|
11
11
|
]
|
@@ -3,9 +3,13 @@ FROM --platform=$TARGETPLATFORM ${FINAL_IMAGE} as final
|
|
3
3
|
|
4
4
|
COPY --link requirements.txt /home/nonroot/requirements.txt
|
5
5
|
|
6
|
+
ENV VIRTUAL_ENV=/venv
|
7
|
+
ENV PATH="/home/nonroot/.local/bin:$VIRTUAL_ENV/bin:$PATH"
|
8
|
+
|
9
|
+
|
6
10
|
# Update clarifai package so we always have latest protocol to the API. Everything should land in /venv
|
7
|
-
RUN ["pip", "install", "--no-cache-dir", "-r", "/home/nonroot/requirements.txt"]
|
8
|
-
RUN ["pip", "show", "clarifai"]
|
11
|
+
RUN ["uv", "pip", "install", "--no-cache-dir", "-r", "/home/nonroot/requirements.txt"]
|
12
|
+
RUN ["uv", "pip", "show", "--no-cache-dir", "clarifai"]
|
9
13
|
|
10
14
|
# Set the NUMBA cache dir to /tmp
|
11
15
|
# Set the TORCHINDUCTOR cache dir to /tmp
|
@@ -2,14 +2,13 @@
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import json
|
5
|
-
from typing import Any
|
6
|
-
|
7
|
-
from fastmcp import Client, FastMCP # use fastmcp v2 not the built in mcp
|
8
|
-
from mcp import types
|
9
|
-
from mcp.shared.exceptions import McpError
|
5
|
+
from typing import TYPE_CHECKING, Any
|
10
6
|
|
11
7
|
from clarifai.runners.models.model_class import ModelClass
|
12
8
|
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from fastmcp import FastMCP
|
11
|
+
|
13
12
|
|
14
13
|
class MCPModelClass(ModelClass):
|
15
14
|
"""Base class for wrapping FastMCP servers as a model running in Clarfai. This handles
|
@@ -19,10 +18,17 @@ class MCPModelClass(ModelClass):
|
|
19
18
|
"""
|
20
19
|
|
21
20
|
def load_model(self):
|
21
|
+
try:
|
22
|
+
from fastmcp import Client
|
23
|
+
except ImportError:
|
24
|
+
raise ImportError(
|
25
|
+
"fastmcp package is required to use MCP functionality. "
|
26
|
+
"Install it with: pip install fastmcp"
|
27
|
+
)
|
22
28
|
# in memory transport provided in fastmcp v2 so we can easily use the client functions.
|
23
29
|
self.client = Client(self.get_server())
|
24
30
|
|
25
|
-
def get_server(self) -> FastMCP:
|
31
|
+
def get_server(self) -> 'FastMCP':
|
26
32
|
"""Required method for each subclass to implement to return the FastMCP server to use."""
|
27
33
|
raise NotImplementedError("Subclasses must implement get_server() method")
|
28
34
|
|
@@ -32,6 +38,8 @@ class MCPModelClass(ModelClass):
|
|
32
38
|
return it's response.
|
33
39
|
|
34
40
|
"""
|
41
|
+
from mcp import types
|
42
|
+
from mcp.shared.exceptions import McpError
|
35
43
|
|
36
44
|
async def send_notification(client_message: types.ClientNotification) -> None:
|
37
45
|
async with self.client:
|
@@ -4,9 +4,11 @@ import inspect
|
|
4
4
|
import os
|
5
5
|
import re
|
6
6
|
import shutil
|
7
|
+
import subprocess
|
7
8
|
import sys
|
8
9
|
import tarfile
|
9
10
|
import time
|
11
|
+
import webbrowser
|
10
12
|
from string import Template
|
11
13
|
from unittest.mock import MagicMock
|
12
14
|
|
@@ -16,6 +18,7 @@ from clarifai_grpc.grpc.api.status import status_code_pb2
|
|
16
18
|
from google.protobuf import json_format
|
17
19
|
|
18
20
|
from clarifai.client.base import BaseClient
|
21
|
+
from clarifai.client.user import User
|
19
22
|
from clarifai.runners.models.model_class import ModelClass
|
20
23
|
from clarifai.runners.utils.const import (
|
21
24
|
AMD_PYTHON_BASE_IMAGE,
|
@@ -61,7 +64,12 @@ def is_related(object_class, main_class):
|
|
61
64
|
class ModelBuilder:
|
62
65
|
DEFAULT_CHECKPOINT_SIZE = 50 * 1024**3 # 50 GiB
|
63
66
|
|
64
|
-
def __init__(
|
67
|
+
def __init__(
|
68
|
+
self,
|
69
|
+
folder: str,
|
70
|
+
validate_api_ids: bool = True,
|
71
|
+
download_validation_only: bool = False,
|
72
|
+
):
|
65
73
|
"""
|
66
74
|
:param folder: The folder containing the model.py, config.yaml, requirements.txt and
|
67
75
|
checkpoints.
|
@@ -563,6 +571,42 @@ class ModelBuilder:
|
|
563
571
|
dependencies_version[dependency] = version if version else None
|
564
572
|
return dependencies_version
|
565
573
|
|
574
|
+
def _validate_requirements(self, python_version):
|
575
|
+
"""here we use uv pip compile to validate the requirements.txt file
|
576
|
+
and ensure that the dependencies are compatible with each other prior to uploading
|
577
|
+
"""
|
578
|
+
if not os.path.exists(os.path.join(self.folder, 'requirements.txt')):
|
579
|
+
raise FileNotFoundError(
|
580
|
+
"requirements.txt not found in the folder, please provide a valid requirements.txt file"
|
581
|
+
)
|
582
|
+
path = os.path.join(self.folder, 'requirements.txt')
|
583
|
+
# run the f"uv pip compile {path} --universal" command to validate the requirements.txt file
|
584
|
+
if not shutil.which('uv'):
|
585
|
+
raise Exception(
|
586
|
+
"uv command not found, please install uv to validate the requirements.txt file"
|
587
|
+
)
|
588
|
+
logger.info(f"Setup: Validating requirements.txt file at {path} using uv pip compile")
|
589
|
+
# Don't log the output of the comment unless it errors.
|
590
|
+
result = subprocess.run(
|
591
|
+
f"uv pip compile {path} --universal --python {python_version} --no-header --no-emit-index-url",
|
592
|
+
shell=True,
|
593
|
+
text=True,
|
594
|
+
capture_output=True,
|
595
|
+
check=False,
|
596
|
+
)
|
597
|
+
if result.returncode != 0:
|
598
|
+
logger.error(f"Error validating requirements.txt file: {result.stderr}")
|
599
|
+
logger.error(
|
600
|
+
"Failed to validate the requirements.txt file, please check the file for errors. Note this can happen if the machine you're upload from has different python version, accelerator, etc. from the desired machine you want to upload to."
|
601
|
+
)
|
602
|
+
logger.error("Output: " + result.stdout)
|
603
|
+
# If we have an error, raise an exception.
|
604
|
+
return False
|
605
|
+
else:
|
606
|
+
logger.info("Setup: Requirements.txt file validated successfully")
|
607
|
+
# If we have no error, we can just return.
|
608
|
+
return True
|
609
|
+
|
566
610
|
def _is_amd(self):
|
567
611
|
"""
|
568
612
|
Check if the model is AMD or not.
|
@@ -582,11 +626,50 @@ class ModelBuilder:
|
|
582
626
|
"Both AMD and NVIDIA GPUs are specified in the config file, please use only one type of GPU."
|
583
627
|
)
|
584
628
|
if is_amd_gpu:
|
585
|
-
logger.info(
|
629
|
+
logger.info(
|
630
|
+
"Setup: Using AMD base image to build the Docker image and upload the model"
|
631
|
+
)
|
586
632
|
elif is_nvidia_gpu:
|
587
|
-
logger.info(
|
633
|
+
logger.info(
|
634
|
+
"Setup: Using NVIDIA base image to build the Docker image and upload the model"
|
635
|
+
)
|
588
636
|
return is_amd_gpu
|
589
637
|
|
638
|
+
def _lint_python_code(self):
|
639
|
+
"""
|
640
|
+
Lint the python code in the model.py file using flake8.
|
641
|
+
This will help catch any simple bugs in the code before uploading it to the API.
|
642
|
+
"""
|
643
|
+
if not shutil.which('ruff'):
|
644
|
+
raise Exception("ruff command not found, please install ruff to lint the python code")
|
645
|
+
# List all the python files in the /1/ folder recursively and lint them.
|
646
|
+
python_files = []
|
647
|
+
for root, _, files in os.walk(os.path.join(self.folder, '1')):
|
648
|
+
for file in files:
|
649
|
+
if file.endswith('.py'):
|
650
|
+
python_files.append(os.path.join(root, file))
|
651
|
+
if not python_files:
|
652
|
+
logger.info("No Python files found to lint, skipping linting step.")
|
653
|
+
else:
|
654
|
+
logger.info(f"Setup: Linting Python files: {python_files}")
|
655
|
+
# Run ruff to lint the python code.
|
656
|
+
command = "ruff check --select=F"
|
657
|
+
result = subprocess.run(
|
658
|
+
f"{command} {' '.join(python_files)}",
|
659
|
+
shell=True,
|
660
|
+
text=True,
|
661
|
+
capture_output=True,
|
662
|
+
check=False,
|
663
|
+
)
|
664
|
+
if result.returncode != 0:
|
665
|
+
logger.error(f"Error linting Python code: {result.stderr}")
|
666
|
+
logger.error("Output: " + result.stdout)
|
667
|
+
logger.error(
|
668
|
+
f"Failed to lint the Python code, please check the code for errors using '{command}' so you don't have simple errors in your code prior to upload."
|
669
|
+
)
|
670
|
+
else:
|
671
|
+
logger.info("Setup: Python code linted successfully, no errors found.")
|
672
|
+
|
590
673
|
def create_dockerfile(self):
|
591
674
|
dockerfile_template = os.path.join(
|
592
675
|
os.path.dirname(os.path.dirname(__file__)),
|
@@ -609,14 +692,21 @@ class ModelBuilder:
|
|
609
692
|
)
|
610
693
|
|
611
694
|
logger.info(
|
612
|
-
f"Using Python version {python_version} from the config file to build the Dockerfile"
|
695
|
+
f"Setup: Using Python version {python_version} from the config file to build the Dockerfile"
|
613
696
|
)
|
614
697
|
else:
|
615
698
|
logger.info(
|
616
|
-
f"Python version not found in the config file, using default Python version: {DEFAULT_PYTHON_VERSION}"
|
699
|
+
f"Setup: Python version not found in the config file, using default Python version: {DEFAULT_PYTHON_VERSION}"
|
617
700
|
)
|
618
701
|
python_version = DEFAULT_PYTHON_VERSION
|
619
702
|
|
703
|
+
# Before we bother even picking the right base image, let's use uv to validate
|
704
|
+
# that the requirements.txt file is valid and compatible.
|
705
|
+
self._validate_requirements(python_version)
|
706
|
+
|
707
|
+
# Make sure any python code will not have simple bugs by linting it first.
|
708
|
+
self._lint_python_code()
|
709
|
+
|
620
710
|
# Parse the requirements.txt file to determine the base image
|
621
711
|
dependencies = self._parse_requirements()
|
622
712
|
|
@@ -637,7 +727,7 @@ class ModelBuilder:
|
|
637
727
|
)
|
638
728
|
if not torch_version:
|
639
729
|
logger.info(
|
640
|
-
f"torch version not found in requirements.txt, using the default version {DEFAULT_AMD_TORCH_VERSION}"
|
730
|
+
f"Setup: torch version not found in requirements.txt, using the default version {DEFAULT_AMD_TORCH_VERSION}"
|
641
731
|
)
|
642
732
|
torch_version = DEFAULT_AMD_TORCH_VERSION
|
643
733
|
if torch_version not in [DEFAULT_AMD_TORCH_VERSION]:
|
@@ -651,7 +741,7 @@ class ModelBuilder:
|
|
651
741
|
python_version=python_version,
|
652
742
|
gpu_version=gpu_version,
|
653
743
|
)
|
654
|
-
logger.info("Using vLLM base image to build the Docker image")
|
744
|
+
logger.info("Setup: Using vLLM base image to build the Docker image")
|
655
745
|
elif 'torch' in dependencies:
|
656
746
|
torch_version = dependencies['torch']
|
657
747
|
if python_version != DEFAULT_PYTHON_VERSION:
|
@@ -675,7 +765,7 @@ class ModelBuilder:
|
|
675
765
|
gpu_version=gpu_version,
|
676
766
|
)
|
677
767
|
logger.info(
|
678
|
-
f"Using Torch version {torch_version} base image to build the Docker image"
|
768
|
+
f"Setup: Using Torch version {torch_version} base image to build the Docker image"
|
679
769
|
)
|
680
770
|
else:
|
681
771
|
final_image = PYTHON_BASE_IMAGE.format(python_version=python_version)
|
@@ -684,6 +774,8 @@ class ModelBuilder:
|
|
684
774
|
torch_version = dependencies['torch']
|
685
775
|
# Sort in reverse so that newer cuda versions come first and are preferred.
|
686
776
|
for image in sorted(AVAILABLE_TORCH_IMAGES, reverse=True):
|
777
|
+
if image.find('rocm') >= 0:
|
778
|
+
continue # skip ROCm images as those are handled above.
|
687
779
|
if torch_version in image and f'py{python_version}' in image:
|
688
780
|
# like cu124, rocm6.3, etc.
|
689
781
|
gpu_version = image.split('-')[-1]
|
@@ -693,7 +785,7 @@ class ModelBuilder:
|
|
693
785
|
gpu_version=gpu_version,
|
694
786
|
)
|
695
787
|
logger.info(
|
696
|
-
f"Using Torch version {torch_version} base image to build the Docker image"
|
788
|
+
f"Setup: Using Torch version {torch_version} base image to build the Docker image"
|
697
789
|
)
|
698
790
|
break
|
699
791
|
if 'clarifai' not in dependencies:
|
@@ -1109,4 +1201,208 @@ def upload_model(folder, stage, skip_dockerfile):
|
|
1109
1201
|
)
|
1110
1202
|
|
1111
1203
|
input("Press Enter to continue...")
|
1112
|
-
builder.upload_model_version()
|
1204
|
+
model_version = builder.upload_model_version()
|
1205
|
+
|
1206
|
+
# Ask user if they want to deploy the model
|
1207
|
+
deploy_model = input("Do you want to deploy the model? (y/n): ")
|
1208
|
+
if deploy_model.lower() != 'y':
|
1209
|
+
logger.info("Model uploaded successfully. Skipping deployment setup.")
|
1210
|
+
return
|
1211
|
+
|
1212
|
+
# Setup deployment for the uploaded model
|
1213
|
+
setup_deployment_for_model(builder)
|
1214
|
+
|
1215
|
+
|
1216
|
+
def setup_deployment_for_model(builder):
|
1217
|
+
"""
|
1218
|
+
Set up deployment for a model after upload.
|
1219
|
+
|
1220
|
+
:param builder: The ModelBuilder instance that has uploaded the model.
|
1221
|
+
"""
|
1222
|
+
|
1223
|
+
model = builder.config.get('model')
|
1224
|
+
user_id = model.get('user_id')
|
1225
|
+
app_id = model.get('app_id')
|
1226
|
+
model_id = model.get('id')
|
1227
|
+
|
1228
|
+
# Set up the API client with the user's credentials
|
1229
|
+
user = User(user_id=user_id, pat=builder.client.pat, base_url=builder.client.base)
|
1230
|
+
|
1231
|
+
# Step 1: Check for available compute clusters and let user choose or create a new one
|
1232
|
+
logger.info("Checking for available compute clusters...")
|
1233
|
+
compute_clusters = list(user.list_compute_clusters())
|
1234
|
+
|
1235
|
+
compute_cluster = None
|
1236
|
+
if compute_clusters:
|
1237
|
+
logger.info("Available compute clusters:")
|
1238
|
+
for i, cc in enumerate(compute_clusters):
|
1239
|
+
logger.info(
|
1240
|
+
f"{i + 1}. {cc.id} ({cc.description if hasattr(cc, 'description') else 'No description'})"
|
1241
|
+
)
|
1242
|
+
|
1243
|
+
choice = input(
|
1244
|
+
f"Choose a compute cluster (1-{len(compute_clusters)}) or 'n' to create a new one: "
|
1245
|
+
)
|
1246
|
+
if choice.lower() == 'n':
|
1247
|
+
create_new_cc = True
|
1248
|
+
else:
|
1249
|
+
try:
|
1250
|
+
idx = int(choice) - 1
|
1251
|
+
if 0 <= idx < len(compute_clusters):
|
1252
|
+
compute_cluster = compute_clusters[idx]
|
1253
|
+
create_new_cc = False
|
1254
|
+
else:
|
1255
|
+
logger.info("Invalid choice. Creating a new compute cluster.")
|
1256
|
+
create_new_cc = True
|
1257
|
+
except ValueError:
|
1258
|
+
logger.info("Invalid choice. Creating a new compute cluster.")
|
1259
|
+
create_new_cc = True
|
1260
|
+
else:
|
1261
|
+
logger.info("No compute clusters found.")
|
1262
|
+
create_new_cc = True
|
1263
|
+
|
1264
|
+
if create_new_cc:
|
1265
|
+
# Provide URL to create a new compute cluster
|
1266
|
+
url_helper = ClarifaiUrlHelper()
|
1267
|
+
compute_cluster_url = f"{url_helper.ui}/settings/compute/new"
|
1268
|
+
logger.info(f"Please create a new compute cluster by visiting: {compute_cluster_url}")
|
1269
|
+
|
1270
|
+
# Ask if they want to open the URL in browser
|
1271
|
+
open_browser = input(
|
1272
|
+
"Do you want to open the compute cluster creation page in your browser? (y/n): "
|
1273
|
+
)
|
1274
|
+
if open_browser.lower() == 'y':
|
1275
|
+
try:
|
1276
|
+
webbrowser.open(compute_cluster_url)
|
1277
|
+
except Exception as e:
|
1278
|
+
logger.error(f"Failed to open browser: {e}")
|
1279
|
+
|
1280
|
+
input("After creating the compute cluster, press Enter to continue...")
|
1281
|
+
|
1282
|
+
# Re-fetch the compute clusters list after user has created one
|
1283
|
+
logger.info("Re-checking for available compute clusters...")
|
1284
|
+
compute_clusters = list(user.list_compute_clusters())
|
1285
|
+
|
1286
|
+
if not compute_clusters:
|
1287
|
+
logger.info(
|
1288
|
+
"No compute clusters found. Please make sure you have created a compute cluster and try again."
|
1289
|
+
)
|
1290
|
+
return
|
1291
|
+
|
1292
|
+
# Show the updated list and let user choose
|
1293
|
+
logger.info("Available compute clusters:")
|
1294
|
+
for i, cc in enumerate(compute_clusters):
|
1295
|
+
logger.info(
|
1296
|
+
f"{i + 1}. {cc.id} ({cc.description if hasattr(cc, 'description') else 'No description'})"
|
1297
|
+
)
|
1298
|
+
|
1299
|
+
choice = input(f"Choose a compute cluster (1-{len(compute_clusters)}): ")
|
1300
|
+
try:
|
1301
|
+
idx = int(choice) - 1
|
1302
|
+
if 0 <= idx < len(compute_clusters):
|
1303
|
+
compute_cluster = compute_clusters[idx]
|
1304
|
+
else:
|
1305
|
+
logger.info("Invalid choice. Aborting deployment setup.")
|
1306
|
+
return
|
1307
|
+
except ValueError:
|
1308
|
+
logger.info("Invalid choice. Aborting deployment setup.")
|
1309
|
+
return
|
1310
|
+
|
1311
|
+
# Step 2: Check for available nodepools and let user choose or create a new one
|
1312
|
+
logger.info(f"Checking for available nodepools in compute cluster '{compute_cluster.id}'...")
|
1313
|
+
nodepools = list(compute_cluster.list_nodepools())
|
1314
|
+
|
1315
|
+
nodepool = None
|
1316
|
+
if nodepools:
|
1317
|
+
logger.info("Available nodepools:")
|
1318
|
+
for i, np in enumerate(nodepools):
|
1319
|
+
logger.info(
|
1320
|
+
f"{i + 1}. {np.id} ({np.description if hasattr(np, 'description') else 'No description'})"
|
1321
|
+
)
|
1322
|
+
|
1323
|
+
choice = input(f"Choose a nodepool (1-{len(nodepools)}) or 'n' to create a new one: ")
|
1324
|
+
if choice.lower() == 'n':
|
1325
|
+
create_new_np = True
|
1326
|
+
else:
|
1327
|
+
try:
|
1328
|
+
idx = int(choice) - 1
|
1329
|
+
if 0 <= idx < len(nodepools):
|
1330
|
+
nodepool = nodepools[idx]
|
1331
|
+
create_new_np = False
|
1332
|
+
else:
|
1333
|
+
logger.info("Invalid choice. Creating a new nodepool.")
|
1334
|
+
create_new_np = True
|
1335
|
+
except ValueError:
|
1336
|
+
logger.info("Invalid choice. Creating a new nodepool.")
|
1337
|
+
create_new_np = True
|
1338
|
+
else:
|
1339
|
+
logger.info("No nodepools found in this compute cluster.")
|
1340
|
+
create_new_np = True
|
1341
|
+
|
1342
|
+
if create_new_np:
|
1343
|
+
# Provide URL to create a new nodepool
|
1344
|
+
url_helper = ClarifaiUrlHelper()
|
1345
|
+
nodepool_url = f"{url_helper.ui}/settings/compute/{compute_cluster.id}/nodepools/new"
|
1346
|
+
logger.info(f"Please create a new nodepool by visiting: {nodepool_url}")
|
1347
|
+
|
1348
|
+
# Ask if they want to open the URL in browser
|
1349
|
+
open_browser = input(
|
1350
|
+
"Do you want to open the nodepool creation page in your browser? (y/n): "
|
1351
|
+
)
|
1352
|
+
if open_browser.lower() == 'y':
|
1353
|
+
try:
|
1354
|
+
webbrowser.open(nodepool_url)
|
1355
|
+
except Exception as e:
|
1356
|
+
logger.error(f"Failed to open browser: {e}")
|
1357
|
+
|
1358
|
+
input("After creating the nodepool, press Enter to continue...")
|
1359
|
+
|
1360
|
+
# Re-fetch the nodepools list after user has created one
|
1361
|
+
logger.info(
|
1362
|
+
f"Re-checking for available nodepools in compute cluster '{compute_cluster.id}'..."
|
1363
|
+
)
|
1364
|
+
nodepools = list(compute_cluster.list_nodepools())
|
1365
|
+
|
1366
|
+
if not nodepools:
|
1367
|
+
logger.info(
|
1368
|
+
"No nodepools found. Please make sure you have created a nodepool in the selected compute cluster and try again."
|
1369
|
+
)
|
1370
|
+
return
|
1371
|
+
|
1372
|
+
# Show the updated list and let user choose
|
1373
|
+
logger.info("Available nodepools:")
|
1374
|
+
for i, np in enumerate(nodepools):
|
1375
|
+
logger.info(
|
1376
|
+
f"{i + 1}. {np.id} ({np.description if hasattr(np, 'description') else 'No description'})"
|
1377
|
+
)
|
1378
|
+
|
1379
|
+
choice = input(f"Choose a nodepool (1-{len(nodepools)}): ")
|
1380
|
+
try:
|
1381
|
+
idx = int(choice) - 1
|
1382
|
+
if 0 <= idx < len(nodepools):
|
1383
|
+
nodepool = nodepools[idx]
|
1384
|
+
else:
|
1385
|
+
logger.info("Invalid choice. Aborting deployment setup.")
|
1386
|
+
return
|
1387
|
+
except ValueError:
|
1388
|
+
logger.info("Invalid choice. Aborting deployment setup.")
|
1389
|
+
return
|
1390
|
+
|
1391
|
+
# Step 3: Help create a new deployment by providing URL
|
1392
|
+
# Provide URL to create a new deployment
|
1393
|
+
url_helper = ClarifaiUrlHelper()
|
1394
|
+
deployment_url = f"{url_helper.ui}/settings/compute/deployments/new?computeClusterId={compute_cluster.id}&nodePoolId={nodepool.id}"
|
1395
|
+
logger.info(f"Please create a new deployment by visiting: {deployment_url}")
|
1396
|
+
|
1397
|
+
# Ask if they want to open the URL in browser
|
1398
|
+
open_browser = input(
|
1399
|
+
"Do you want to open the deployment creation page in your browser? (y/n): "
|
1400
|
+
)
|
1401
|
+
if open_browser.lower() == 'y':
|
1402
|
+
try:
|
1403
|
+
webbrowser.open(deployment_url)
|
1404
|
+
except Exception as e:
|
1405
|
+
logger.error(f"Failed to open browser: {e}")
|
1406
|
+
|
1407
|
+
logger.info("After creating the deployment, your model will be ready for inference!")
|
1408
|
+
logger.info(f"You can always return to view your deployments at: {deployment_url}")
|
@@ -76,7 +76,7 @@ client = OpenAI(
|
|
76
76
|
response = client.chat.completions.create(
|
77
77
|
model="%s",
|
78
78
|
messages=[
|
79
|
-
{"role": "
|
79
|
+
{"role": "system", "content": "Talk like a pirate."},
|
80
80
|
{
|
81
81
|
"role": "user",
|
82
82
|
"content": "How do I check if a Python object is an instance of a class?",
|
clarifai/runners/utils/const.py
CHANGED
@@ -2,9 +2,9 @@ import os
|
|
2
2
|
|
3
3
|
registry = os.environ.get('CLARIFAI_BASE_IMAGE_REGISTRY', 'public.ecr.aws/clarifai-models')
|
4
4
|
|
5
|
-
GIT_SHA = "
|
5
|
+
GIT_SHA = "42938da8e33b0f37ee7db16b83631da94c2348b9"
|
6
6
|
|
7
|
-
AMD_GIT_SHA = "
|
7
|
+
AMD_GIT_SHA = "42938da8e33b0f37ee7db16b83631da94c2348b9"
|
8
8
|
|
9
9
|
PYTHON_BASE_IMAGE = registry + '/python-base:{python_version}-' + GIT_SHA
|
10
10
|
TORCH_BASE_IMAGE = registry + '/torch:{torch_version}-py{python_version}-{gpu_version}-' + GIT_SHA
|
@@ -20,7 +20,7 @@ AMD_VLLM_BASE_IMAGE = (
|
|
20
20
|
# List of available python base images
|
21
21
|
AVAILABLE_PYTHON_IMAGES = ['3.11', '3.12']
|
22
22
|
|
23
|
-
DEFAULT_PYTHON_VERSION = 3.12
|
23
|
+
DEFAULT_PYTHON_VERSION = '3.12'
|
24
24
|
|
25
25
|
DEFAULT_AMD_TORCH_VERSION = '2.8.0.dev20250511+rocm6.4'
|
26
26
|
|
clarifai/workflows/export.py
CHANGED
@@ -16,12 +16,9 @@ def clean_up_unused_keys(wf: dict):
|
|
16
16
|
new_wf["model"] = {
|
17
17
|
"model_id": wf["model"]["id"],
|
18
18
|
"model_version_id": wf["model"]["model_version"]["id"],
|
19
|
+
"user_id": wf["model"]["user_id"],
|
20
|
+
"app_id": wf["model"]["app_id"],
|
19
21
|
}
|
20
|
-
# If the model is not from clarifai main, add the app_id and user_id to the model dict.
|
21
|
-
if wf["model"]["user_id"] != "clarifai" and wf["model"]["app_id"] != "main":
|
22
|
-
new_wf["model"].update(
|
23
|
-
{"app_id": wf["model"]["app_id"], "user_id": wf["model"]["user_id"]}
|
24
|
-
)
|
25
22
|
elif isinstance(val, dict):
|
26
23
|
new_wf[key] = clean_up_unused_keys(val)
|
27
24
|
elif isinstance(val, list):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: clarifai
|
3
|
-
Version: 11.5.
|
3
|
+
Version: 11.5.2
|
4
4
|
Home-page: https://github.com/Clarifai/clarifai-python
|
5
5
|
Author: Clarifai
|
6
6
|
Author-email: support@clarifai.com
|
@@ -31,6 +31,8 @@ Requires-Dist: fsspec>=2024.6.1
|
|
31
31
|
Requires-Dist: click>=8.1.7
|
32
32
|
Requires-Dist: requests>=2.32.3
|
33
33
|
Requires-Dist: aiohttp>=3.10.0
|
34
|
+
Requires-Dist: uv==0.7.12
|
35
|
+
Requires-Dist: ruff==0.11.4
|
34
36
|
Provides-Extra: all
|
35
37
|
Requires-Dist: pycocotools>=2.0.7; extra == "all"
|
36
38
|
Dynamic: author
|
@@ -1,4 +1,4 @@
|
|
1
|
-
clarifai/__init__.py,sha256=
|
1
|
+
clarifai/__init__.py,sha256=a5pEEaKahMvZwBR_7X6j8C953yywB3wOFeS2bzAe50o,23
|
2
2
|
clarifai/cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
clarifai/errors.py,sha256=GXa6D4v_L404J83jnRNFPH7s-1V9lk7w6Ws99f1g-AY,2772
|
4
4
|
clarifai/versions.py,sha256=ecSuEB_nOL2XSoYHDw2n23XUbm_KPOGjudMXmQrGdS8,224
|
@@ -8,11 +8,11 @@ clarifai/cli/__main__.py,sha256=7nPbLW7Jr2shkgMPvnxpn4xYGMvIcnqluJ69t9w4H_k,74
|
|
8
8
|
clarifai/cli/base.py,sha256=mzfAHRhon6tKntpxk241GD-Sjrb2-V99nAOasElLuuw,8254
|
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=
|
11
|
+
clarifai/cli/model.py,sha256=RYo0ZGdTXG8e72QDH4-w5_73aa30sNHE5o2f1pWZpK0,30793
|
12
12
|
clarifai/cli/model_templates.py,sha256=_ZonIBnY9KKSJY31KZbUys_uN_k_Txu7Dip12KWfmSU,9633
|
13
13
|
clarifai/cli/nodepool.py,sha256=H6OIdUW_EiyDUwZogzEDoYmVwEjLMsgoDlPyE7gjIuU,4245
|
14
14
|
clarifai/client/__init__.py,sha256=NhpNFRJY6mTi8ca-5hUeTEmYeDKHDNXY48FN63pDuos,703
|
15
|
-
clarifai/client/app.py,sha256=
|
15
|
+
clarifai/client/app.py,sha256=1M9XDsPWIEsj0g-mgIeZ9Mvkt85UHSbrv6pEr-QKfNg,41423
|
16
16
|
clarifai/client/base.py,sha256=zOmB5HJP_-NmF2BPka14W7VUeJ1OF-fNxeacLsaRj3E,8775
|
17
17
|
clarifai/client/compute_cluster.py,sha256=ViPyh-FibXL1J0ypsVOTaQnR1ymKohmZEuA13RwA-hc,10254
|
18
18
|
clarifai/client/dataset.py,sha256=OgdpZkQ_vYmRxL8-qphcNozpvPV1bWTlte9Jv6UkKb8,35299
|
@@ -28,7 +28,7 @@ clarifai/client/search.py,sha256=3LLfATrdU43a0mRNITmJV-E53bhfafZkYsbwkTtlnyU,156
|
|
28
28
|
clarifai/client/user.py,sha256=_G_x3P6IJqtRqI2r0HDAhSfQJmqWgEfwEVmRTOJISbw,19052
|
29
29
|
clarifai/client/workflow.py,sha256=Bqh8lAmJcSbviJebckchTxucYlU11UQEhFSov7elpsk,13417
|
30
30
|
clarifai/client/auth/__init__.py,sha256=7EwR0NrozkAUwpUnCsqXvE_p0wqx_SelXlSpKShKJK0,136
|
31
|
-
clarifai/client/auth/helper.py,sha256=
|
31
|
+
clarifai/client/auth/helper.py,sha256=T-j2hFoczdbWft5p72aU3vTOEr9H8xOOZlBXA5JBJL0,15181
|
32
32
|
clarifai/client/auth/register.py,sha256=pyY-Kg_64WpN6rXz1SyEzfqL14BS4yADtuYMxLJ4jx4,554
|
33
33
|
clarifai/client/auth/stub.py,sha256=pU4FYeghooCBZmCRyNTdDfJaVe4WyeRBqE3xVwfmMTY,5388
|
34
34
|
clarifai/constants/base.py,sha256=ogmFSZYoF0YhGjHg5aiOc3MLqPr_poKAls6xaD0_C3U,89
|
@@ -64,13 +64,13 @@ clarifai/modules/style.css,sha256=j7FNPZVhLPj35vvBksAJ90RuX5sLuqzDR5iM2WIEhiA,60
|
|
64
64
|
clarifai/rag/__init__.py,sha256=wu3PzAzo7uqgrEzuaC9lY_3gj1HFiR3GU3elZIKTT5g,40
|
65
65
|
clarifai/rag/rag.py,sha256=EG3GoFrHFCmA70Tz49_0Jo1-3WIaHSgWGHecPeErcdc,14170
|
66
66
|
clarifai/rag/utils.py,sha256=_gVZdABuMnraCKViLruV75x0F3IpgFXN6amYSGE5_xc,4462
|
67
|
-
clarifai/runners/__init__.py,sha256=
|
67
|
+
clarifai/runners/__init__.py,sha256=wXLaSljH7qLeJCrZdKEnlQh2tNqTQAIZKWOu2rZ6wGs,279
|
68
68
|
clarifai/runners/server.py,sha256=9qVAs8pRHmtyY0RCNIQ1uP8nqDADIFZ03LnkoDt1h4U,4692
|
69
|
-
clarifai/runners/dockerfile_template/Dockerfile.template,sha256=
|
69
|
+
clarifai/runners/dockerfile_template/Dockerfile.template,sha256=DUH7F0-uLOV0LTjnPde-9chSzscAAxBAwjTxi9b_l9g,2425
|
70
70
|
clarifai/runners/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
71
|
clarifai/runners/models/dummy_openai_model.py,sha256=pcmAVbqTTGG4J3BLVjKfvM_SQ-GET_XexIUdLcr9Zvo,8373
|
72
|
-
clarifai/runners/models/mcp_class.py,sha256=
|
73
|
-
clarifai/runners/models/model_builder.py,sha256=
|
72
|
+
clarifai/runners/models/mcp_class.py,sha256=RdKn7rW4vYol0VRDZiLTSMfkqjLhO1ijXAQ0Rq0Jfnw,6647
|
73
|
+
clarifai/runners/models/model_builder.py,sha256=s5TpCnLeiYLBgWHlCT6x6vpwTGX52oywViU6xDRl0MU,61599
|
74
74
|
clarifai/runners/models/model_class.py,sha256=-euUF-eHUi4KXR_e1pIwvToDZ13CM6TSz2FolzildjM,16069
|
75
75
|
clarifai/runners/models/model_run_locally.py,sha256=6-6WjEKc0ba3gAv4wOLdMs2XOzS3b-2bZHJS0wdVqJY,20088
|
76
76
|
clarifai/runners/models/model_runner.py,sha256=SccX-RxTgruSpQaM21uMSl-z1x6fOa13fQZMQW8NNRY,7297
|
@@ -79,8 +79,8 @@ clarifai/runners/models/openai_class.py,sha256=aXlk5W6LWkh-A4eZYi74DeLW0i_86_9DY
|
|
79
79
|
clarifai/runners/models/visual_classifier_class.py,sha256=1ZoLfCT2crrgRbejjTMAIwpTRgQMiH9N9yflOVpFxSg,2721
|
80
80
|
clarifai/runners/models/visual_detector_class.py,sha256=ky4oFAkGCKPpGPdgaOso-n6D3HcmnbKee_8hBsNiV8U,2883
|
81
81
|
clarifai/runners/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
82
|
-
clarifai/runners/utils/code_script.py,sha256=
|
83
|
-
clarifai/runners/utils/const.py,sha256=
|
82
|
+
clarifai/runners/utils/code_script.py,sha256=1n525IhMEdWlr9jBUG76tMa42piZtl0CmWYBmkvu368,12769
|
83
|
+
clarifai/runners/utils/const.py,sha256=B0TnmVgjh5NlsXIkowrlgz2QdsgLj4a-gu4Igc9ukCo,1546
|
84
84
|
clarifai/runners/utils/data_utils.py,sha256=HRpMYR2O0OiDpXXhOManLHTeomC4bFnXMHVAiT_12yE,20856
|
85
85
|
clarifai/runners/utils/loader.py,sha256=K5Y8MPbIe5STw2gDnrL8KqFgKNxEo7bz-RV0ip1T4PM,10900
|
86
86
|
clarifai/runners/utils/method_signatures.py,sha256=qdHaO8ZIgP6BBXXMhMPhcQ46dse-XMP2t4VJCNG7O3Q,18335
|
@@ -104,12 +104,12 @@ clarifai/utils/evaluation/helpers.py,sha256=0t6eIDXeZEoiVvnmHTnsIF_-v4BzrQW1hFaq
|
|
104
104
|
clarifai/utils/evaluation/main.py,sha256=N_sfRuMjHrUeuWN0Pzms65M1PbkQkgYg3WoQVaDR1Jw,17764
|
105
105
|
clarifai/utils/evaluation/testset_annotation_parser.py,sha256=Nmodfi5BYFYEbybWcC8tmU5-wtwRBsWIbnpd3OvKSmA,5414
|
106
106
|
clarifai/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
107
|
-
clarifai/workflows/export.py,sha256=
|
107
|
+
clarifai/workflows/export.py,sha256=HvUYG9N_-UZoRR0-_tdGbZ950_AeBqawSppgUxQebR0,1913
|
108
108
|
clarifai/workflows/utils.py,sha256=ESL3INcouNcLKCh-nMpfXX-YbtCzX7tz7hT57_RGQ3M,2079
|
109
109
|
clarifai/workflows/validate.py,sha256=UhmukyHkfxiMFrPPeBdUTiCOHQT5-shqivlBYEyKTlU,2931
|
110
|
-
clarifai-11.5.
|
111
|
-
clarifai-11.5.
|
112
|
-
clarifai-11.5.
|
113
|
-
clarifai-11.5.
|
114
|
-
clarifai-11.5.
|
115
|
-
clarifai-11.5.
|
110
|
+
clarifai-11.5.2.dist-info/licenses/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
|
111
|
+
clarifai-11.5.2.dist-info/METADATA,sha256=RMLvJgPoDkyA4gjGfP0_LjJeN4encm6srVnGu7ibDBk,22736
|
112
|
+
clarifai-11.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
113
|
+
clarifai-11.5.2.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
|
114
|
+
clarifai-11.5.2.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
|
115
|
+
clarifai-11.5.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|