clarifai 11.4.5__py3-none-any.whl → 11.4.7__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/base.py +3 -2
- clarifai/cli/model.py +12 -12
- clarifai/client/model.py +11 -21
- clarifai/client/user.py +18 -0
- clarifai/runners/models/dummy_openai_model.py +2 -2
- clarifai/runners/models/model_class.py +8 -0
- clarifai/runners/models/openai_class.py +15 -13
- clarifai/runners/utils/code_script.py +47 -6
- clarifai/urls/helper.py +127 -17
- clarifai/utils/config.py +38 -6
- clarifai/utils/constants.py +2 -0
- {clarifai-11.4.5.dist-info → clarifai-11.4.7.dist-info}/METADATA +1 -1
- {clarifai-11.4.5.dist-info → clarifai-11.4.7.dist-info}/RECORD +18 -18
- {clarifai-11.4.5.dist-info → clarifai-11.4.7.dist-info}/WHEEL +0 -0
- {clarifai-11.4.5.dist-info → clarifai-11.4.7.dist-info}/entry_points.txt +0 -0
- {clarifai-11.4.5.dist-info → clarifai-11.4.7.dist-info}/licenses/LICENSE +0 -0
- {clarifai-11.4.5.dist-info → clarifai-11.4.7.dist-info}/top_level.txt +0 -0
clarifai/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "11.4.
|
1
|
+
__version__ = "11.4.7"
|
clarifai/cli/base.py
CHANGED
@@ -8,7 +8,7 @@ import yaml
|
|
8
8
|
|
9
9
|
from clarifai.utils.cli import AliasedGroup, TableFormatter, load_command_modules
|
10
10
|
from clarifai.utils.config import Config, Context
|
11
|
-
from clarifai.utils.constants import DEFAULT_CONFIG
|
11
|
+
from clarifai.utils.constants import DEFAULT_BASE, DEFAULT_CONFIG, DEFAULT_UI
|
12
12
|
from clarifai.utils.logging import logger
|
13
13
|
|
14
14
|
|
@@ -31,7 +31,8 @@ def cli(ctx, config):
|
|
31
31
|
'default',
|
32
32
|
CLARIFAI_PAT=os.environ.get('CLARIFAI_PAT', ''),
|
33
33
|
CLARIFAI_USER_ID=os.environ.get('CLARIFAI_USER_ID', ''),
|
34
|
-
CLARIFAI_API_BASE=
|
34
|
+
CLARIFAI_API_BASE=DEFAULT_BASE,
|
35
|
+
CLARIFAI_UI=DEFAULT_UI,
|
35
36
|
)
|
36
37
|
},
|
37
38
|
)
|
clarifai/cli/model.py
CHANGED
@@ -505,7 +505,7 @@ def local_dev(ctx, model_path):
|
|
505
505
|
logger.info(f"Current deployment_id: {deployment_id}")
|
506
506
|
|
507
507
|
logger.info(
|
508
|
-
f"Full url for the model: /users/{user_id}/apps/{app_id}/models/{model.id}/versions/{version.id}"
|
508
|
+
f"Full url for the model: {ctx.obj.current.ui}/users/{user_id}/apps/{app_id}/models/{model.id}/versions/{version.id}"
|
509
509
|
)
|
510
510
|
|
511
511
|
# Now that we have all the context in ctx.obj, we need to update the config.yaml in
|
@@ -542,7 +542,6 @@ def local_dev(ctx, model_path):
|
|
542
542
|
app_id=app_id,
|
543
543
|
model_id=model_id,
|
544
544
|
deployment_id=deployment_id,
|
545
|
-
use_ctx=True,
|
546
545
|
base_url=ctx.obj.current.api_base,
|
547
546
|
)
|
548
547
|
|
@@ -683,7 +682,14 @@ def predict(
|
|
683
682
|
"Either --compute_cluster_id & --nodepool_id or --deployment_id must be provided."
|
684
683
|
)
|
685
684
|
if model_url:
|
686
|
-
model = Model(
|
685
|
+
model = Model(
|
686
|
+
url=model_url,
|
687
|
+
pat=ctx.obj['pat'],
|
688
|
+
base_url=ctx.obj['base_url'],
|
689
|
+
compute_cluster_id=compute_cluster_id,
|
690
|
+
nodepool_id=nodepool_id,
|
691
|
+
deployment_id=deployment_id,
|
692
|
+
)
|
687
693
|
else:
|
688
694
|
model = Model(
|
689
695
|
model_id=model_id,
|
@@ -691,6 +697,9 @@ def predict(
|
|
691
697
|
app_id=app_id,
|
692
698
|
pat=ctx.obj['pat'],
|
693
699
|
base_url=ctx.obj['base_url'],
|
700
|
+
compute_cluster_id=compute_cluster_id,
|
701
|
+
nodepool_id=nodepool_id,
|
702
|
+
deployment_id=deployment_id,
|
694
703
|
)
|
695
704
|
|
696
705
|
if inference_params:
|
@@ -702,9 +711,6 @@ def predict(
|
|
702
711
|
model_prediction = model.predict_by_filepath(
|
703
712
|
filepath=file_path,
|
704
713
|
input_type=input_type,
|
705
|
-
compute_cluster_id=compute_cluster_id,
|
706
|
-
nodepool_id=nodepool_id,
|
707
|
-
deployment_id=deployment_id,
|
708
714
|
inference_params=inference_params,
|
709
715
|
output_config=output_config,
|
710
716
|
)
|
@@ -712,9 +718,6 @@ def predict(
|
|
712
718
|
model_prediction = model.predict_by_url(
|
713
719
|
url=url,
|
714
720
|
input_type=input_type,
|
715
|
-
compute_cluster_id=compute_cluster_id,
|
716
|
-
nodepool_id=nodepool_id,
|
717
|
-
deployment_id=deployment_id,
|
718
721
|
inference_params=inference_params,
|
719
722
|
output_config=output_config,
|
720
723
|
)
|
@@ -723,9 +726,6 @@ def predict(
|
|
723
726
|
model_prediction = model.predict_by_bytes(
|
724
727
|
input_bytes=bytes,
|
725
728
|
input_type=input_type,
|
726
|
-
compute_cluster_id=compute_cluster_id,
|
727
|
-
nodepool_id=nodepool_id,
|
728
|
-
deployment_id=deployment_id,
|
729
729
|
inference_params=inference_params,
|
730
730
|
output_config=output_config,
|
731
731
|
) ## TO DO: Add support for input_id
|
clarifai/client/model.py
CHANGED
@@ -108,7 +108,6 @@ class Model(Lister, BaseClient):
|
|
108
108
|
compute_cluster_id=compute_cluster_id,
|
109
109
|
nodepool_id=nodepool_id,
|
110
110
|
deployment_id=deployment_id,
|
111
|
-
user_id=self.user_id, # FIXME the deployment's user_id can be different than the model's.
|
112
111
|
)
|
113
112
|
BaseClient.__init__(
|
114
113
|
self,
|
@@ -123,14 +122,12 @@ class Model(Lister, BaseClient):
|
|
123
122
|
|
124
123
|
@classmethod
|
125
124
|
def from_current_context(cls, **kwargs) -> 'Model':
|
126
|
-
from clarifai.
|
125
|
+
from clarifai.urls.helper import ClarifaiUrlHelper
|
127
126
|
|
128
|
-
current
|
129
|
-
|
130
|
-
|
131
|
-
current.
|
132
|
-
|
133
|
-
url = f"https://clarifai.com/{current.user_id}/{current.app_id}/models/{current.model_id}"
|
127
|
+
# passing None to ClarifaiUrlHelper uses the current context to set it up.
|
128
|
+
url_helper = ClarifaiUrlHelper()
|
129
|
+
current = url_helper.current_ctx
|
130
|
+
url = url_helper.clarifai_url(resource_type="models", resource_id=current.model_id)
|
134
131
|
|
135
132
|
# construct the Model object.
|
136
133
|
kwargs = {}
|
@@ -601,8 +598,13 @@ class Model(Lister, BaseClient):
|
|
601
598
|
compute_cluster_id: str = None,
|
602
599
|
nodepool_id: str = None,
|
603
600
|
deployment_id: str = None,
|
604
|
-
user_id: str = None,
|
605
601
|
):
|
602
|
+
# Get UserID
|
603
|
+
if any([deployment_id, nodepool_id, compute_cluster_id]):
|
604
|
+
from clarifai.client.user import User
|
605
|
+
|
606
|
+
user_id = User().get_user_info(user_id='me').user.id
|
607
|
+
|
606
608
|
runner_selector = None
|
607
609
|
if deployment_id and (compute_cluster_id or nodepool_id):
|
608
610
|
raise UserError(
|
@@ -610,22 +612,10 @@ class Model(Lister, BaseClient):
|
|
610
612
|
)
|
611
613
|
|
612
614
|
if deployment_id:
|
613
|
-
if not user_id and not os.environ.get('CLARIFAI_USER_ID'):
|
614
|
-
raise UserError(
|
615
|
-
"User ID is required for model prediction with deployment ID, please provide user_id in the method call."
|
616
|
-
)
|
617
|
-
if not user_id:
|
618
|
-
user_id = os.environ.get('CLARIFAI_USER_ID')
|
619
615
|
runner_selector = Deployment.get_runner_selector(
|
620
616
|
user_id=user_id, deployment_id=deployment_id
|
621
617
|
)
|
622
618
|
elif compute_cluster_id and nodepool_id:
|
623
|
-
if not user_id and not os.environ.get('CLARIFAI_USER_ID'):
|
624
|
-
raise UserError(
|
625
|
-
"User ID is required for model prediction with compute cluster ID and nodepool ID, please provide user_id in the method call."
|
626
|
-
)
|
627
|
-
if not user_id:
|
628
|
-
user_id = os.environ.get('CLARIFAI_USER_ID')
|
629
619
|
runner_selector = Nodepool.get_runner_selector(
|
630
620
|
user_id=user_id, compute_cluster_id=compute_cluster_id, nodepool_id=nodepool_id
|
631
621
|
)
|
clarifai/client/user.py
CHANGED
@@ -427,6 +427,24 @@ class User(Lister, BaseClient):
|
|
427
427
|
raise Exception(response.status)
|
428
428
|
self.logger.info("\nCompute Cluster Deleted\n%s", response.status)
|
429
429
|
|
430
|
+
def get_user_info(self, user_id: str = None) -> resources_pb2.User:
|
431
|
+
"""Returns the user information for the specified user ID.
|
432
|
+
|
433
|
+
Args:
|
434
|
+
user_id (str): The user ID for the user to interact with.
|
435
|
+
|
436
|
+
Returns:
|
437
|
+
User: A User object for the specified user ID.
|
438
|
+
"""
|
439
|
+
request = service_pb2.GetUserRequest(
|
440
|
+
user_app_id=resources_pb2.UserAppIDSet(user_id=user_id if user_id else self.id)
|
441
|
+
)
|
442
|
+
response = self._grpc_request(self.STUB.GetUser, request)
|
443
|
+
if response.status.code != status_code_pb2.SUCCESS:
|
444
|
+
raise Exception(response.status)
|
445
|
+
|
446
|
+
return response
|
447
|
+
|
430
448
|
def __getattr__(self, name):
|
431
449
|
return getattr(self.user_info, name)
|
432
450
|
|
@@ -144,8 +144,8 @@ class MockCompletionStream:
|
|
144
144
|
class DummyOpenAIModel(OpenAIModelClass):
|
145
145
|
"""Dummy OpenAI model implementation for testing."""
|
146
146
|
|
147
|
-
|
148
|
-
|
147
|
+
client = MockOpenAIClient()
|
148
|
+
model = "dummy-model"
|
149
149
|
|
150
150
|
def _process_request(self, **kwargs) -> Dict[str, Any]:
|
151
151
|
"""Process a request for non-streaming responses."""
|
@@ -6,6 +6,7 @@ import traceback
|
|
6
6
|
from abc import ABC
|
7
7
|
from collections import abc
|
8
8
|
from typing import Any, Dict, Iterator, List
|
9
|
+
from unittest.mock import MagicMock
|
9
10
|
|
10
11
|
from clarifai_grpc.grpc.api import resources_pb2, service_pb2
|
11
12
|
from clarifai_grpc.grpc.api.status import status_code_pb2, status_pb2
|
@@ -346,6 +347,13 @@ class ModelClass(ABC):
|
|
346
347
|
methods = {}
|
347
348
|
for base in reversed(cls.__mro__):
|
348
349
|
for name, method in base.__dict__.items():
|
350
|
+
# Skip class attributes, mocked objects, and non-methods
|
351
|
+
if not callable(method) or isinstance(method, (classmethod, staticmethod)):
|
352
|
+
continue
|
353
|
+
# Skip any mocked objects or attributes
|
354
|
+
if isinstance(method, MagicMock) or hasattr(method, '_mock_return_value'):
|
355
|
+
continue
|
356
|
+
# Only include methods that have been decorated with @ModelClass.method
|
349
357
|
method_info = getattr(method, _METHOD_INFO_ATTR, None)
|
350
358
|
if not method_info: # regular function, not a model method
|
351
359
|
continue
|
@@ -11,27 +11,29 @@ class OpenAIModelClass(ModelClass):
|
|
11
11
|
This handles all the transport between the API and the OpenAI-compatible server.
|
12
12
|
|
13
13
|
To use this class, create a subclass and set the following class attributes:
|
14
|
-
-
|
15
|
-
-
|
14
|
+
- client: The OpenAI-compatible client instance
|
15
|
+
- model: The name of the model to use with the client
|
16
16
|
|
17
17
|
Example:
|
18
18
|
class MyOpenAIModel(OpenAIModelClass):
|
19
|
-
|
20
|
-
|
19
|
+
client = OpenAI(api_key="your-key")
|
20
|
+
model = "gpt-4"
|
21
21
|
"""
|
22
22
|
|
23
23
|
# These should be overridden in subclasses
|
24
|
-
|
25
|
-
|
24
|
+
client = None
|
25
|
+
model = None
|
26
26
|
|
27
27
|
def __init__(self) -> None:
|
28
|
-
if self.
|
29
|
-
raise NotImplementedError("Subclasses must set the '
|
30
|
-
if self.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
if self.client is None:
|
29
|
+
raise NotImplementedError("Subclasses must set the 'client' class attribute")
|
30
|
+
if self.model is None:
|
31
|
+
try:
|
32
|
+
self.model = self.client.models.list().data[0].id
|
33
|
+
except Exception as e:
|
34
|
+
raise NotImplementedError(
|
35
|
+
"Subclasses must set the 'model' class attribute or ensure the client can list models"
|
36
|
+
) from e
|
35
37
|
|
36
38
|
def _extract_request_params(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
|
37
39
|
"""Extract and validate common openai arguments parameters from the request data.
|
@@ -5,6 +5,20 @@ from clarifai_grpc.grpc.api import resources_pb2
|
|
5
5
|
|
6
6
|
from clarifai.runners.utils import data_utils
|
7
7
|
from clarifai.urls.helper import ClarifaiUrlHelper
|
8
|
+
from clarifai.utils.constants import MCP_TRANSPORT_NAME, OPENAI_TRANSPORT_NAME
|
9
|
+
|
10
|
+
|
11
|
+
def has_signature_method(
|
12
|
+
name: str, method_signatures: List[resources_pb2.MethodSignature]
|
13
|
+
) -> bool:
|
14
|
+
"""
|
15
|
+
Check if a method signature with the given name exists in the list of method signatures.
|
16
|
+
|
17
|
+
:param name: The name of the method to check.
|
18
|
+
:param method_signatures: List of MethodSignature objects to search in.
|
19
|
+
:return: True if a method with the given name exists, False otherwise.
|
20
|
+
"""
|
21
|
+
return any(method_signature.name == name for method_signature in method_signatures)
|
8
22
|
|
9
23
|
|
10
24
|
def generate_client_script(
|
@@ -18,12 +32,11 @@ def generate_client_script(
|
|
18
32
|
) -> str:
|
19
33
|
url_helper = ClarifaiUrlHelper()
|
20
34
|
|
21
|
-
# Provide an mcp client config
|
22
|
-
if
|
23
|
-
|
35
|
+
# Provide an mcp client config if there is a method named "mcp_transport"
|
36
|
+
if has_signature_method(MCP_TRANSPORT_NAME, method_signatures):
|
37
|
+
mcp_url = url_helper.mcp_api_url(
|
24
38
|
user_id,
|
25
39
|
app_id,
|
26
|
-
"models",
|
27
40
|
model_id,
|
28
41
|
)
|
29
42
|
|
@@ -33,20 +46,48 @@ import os
|
|
33
46
|
from fastmcp import Client
|
34
47
|
from fastmcp.client.transports import StreamableHttpTransport
|
35
48
|
|
36
|
-
transport = StreamableHttpTransport(url="%s
|
49
|
+
transport = StreamableHttpTransport(url="%s",
|
37
50
|
headers={"Authorization": "Bearer " + os.environ["CLARIFAI_PAT"]})
|
38
51
|
|
39
52
|
async def main():
|
40
53
|
async with Client(transport) as client:
|
41
54
|
tools = await client.list_tools()
|
42
55
|
print(f"Available tools: {tools}")
|
56
|
+
# TODO: update the dictionary of arguments passed to call_tool to make sense for your MCP.
|
43
57
|
result = await client.call_tool(tools[0].name, {"a": 5, "b": 3})
|
44
58
|
print(f"Result: {result[0].text}")
|
45
59
|
|
46
60
|
if __name__ == "__main__":
|
47
61
|
asyncio.run(main())
|
48
62
|
"""
|
49
|
-
return _CLIENT_TEMPLATE %
|
63
|
+
return _CLIENT_TEMPLATE % mcp_url
|
64
|
+
|
65
|
+
if has_signature_method(OPENAI_TRANSPORT_NAME, method_signatures):
|
66
|
+
openai_api_base = url_helper.openai_api_url()
|
67
|
+
model_ui_url = url_helper.clarifai_url(user_id, app_id, "models", model_id)
|
68
|
+
_CLIENT_TEMPLATE = """
|
69
|
+
import os
|
70
|
+
from openai import OpenAI
|
71
|
+
|
72
|
+
client = OpenAI(
|
73
|
+
base_url="%s",
|
74
|
+
api_key=os.environ['CLARIFAI_PAT'],
|
75
|
+
)
|
76
|
+
response = client.chat.completions.create(
|
77
|
+
model="%s",
|
78
|
+
messages=[
|
79
|
+
{"role": "developer", "content": "Talk like a pirate."},
|
80
|
+
{
|
81
|
+
"role": "user",
|
82
|
+
"content": "How do I check if a Python object is an instance of a class?",
|
83
|
+
},
|
84
|
+
],
|
85
|
+
temperature=0.7,
|
86
|
+
stream=False, # stream=True also works, just iterator over the response
|
87
|
+
)
|
88
|
+
print(response)
|
89
|
+
"""
|
90
|
+
return _CLIENT_TEMPLATE % (openai_api_base, model_ui_url)
|
50
91
|
|
51
92
|
_CLIENT_TEMPLATE = """\
|
52
93
|
import os
|
clarifai/urls/helper.py
CHANGED
@@ -1,15 +1,8 @@
|
|
1
|
-
import os
|
2
1
|
from collections import namedtuple
|
3
2
|
from urllib.parse import urlparse
|
4
3
|
|
5
|
-
from clarifai.utils.constants import DEFAULT_BASE, DEFAULT_UI
|
6
|
-
|
7
4
|
# To help with using ClarifaiUrlHelper with defaults as ClarifaiUrlHelper()
|
8
5
|
auth_obj = namedtuple("auth", ["ui", "base"])
|
9
|
-
default_auth = auth_obj(
|
10
|
-
ui=os.environ.get("CLARIFAI_UI", DEFAULT_UI),
|
11
|
-
base=os.environ.get("CLARIFAI_API_BASE", DEFAULT_BASE),
|
12
|
-
)
|
13
6
|
|
14
7
|
|
15
8
|
class ClarifaiUrlHelper(object):
|
@@ -17,15 +10,19 @@ class ClarifaiUrlHelper(object):
|
|
17
10
|
|
18
11
|
def __init__(
|
19
12
|
self,
|
20
|
-
auth=
|
13
|
+
auth=None,
|
21
14
|
module_manager_imv_id: str = "module_manager_install",
|
22
15
|
):
|
23
16
|
"""
|
24
17
|
Args:
|
25
|
-
auth: a ClarifaiAuthHelper object.
|
18
|
+
auth: a ClarifaiAuthHelper object. Pass None to use the values from the current context.
|
19
|
+
module_manager_imv_id: the ID of the module manager installed module version.
|
26
20
|
"""
|
27
21
|
self._auth = auth
|
28
22
|
self._module_manager_imv_id = module_manager_imv_id
|
23
|
+
self._current_context = None
|
24
|
+
if self._auth is None:
|
25
|
+
self._auth = auth_obj(self.current_ctx.ui, self.current_ctx.api_base)
|
29
26
|
|
30
27
|
@property
|
31
28
|
def ui(self):
|
@@ -35,8 +32,31 @@ class ClarifaiUrlHelper(object):
|
|
35
32
|
def base(self):
|
36
33
|
return self._auth.base
|
37
34
|
|
38
|
-
|
35
|
+
@property
|
36
|
+
def current_ctx(self):
|
37
|
+
if self._current_context is None:
|
38
|
+
from clarifai.utils.config import Config
|
39
|
+
|
40
|
+
self._current_context = Config.from_yaml().current
|
41
|
+
return self._current_context
|
42
|
+
|
43
|
+
def module_ui_url(
|
44
|
+
self,
|
45
|
+
user_id: str = None,
|
46
|
+
app_id: str = None,
|
47
|
+
module_id: str = None,
|
48
|
+
module_version_id: str = None,
|
49
|
+
):
|
39
50
|
"""This is the path to the module in community."""
|
51
|
+
if user_id is None:
|
52
|
+
user_id = self.current_ctx.user_id
|
53
|
+
if app_id is None:
|
54
|
+
app_id = self.current_ctx.app_id
|
55
|
+
if module_id is None:
|
56
|
+
module_id = self.current_ctx.module_id
|
57
|
+
if module_version_id is None:
|
58
|
+
module_version_id = self.current_ctx.module_version_id
|
59
|
+
|
40
60
|
return "%s/%s/%s/modules/%s/versions/%s" % (
|
41
61
|
self.ui,
|
42
62
|
user_id,
|
@@ -45,9 +65,17 @@ class ClarifaiUrlHelper(object):
|
|
45
65
|
module_version_id,
|
46
66
|
)
|
47
67
|
|
48
|
-
def module_install_ui_url(
|
68
|
+
def module_install_ui_url(
|
69
|
+
self, dest_user_id: str = None, dest_app_id: str = None, module_url: str = None
|
70
|
+
):
|
49
71
|
"""This is a url that allows for installation of the module from the community at 'module_url'
|
50
72
|
into the destination app_id of the destination user_id."""
|
73
|
+
if dest_user_id is None:
|
74
|
+
dest_user_id = self.current_ctx.user_id
|
75
|
+
if dest_app_id is None:
|
76
|
+
dest_app_id = self.current_ctx.app_id
|
77
|
+
if module_url is None:
|
78
|
+
raise ValueError("module_url must be provided to install a module.")
|
51
79
|
return "%s/%s/%s/installed_module_versions/%s/install?install=%s" % (
|
52
80
|
self.ui,
|
53
81
|
dest_user_id,
|
@@ -56,7 +84,14 @@ class ClarifaiUrlHelper(object):
|
|
56
84
|
module_url,
|
57
85
|
)
|
58
86
|
|
59
|
-
def imv_ui_url(self, dest_user_id, dest_app_id, imv_id):
|
87
|
+
def imv_ui_url(self, dest_user_id: str = None, dest_app_id: str = None, imv_id: str = None):
|
88
|
+
"""This is the path to the resource in the UI."""
|
89
|
+
if dest_user_id is None:
|
90
|
+
dest_user_id = self.current_ctx.user_id
|
91
|
+
if dest_app_id is None:
|
92
|
+
dest_app_id = self.current_ctx.app_id
|
93
|
+
if imv_id is None:
|
94
|
+
raise ValueError("imv_id must be provided to get the IMV API URL.")
|
60
95
|
return "%s/%s/%s/installed_module_versions/%s" % (
|
61
96
|
self.ui,
|
62
97
|
dest_user_id,
|
@@ -64,7 +99,63 @@ class ClarifaiUrlHelper(object):
|
|
64
99
|
imv_id,
|
65
100
|
)
|
66
101
|
|
67
|
-
def
|
102
|
+
def mcp_api_url(
|
103
|
+
self, user_id: str = None, app_id: str = None, model_id: str = None, version_id: str = None
|
104
|
+
):
|
105
|
+
"""We have a special endpoint for MCP hosted models.
|
106
|
+
|
107
|
+
Example:
|
108
|
+
https://api.clarifai.com/v2/ext/mcp/v1/users/{user_id}/apps/{app_id}/models/{model_id}/versions/{version_id}
|
109
|
+
|
110
|
+
Args:
|
111
|
+
user_id: the author of the resource.
|
112
|
+
app_id: the author's app the resource was created in.
|
113
|
+
model_id: the resource ID
|
114
|
+
version_id: the version of the resource.
|
115
|
+
"""
|
116
|
+
if user_id is None:
|
117
|
+
user_id = self.current_ctx.user_id
|
118
|
+
if app_id is None:
|
119
|
+
app_id = self.current_ctx.app_id
|
120
|
+
if model_id is None:
|
121
|
+
model_id = self.current_ctx.model_id
|
122
|
+
if version_id is None:
|
123
|
+
return "%s/v2/ext/mcp/v1/users/%s/apps/%s/models/%s" % (
|
124
|
+
self.base,
|
125
|
+
user_id,
|
126
|
+
app_id,
|
127
|
+
model_id,
|
128
|
+
)
|
129
|
+
return "%s/v2/ext/mcp/v1/users/%s/apps/%s/models/%s/versions/%s" % (
|
130
|
+
self.base,
|
131
|
+
user_id,
|
132
|
+
app_id,
|
133
|
+
model_id,
|
134
|
+
version_id,
|
135
|
+
)
|
136
|
+
|
137
|
+
def openai_api_url(self):
|
138
|
+
"""We have a special endpoint for openAI compatible models.
|
139
|
+
|
140
|
+
This doesn't include the /chat/completions suffix which the openAI client automatically
|
141
|
+
adds.
|
142
|
+
|
143
|
+
It also doesn't incldue the model which you an set as the model arg in an openAI client call
|
144
|
+
using the clarifai_url() method below.
|
145
|
+
|
146
|
+
Example:
|
147
|
+
https://api.clarifai.com/v2/ext/openai/v1
|
148
|
+
"""
|
149
|
+
return "%s/v2/ext/openai/v1" % self.base
|
150
|
+
|
151
|
+
def api_url(
|
152
|
+
self,
|
153
|
+
user_id: str = None,
|
154
|
+
app_id: str = None,
|
155
|
+
resource_type: str = None,
|
156
|
+
resource_id: str = None,
|
157
|
+
version_id: str = None,
|
158
|
+
):
|
68
159
|
"""This is the path to the resource in the API.
|
69
160
|
|
70
161
|
Example:
|
@@ -81,6 +172,12 @@ class ClarifaiUrlHelper(object):
|
|
81
172
|
resource_type: the type of resource. One of "modules", "models", "concepts", "inputs", "workflows", "tasks"
|
82
173
|
resource_id: the resource ID
|
83
174
|
"""
|
175
|
+
if user_id is None:
|
176
|
+
user_id = self.current_ctx.user_id
|
177
|
+
if app_id is None:
|
178
|
+
app_id = self.current_ctx.app_id
|
179
|
+
if resource_id is None:
|
180
|
+
raise ValueError("resource_id must be provided to get the API URL.")
|
84
181
|
self._validate_resource_type(resource_type)
|
85
182
|
if version_id is None:
|
86
183
|
return "%s/v2/users/%s/apps/%s/%s/%s" % (
|
@@ -117,7 +214,14 @@ class ClarifaiUrlHelper(object):
|
|
117
214
|
% resource_type
|
118
215
|
)
|
119
216
|
|
120
|
-
def clarifai_url(
|
217
|
+
def clarifai_url(
|
218
|
+
self,
|
219
|
+
user_id: str = None,
|
220
|
+
app_id: str = None,
|
221
|
+
resource_type: str = None,
|
222
|
+
resource_id: str = None,
|
223
|
+
version_id: str = None,
|
224
|
+
):
|
121
225
|
"""This is the path to the resource in community UI.
|
122
226
|
|
123
227
|
Example:
|
@@ -135,6 +239,12 @@ class ClarifaiUrlHelper(object):
|
|
135
239
|
resource_id: the resource ID
|
136
240
|
version_id: the version of the resource.
|
137
241
|
"""
|
242
|
+
if user_id is None:
|
243
|
+
user_id = self.current_ctx.user_id
|
244
|
+
if app_id is None:
|
245
|
+
app_id = self.current_ctx.app_id
|
246
|
+
if resource_id is None:
|
247
|
+
raise ValueError("resource_id must be provided to get the API URL.")
|
138
248
|
self._validate_resource_type(resource_type)
|
139
249
|
if version_id is None:
|
140
250
|
return "%s/%s/%s/%s/%s" % (self.ui, user_id, app_id, resource_type, resource_id)
|
@@ -148,7 +258,7 @@ class ClarifaiUrlHelper(object):
|
|
148
258
|
)
|
149
259
|
|
150
260
|
@classmethod
|
151
|
-
def split_clarifai_app_url(cls, url):
|
261
|
+
def split_clarifai_app_url(cls, url: str):
|
152
262
|
"""
|
153
263
|
clarifai.com uses fully qualified urls to resources.
|
154
264
|
They are in the format of:
|
@@ -166,7 +276,7 @@ class ClarifaiUrlHelper(object):
|
|
166
276
|
return tuple(parts[1:])
|
167
277
|
|
168
278
|
@classmethod
|
169
|
-
def split_clarifai_url(cls, url):
|
279
|
+
def split_clarifai_url(cls, url: str):
|
170
280
|
"""
|
171
281
|
clarifai.com uses fully qualified urls to resources.
|
172
282
|
They are in the format of:
|
@@ -191,7 +301,7 @@ class ClarifaiUrlHelper(object):
|
|
191
301
|
return user_id, app_id, resource_type, resource_id, resource_version_id
|
192
302
|
|
193
303
|
@classmethod
|
194
|
-
def split_module_ui_url(cls, install):
|
304
|
+
def split_module_ui_url(cls, install: str):
|
195
305
|
"""Takes in a path like https://clarifai.com/zeiler/app/modules/module1/versions/2 to split it apart into it's IDs.
|
196
306
|
|
197
307
|
Returns:
|
clarifai/utils/config.py
CHANGED
@@ -4,7 +4,8 @@ from dataclasses import dataclass, field
|
|
4
4
|
|
5
5
|
import yaml
|
6
6
|
|
7
|
-
from clarifai.utils.constants import DEFAULT_CONFIG
|
7
|
+
from clarifai.utils.constants import DEFAULT_BASE, DEFAULT_CONFIG, DEFAULT_UI
|
8
|
+
from clarifai.utils.logging import logger
|
8
9
|
|
9
10
|
|
10
11
|
class Context(OrderedDict):
|
@@ -23,6 +24,12 @@ class Context(OrderedDict):
|
|
23
24
|
self['env'] = kwargs
|
24
25
|
|
25
26
|
def __getattr__(self, key):
|
27
|
+
"""Get the key from the config. You can pass a lowercase key like "pat" and it will check if
|
28
|
+
the environment variable CLARIFAI_PAT set and use that first. If no env var, then it checks
|
29
|
+
if that env var name is in the config and use that. If not then checks if
|
30
|
+
"pat" is in the config, if not then it falls back to CLARIFAI_PAT in the environment
|
31
|
+
variables, else raises an AttributeError.
|
32
|
+
"""
|
26
33
|
try:
|
27
34
|
if key == 'name':
|
28
35
|
return self[key]
|
@@ -32,7 +39,9 @@ class Context(OrderedDict):
|
|
32
39
|
# Allow accessing CLARIFAI_PAT type env var names from config as .pat
|
33
40
|
envvar_name = 'CLARIFAI_' + key.upper()
|
34
41
|
env = self['env']
|
35
|
-
if envvar_name in
|
42
|
+
if envvar_name in os.environ: # environment variable take precedence.
|
43
|
+
value = os.environ[envvar_name]
|
44
|
+
elif envvar_name in env:
|
36
45
|
value = env[envvar_name]
|
37
46
|
if value == "ENVVAR":
|
38
47
|
if envvar_name not in os.environ:
|
@@ -40,8 +49,17 @@ class Context(OrderedDict):
|
|
40
49
|
f"Environment variable '{envvar_name}' not set. Attempting to load it for config '{self['name']}'. Please set it in your terminal."
|
41
50
|
)
|
42
51
|
return os.environ[envvar_name]
|
43
|
-
|
52
|
+
elif key in env: # check if key is in the config
|
44
53
|
value = env[key]
|
54
|
+
# below are some default fallback values for UI and API base.
|
55
|
+
elif envvar_name == 'CLARIFAI_UI':
|
56
|
+
value = DEFAULT_UI
|
57
|
+
elif envvar_name == 'CLARIFAI_API_BASE':
|
58
|
+
value = DEFAULT_BASE
|
59
|
+
else:
|
60
|
+
raise AttributeError(
|
61
|
+
f"'{type(self).__name__}' object has no attribute '{key}' or '{envvar_name}' and '{envvar_name}' is also not in os.environ:"
|
62
|
+
)
|
45
63
|
|
46
64
|
if isinstance(value, dict):
|
47
65
|
return Context(value)
|
@@ -136,8 +154,17 @@ class Config:
|
|
136
154
|
|
137
155
|
@classmethod
|
138
156
|
def from_yaml(cls, filename: str = DEFAULT_CONFIG):
|
139
|
-
|
140
|
-
|
157
|
+
"""Loads the configuration from a YAML file.
|
158
|
+
If the file does not exist, it initializes with empty config.
|
159
|
+
"""
|
160
|
+
cfg = {"current_context": "_empty_", "contexts": {"_empty_": {}}}
|
161
|
+
if os.path.exists(filename):
|
162
|
+
with open(filename, 'r') as f:
|
163
|
+
cfg = yaml.safe_load(f)
|
164
|
+
else:
|
165
|
+
logger.warning(
|
166
|
+
f"Config file {filename} not found, using default config. Run 'clarifai config' on the command line to create a config file."
|
167
|
+
)
|
141
168
|
return cls(**cfg, filename=filename)
|
142
169
|
|
143
170
|
def to_dict(self):
|
@@ -160,5 +187,10 @@ class Config:
|
|
160
187
|
|
161
188
|
@property
|
162
189
|
def current(self) -> Context:
|
163
|
-
"""
|
190
|
+
"""Get the current Context or an empty one if your config is not setup."""
|
191
|
+
if not self.current_context:
|
192
|
+
logger.warning(
|
193
|
+
"No current context set, returning empty context. Run 'clarifai config' on the command line to create a config file."
|
194
|
+
)
|
195
|
+
return Context("_empty_")
|
164
196
|
return self.contexts[self.current_context]
|
clarifai/utils/constants.py
CHANGED
@@ -3,6 +3,8 @@ import os
|
|
3
3
|
DEFAULT_UI = os.environ.get("CLARIFAI_UI", "https://clarifai.com")
|
4
4
|
DEFAULT_BASE = os.environ.get("CLARIFAI_API_BASE", "https://api.clarifai.com")
|
5
5
|
|
6
|
+
MCP_TRANSPORT_NAME = "mcp_transport"
|
7
|
+
OPENAI_TRANSPORT_NAME = "openai_transport"
|
6
8
|
|
7
9
|
CLARIFAI_PAT_ENV_VAR = "CLARIFAI_PAT"
|
8
10
|
CLARIFAI_SESSION_TOKEN_ENV_VAR = "CLARIFAI_SESSION_TOKEN"
|
@@ -1,14 +1,14 @@
|
|
1
|
-
clarifai/__init__.py,sha256=
|
1
|
+
clarifai/__init__.py,sha256=Bf0R_NXXlv8PTvDDZ0L1u2z4ioD_oDpofFKAfM0SnDw,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
|
5
5
|
clarifai/cli/README.md,sha256=YGApHfeUyu5P0Pdth-mqQCQftWHDxz6bugDlvDXDhOE,1942
|
6
6
|
clarifai/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
clarifai/cli/__main__.py,sha256=7nPbLW7Jr2shkgMPvnxpn4xYGMvIcnqluJ69t9w4H_k,74
|
8
|
-
clarifai/cli/base.py,sha256=
|
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=R9Bl32ELs4w658dDLeugjwf5d_3SMJiv64ciEqoWaTQ,27291
|
12
12
|
clarifai/cli/nodepool.py,sha256=H6OIdUW_EiyDUwZogzEDoYmVwEjLMsgoDlPyE7gjIuU,4245
|
13
13
|
clarifai/client/__init__.py,sha256=NhpNFRJY6mTi8ca-5hUeTEmYeDKHDNXY48FN63pDuos,703
|
14
14
|
clarifai/client/app.py,sha256=D0FG9v07g1dExLnQsYt0OQjsJCkVvuw76BOpcqaCzfM,41380
|
@@ -18,13 +18,13 @@ clarifai/client/dataset.py,sha256=OgdpZkQ_vYmRxL8-qphcNozpvPV1bWTlte9Jv6UkKb8,35
|
|
18
18
|
clarifai/client/deployment.py,sha256=QBf0tzkKBEpzNgmOEmWUJMOlVWdFEFc70Y44o8y75Gs,2875
|
19
19
|
clarifai/client/input.py,sha256=jpX47qwn7aUBBIEuSSLHF5jk70XaWEh0prD065c9b-E,51205
|
20
20
|
clarifai/client/lister.py,sha256=1YEm2suNxPaJO4x9V5szgD_YX6N_00vgSO-7m0HagY8,2208
|
21
|
-
clarifai/client/model.py,sha256=
|
21
|
+
clarifai/client/model.py,sha256=cITBJ7J2C2FnVFtHlahcKP4ALYR45-q2y7RPjq2I2cY,85689
|
22
22
|
clarifai/client/model_client.py,sha256=XOrrm1xOZt62eLX5KbvV-zKSpK4EJOPUTUrGnH8z_KE,22392
|
23
23
|
clarifai/client/module.py,sha256=jLViQYvVV3FmRN_ivvbk83uwsx7CgYGeEx4dYAr6yD4,4537
|
24
24
|
clarifai/client/nodepool.py,sha256=Y5zQ0JLdTjAp2TmVnx7AAOwaB2YUslk3nl7s6BQ90FQ,16415
|
25
25
|
clarifai/client/runner.py,sha256=5xCiqByGGscfNm0IjHelhDTx8-9l8G0C3HL-3YZogK8,2253
|
26
26
|
clarifai/client/search.py,sha256=3LLfATrdU43a0mRNITmJV-E53bhfafZkYsbwkTtlnyU,15661
|
27
|
-
clarifai/client/user.py,sha256=
|
27
|
+
clarifai/client/user.py,sha256=_G_x3P6IJqtRqI2r0HDAhSfQJmqWgEfwEVmRTOJISbw,19052
|
28
28
|
clarifai/client/workflow.py,sha256=Bqh8lAmJcSbviJebckchTxucYlU11UQEhFSov7elpsk,13417
|
29
29
|
clarifai/client/auth/__init__.py,sha256=7EwR0NrozkAUwpUnCsqXvE_p0wqx_SelXlSpKShKJK0,136
|
30
30
|
clarifai/client/auth/helper.py,sha256=kHBi8GTX19EUiD9n_QgCqilv127TDIpP0_o5MMtzFdY,15167
|
@@ -67,18 +67,18 @@ clarifai/runners/__init__.py,sha256=CQhpUOj_x-oV9xEUKdL-hi3A1BQAtPUv-FFOev4a96w,
|
|
67
67
|
clarifai/runners/server.py,sha256=9qVAs8pRHmtyY0RCNIQ1uP8nqDADIFZ03LnkoDt1h4U,4692
|
68
68
|
clarifai/runners/dockerfile_template/Dockerfile.template,sha256=5cjv7U8PmWa3DB_5B1CqSYh_6GE0E0np52TIAa7EIDE,2312
|
69
69
|
clarifai/runners/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
|
-
clarifai/runners/models/dummy_openai_model.py,sha256=
|
70
|
+
clarifai/runners/models/dummy_openai_model.py,sha256=5oa7r8j1GpymenpmMfaAV9Vt6VObKvrT9tDXgFmP4qY,7208
|
71
71
|
clarifai/runners/models/mcp_class.py,sha256=7uwCMade0LMMBq7vczhPf4Kxdmh8Rj0R7Pg3pPxYdjQ,6386
|
72
72
|
clarifai/runners/models/model_builder.py,sha256=PiqPyTGPSKsYvOQNpBzs4e1_wuEbtE-P3yEkLE4Py10,49231
|
73
|
-
clarifai/runners/models/model_class.py,sha256
|
73
|
+
clarifai/runners/models/model_class.py,sha256=-euUF-eHUi4KXR_e1pIwvToDZ13CM6TSz2FolzildjM,16069
|
74
74
|
clarifai/runners/models/model_run_locally.py,sha256=6-6WjEKc0ba3gAv4wOLdMs2XOzS3b-2bZHJS0wdVqJY,20088
|
75
75
|
clarifai/runners/models/model_runner.py,sha256=SccX-RxTgruSpQaM21uMSl-z1x6fOa13fQZMQW8NNRY,7297
|
76
76
|
clarifai/runners/models/model_servicer.py,sha256=rRd_fNEXwqiBSzTUtPI2r07EBdcCPd8tcSPHeqTe0_I,3445
|
77
|
-
clarifai/runners/models/openai_class.py,sha256=
|
77
|
+
clarifai/runners/models/openai_class.py,sha256=3u6K7vTdYybxdTT3t3pYh9wvQyWhkL595t7hR5IkljU,8159
|
78
78
|
clarifai/runners/models/visual_classifier_class.py,sha256=f9ZP8KFamMUdMpUG3AlL9nVCdcggy_E5n9RJY3ixR1U,2739
|
79
79
|
clarifai/runners/models/visual_detector_class.py,sha256=ky4oFAkGCKPpGPdgaOso-n6D3HcmnbKee_8hBsNiV8U,2883
|
80
80
|
clarifai/runners/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
|
-
clarifai/runners/utils/code_script.py,sha256=
|
81
|
+
clarifai/runners/utils/code_script.py,sha256=fiuwsY-uxmMzQXBv1V8Gpw0n6s_xzrigAk4J5htOAao,12646
|
82
82
|
clarifai/runners/utils/const.py,sha256=Q4Ps6gIEJCyTdQCfmT6PaS61WHmhT25XigV1NugWz-E,1544
|
83
83
|
clarifai/runners/utils/data_utils.py,sha256=4M4n-cGprBEBV5UkgOWaUlVfZ3WBTmegdffGQ3SfYCU,20750
|
84
84
|
clarifai/runners/utils/loader.py,sha256=K5Y8MPbIe5STw2gDnrL8KqFgKNxEo7bz-RV0ip1T4PM,10900
|
@@ -89,11 +89,11 @@ clarifai/runners/utils/url_fetcher.py,sha256=Segkvi-ktPa3-koOpUu8DNZeWOaK6G82Ya9
|
|
89
89
|
clarifai/runners/utils/data_types/__init__.py,sha256=iBtmPkIoLK9ew6ZiXElGt2YBBTDLEA0fmxE_eOYzvhk,478
|
90
90
|
clarifai/runners/utils/data_types/data_types.py,sha256=UBHTNPshr94qUs2KqkYis0VlArm23rcSG_EyO_Uetgc,19823
|
91
91
|
clarifai/schema/search.py,sha256=o9-ct8ulLZByB3RCVwZWPgaDwdcW7cM5s-g8oyAz89s,2421
|
92
|
-
clarifai/urls/helper.py,sha256=
|
92
|
+
clarifai/urls/helper.py,sha256=z6LnLGgLHxD8scFtyRdxqYIRJNhxqPkfLe53UtTLUBY,11727
|
93
93
|
clarifai/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
94
94
|
clarifai/utils/cli.py,sha256=7lHajIsWzyEU7jfgH1nykwYG63wcHCZ3ep7a6amWZH4,5413
|
95
|
-
clarifai/utils/config.py,sha256=
|
96
|
-
clarifai/utils/constants.py,sha256=
|
95
|
+
clarifai/utils/config.py,sha256=jMSWYxJfp_D8eoGqz-HTdsngn5bg_1ymjLidYz6rdLA,7073
|
96
|
+
clarifai/utils/constants.py,sha256=nvKNAOnfvHza03mYHVLfyOfm_AcFiNKr2CzwyEQgoQI,2110
|
97
97
|
clarifai/utils/logging.py,sha256=0we53uTqUvzrulC86whu-oeWNxn1JjJL0OQ98Bwf9vo,15198
|
98
98
|
clarifai/utils/misc.py,sha256=x7JP8oxU672Z9yAav47Y1anFiL4RD8WvlKBHMVlbyZM,3137
|
99
99
|
clarifai/utils/model_train.py,sha256=0XSAoTkSsrwf4f-W9yw2mkXZtkal7LBLJSoi86CFCn4,9250
|
@@ -106,9 +106,9 @@ clarifai/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
106
106
|
clarifai/workflows/export.py,sha256=Oq3RVNKvv1iH46U6oIjXa-MXWJ4sTlXr_NSfwoxr3H4,2149
|
107
107
|
clarifai/workflows/utils.py,sha256=ESL3INcouNcLKCh-nMpfXX-YbtCzX7tz7hT57_RGQ3M,2079
|
108
108
|
clarifai/workflows/validate.py,sha256=UhmukyHkfxiMFrPPeBdUTiCOHQT5-shqivlBYEyKTlU,2931
|
109
|
-
clarifai-11.4.
|
110
|
-
clarifai-11.4.
|
111
|
-
clarifai-11.4.
|
112
|
-
clarifai-11.4.
|
113
|
-
clarifai-11.4.
|
114
|
-
clarifai-11.4.
|
109
|
+
clarifai-11.4.7.dist-info/licenses/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
|
110
|
+
clarifai-11.4.7.dist-info/METADATA,sha256=bnOYR9voVtpCEE1ig6qeV98EUThXwa1k6oomoY91Kt8,22398
|
111
|
+
clarifai-11.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
112
|
+
clarifai-11.4.7.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
|
113
|
+
clarifai-11.4.7.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
|
114
|
+
clarifai-11.4.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|