clarifai 11.4.2__py3-none-any.whl → 11.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
clarifai/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "11.4.2"
1
+ __version__ = "11.4.3"
clarifai/cli/base.py CHANGED
@@ -143,6 +143,13 @@ def dump(ctx_obj, output_format):
143
143
  json.dump(ctx_obj.to_dict(), sys.stdout, indent=2)
144
144
 
145
145
 
146
+ @config.command(['cat'])
147
+ @click.pass_obj
148
+ def env(ctx_obj):
149
+ """Print env vars. Use: eval "$(clarifai config env)" """
150
+ ctx_obj.current.print_env_vars()
151
+
152
+
146
153
  @cli.command()
147
154
  @click.argument('api_url', default="https://api.clarifai.com")
148
155
  @click.option('--user_id', required=False, help='User ID')
clarifai/cli/model.py CHANGED
@@ -504,6 +504,10 @@ def local_dev(ctx, model_path):
504
504
 
505
505
  logger.info(f"Current deployment_id: {deployment_id}")
506
506
 
507
+ logger.info(
508
+ f"Full url for the model: /users/{user_id}/apps/{app_id}/models/{model.id}/versions/{version.id}"
509
+ )
510
+
507
511
  # Now that we have all the context in ctx.obj, we need to update the config.yaml in
508
512
  # the model_path directory with the model object containing user_id, app_id, model_id, version_id
509
513
  config_file = os.path.join(model_path, 'config.yaml')
@@ -526,13 +530,9 @@ def local_dev(ctx, model_path):
526
530
  ModelBuilder._backup_config(config_file)
527
531
  ModelBuilder._save_config(config_file, config)
528
532
 
529
- # client_model = Model(
530
- # TODO: once we can generate_client_script from ModelBuilder or similar
531
- # we should be able to put the exact function call in place.
532
- # model_script = model.generate_client_script()
533
-
534
533
  builder = ModelBuilder(model_path, download_validation_only=True)
535
- method_signatures = builder.get_method_signatures()
534
+ # don't mock for local dev since you need the dependencies to run the code anyways.
535
+ method_signatures = builder.get_method_signatures(mocking=False)
536
536
 
537
537
  from clarifai.runners.utils import code_script
538
538
 
@@ -546,8 +546,6 @@ def local_dev(ctx, model_path):
546
546
  base_url=ctx.obj.current.api_base,
547
547
  )
548
548
 
549
- # TODO: put in the ClarifaiUrlHelper to create the model url.
550
-
551
549
  logger.info("""\n
552
550
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
553
551
  # About to start up the local dev runner in this terminal...
clarifai/client/app.py CHANGED
@@ -19,6 +19,7 @@ from clarifai.client.workflow import Workflow
19
19
  from clarifai.constants.model import TRAINABLE_MODEL_TYPES
20
20
  from clarifai.errors import UserError
21
21
  from clarifai.urls.helper import ClarifaiUrlHelper
22
+ from clarifai.utils.constants import DEFAULT_BASE
22
23
  from clarifai.utils.logging import display_concept_relations_tree, display_workflow_tree, logger
23
24
  from clarifai.utils.misc import concept_relations_accumulation
24
25
  from clarifai.workflows.utils import get_yaml_output_info_proto, is_same_yaml_model
@@ -33,7 +34,7 @@ class App(Lister, BaseClient):
33
34
  url: str = None,
34
35
  app_id: str = None,
35
36
  user_id: str = None,
36
- base_url: str = "https://api.clarifai.com",
37
+ base_url: str = DEFAULT_BASE,
37
38
  pat: str = None,
38
39
  token: str = None,
39
40
  root_certificates_path: str = None,
@@ -7,10 +7,12 @@ from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
7
7
  from clarifai_grpc.grpc.api import resources_pb2, service_pb2_grpc
8
8
 
9
9
  from clarifai import __version__
10
- from clarifai.utils.constants import CLARIFAI_PAT_ENV_VAR, CLARIFAI_SESSION_TOKEN_ENV_VAR
11
-
12
- DEFAULT_BASE = "https://api.clarifai.com"
13
- DEFAULT_UI = "https://clarifai.com"
10
+ from clarifai.utils.constants import (
11
+ CLARIFAI_PAT_ENV_VAR,
12
+ CLARIFAI_SESSION_TOKEN_ENV_VAR,
13
+ DEFAULT_BASE,
14
+ DEFAULT_UI,
15
+ )
14
16
 
15
17
  REQUEST_ID_PREFIX_HEADER = "x-clarifai-request-id-prefix"
16
18
  REQUEST_ID_PREFIX = f"sdk-python-{__version__}"
@@ -10,6 +10,7 @@ from clarifai.client.base import BaseClient
10
10
  from clarifai.client.lister import Lister
11
11
  from clarifai.client.nodepool import Nodepool
12
12
  from clarifai.errors import UserError
13
+ from clarifai.utils.constants import DEFAULT_BASE
13
14
  from clarifai.utils.logging import logger
14
15
 
15
16
 
@@ -20,7 +21,7 @@ class ComputeCluster(Lister, BaseClient):
20
21
  self,
21
22
  compute_cluster_id: str = None,
22
23
  user_id: str = None,
23
- base_url: str = "https://api.clarifai.com",
24
+ base_url: str = DEFAULT_BASE,
24
25
  pat: str = None,
25
26
  token: str = None,
26
27
  root_certificates_path: str = None,
@@ -45,6 +45,7 @@ ClarifaiDatasetType = TypeVar(
45
45
  VisualSegmentationDataset,
46
46
  TextClassificationDataset,
47
47
  )
48
+ from clarifai.utils.constants import DEFAULT_BASE
48
49
 
49
50
 
50
51
  class Dataset(Lister, BaseClient):
@@ -55,7 +56,7 @@ class Dataset(Lister, BaseClient):
55
56
  url: str = None,
56
57
  dataset_id: str = None,
57
58
  dataset_version_id: str = None,
58
- base_url: str = "https://api.clarifai.com",
59
+ base_url: str = DEFAULT_BASE,
59
60
  pat: str = None,
60
61
  token: str = None,
61
62
  root_certificates_path: str = None,
@@ -2,6 +2,7 @@ from clarifai_grpc.grpc.api import resources_pb2
2
2
 
3
3
  from clarifai.client.base import BaseClient
4
4
  from clarifai.client.lister import Lister
5
+ from clarifai.utils.constants import DEFAULT_BASE
5
6
  from clarifai.utils.logging import logger
6
7
  from clarifai.utils.protobuf import dict_to_protobuf
7
8
 
@@ -13,7 +14,7 @@ class Deployment(Lister, BaseClient):
13
14
  self,
14
15
  deployment_id: str = None,
15
16
  user_id: str = None,
16
- base_url: str = "https://api.clarifai.com",
17
+ base_url: str = DEFAULT_BASE,
17
18
  pat: str = None,
18
19
  token: str = None,
19
20
  root_certificates_path: str = None,
clarifai/client/input.py CHANGED
@@ -21,6 +21,7 @@ from clarifai.client.lister import Lister
21
21
  from clarifai.constants.dataset import MAX_RETRIES
22
22
  from clarifai.constants.input import MAX_UPLOAD_BATCH_SIZE
23
23
  from clarifai.errors import UserError
24
+ from clarifai.utils.constants import DEFAULT_BASE
24
25
  from clarifai.utils.logging import logger
25
26
  from clarifai.utils.misc import BackoffIterator, Chunker, clean_input_id
26
27
 
@@ -33,7 +34,7 @@ class Inputs(Lister, BaseClient):
33
34
  user_id: str = None,
34
35
  app_id: str = None,
35
36
  logger_level: str = "INFO",
36
- base_url: str = "https://api.clarifai.com",
37
+ base_url: str = DEFAULT_BASE,
37
38
  pat: str = None,
38
39
  token: str = None,
39
40
  root_certificates_path: str = None,
clarifai/client/model.py CHANGED
@@ -34,6 +34,7 @@ from clarifai.constants.model import (
34
34
  )
35
35
  from clarifai.errors import UserError
36
36
  from clarifai.urls.helper import ClarifaiUrlHelper
37
+ from clarifai.utils.constants import DEFAULT_BASE
37
38
  from clarifai.utils.logging import logger
38
39
  from clarifai.utils.misc import BackoffIterator
39
40
  from clarifai.utils.model_train import (
@@ -58,7 +59,7 @@ class Model(Lister, BaseClient):
58
59
  url: str = None,
59
60
  model_id: str = None,
60
61
  model_version: Dict = {'id': ""},
61
- base_url: str = "https://api.clarifai.com",
62
+ base_url: str = DEFAULT_BASE,
62
63
  pat: str = None,
63
64
  token: str = None,
64
65
  root_certificates_path: str = None,
@@ -8,7 +8,6 @@ from clarifai_grpc.grpc.api.status import status_code_pb2
8
8
  from clarifai.constants.model import MAX_MODEL_PREDICT_INPUTS
9
9
  from clarifai.errors import UserError
10
10
  from clarifai.runners.utils import code_script, method_signatures
11
- from clarifai.runners.utils.data_utils import is_openai_chat_format
12
11
  from clarifai.runners.utils.method_signatures import (
13
12
  CompatibilitySerializer,
14
13
  deserialize,
@@ -16,6 +15,7 @@ from clarifai.runners.utils.method_signatures import (
16
15
  serialize,
17
16
  signatures_from_json,
18
17
  )
18
+ from clarifai.runners.utils.openai_convertor import is_openai_chat_format
19
19
  from clarifai.utils.logging import logger
20
20
  from clarifai.utils.misc import BackoffIterator, status_is_retryable
21
21
 
clarifai/client/module.py CHANGED
@@ -6,6 +6,7 @@ from clarifai.client.base import BaseClient
6
6
  from clarifai.client.lister import Lister
7
7
  from clarifai.errors import UserError
8
8
  from clarifai.urls.helper import ClarifaiUrlHelper
9
+ from clarifai.utils.constants import DEFAULT_BASE
9
10
  from clarifai.utils.logging import logger
10
11
 
11
12
 
@@ -17,7 +18,7 @@ class Module(Lister, BaseClient):
17
18
  url: str = None,
18
19
  module_id: str = None,
19
20
  module_version: Dict = {'id': ""},
20
- base_url: str = "https://api.clarifai.com",
21
+ base_url: str = DEFAULT_BASE,
21
22
  pat: str = None,
22
23
  token: str = None,
23
24
  root_certificates_path: str = None,
@@ -11,6 +11,7 @@ from clarifai.client.deployment import Deployment
11
11
  from clarifai.client.lister import Lister
12
12
  from clarifai.client.runner import Runner
13
13
  from clarifai.errors import UserError
14
+ from clarifai.utils.constants import DEFAULT_BASE
14
15
  from clarifai.utils.logging import logger
15
16
 
16
17
 
@@ -21,7 +22,7 @@ class Nodepool(Lister, BaseClient):
21
22
  self,
22
23
  nodepool_id: str = None,
23
24
  user_id: str = None,
24
- base_url: str = "https://api.clarifai.com",
25
+ base_url: str = DEFAULT_BASE,
25
26
  pat: str = None,
26
27
  token: str = None,
27
28
  root_certificates_path: str = None,
clarifai/client/runner.py CHANGED
@@ -2,6 +2,7 @@ from clarifai_grpc.grpc.api import resources_pb2
2
2
 
3
3
  from clarifai.client.base import BaseClient
4
4
  from clarifai.client.lister import Lister
5
+ from clarifai.utils.constants import DEFAULT_BASE
5
6
  from clarifai.utils.logging import logger
6
7
  from clarifai.utils.protobuf import dict_to_protobuf
7
8
 
@@ -13,7 +14,7 @@ class Runner(Lister, BaseClient):
13
14
  self,
14
15
  runner_id: str = None,
15
16
  user_id: str = None,
16
- base_url: str = "https://api.clarifai.com",
17
+ base_url: str = DEFAULT_BASE,
17
18
  pat: str = None,
18
19
  token: str = None,
19
20
  root_certificates_path: str = None,
clarifai/client/search.py CHANGED
@@ -17,6 +17,7 @@ from clarifai.constants.search import (
17
17
  )
18
18
  from clarifai.errors import UserError
19
19
  from clarifai.schema.search import get_schema
20
+ from clarifai.utils.constants import DEFAULT_BASE
20
21
 
21
22
 
22
23
  class Search(Lister, BaseClient):
@@ -28,7 +29,7 @@ class Search(Lister, BaseClient):
28
29
  metric: str = DEFAULT_SEARCH_METRIC,
29
30
  algorithm: str = DEFAULT_SEARCH_ALGORITHM,
30
31
  pagination: bool = False,
31
- base_url: str = "https://api.clarifai.com",
32
+ base_url: str = DEFAULT_BASE,
32
33
  pat: str = None,
33
34
  token: str = None,
34
35
  root_certificates_path: str = None,
clarifai/client/user.py CHANGED
@@ -12,6 +12,7 @@ from clarifai.client.base import BaseClient
12
12
  from clarifai.client.compute_cluster import ComputeCluster
13
13
  from clarifai.client.lister import Lister
14
14
  from clarifai.errors import UserError
15
+ from clarifai.utils.constants import DEFAULT_BASE
15
16
  from clarifai.utils.logging import logger
16
17
 
17
18
 
@@ -21,7 +22,7 @@ class User(Lister, BaseClient):
21
22
  def __init__(
22
23
  self,
23
24
  user_id: str = None,
24
- base_url: str = "https://api.clarifai.com",
25
+ base_url: str = DEFAULT_BASE,
25
26
  pat: str = None,
26
27
  token: str = None,
27
28
  root_certificates_path: str = None,
@@ -14,6 +14,7 @@ from clarifai.client.model import Model
14
14
  from clarifai.constants.workflow import MAX_WORKFLOW_PREDICT_INPUTS
15
15
  from clarifai.errors import UserError
16
16
  from clarifai.urls.helper import ClarifaiUrlHelper
17
+ from clarifai.utils.constants import DEFAULT_BASE
17
18
  from clarifai.utils.logging import logger
18
19
  from clarifai.utils.misc import BackoffIterator, status_is_retryable
19
20
  from clarifai.workflows.export import Exporter
@@ -28,7 +29,7 @@ class Workflow(Lister, BaseClient):
28
29
  workflow_id: str = None,
29
30
  workflow_version: Dict = {'id': ""},
30
31
  output_config: Dict = {'min_value': 0},
31
- base_url: str = "https://api.clarifai.com",
32
+ base_url: str = DEFAULT_BASE,
32
33
  pat: str = None,
33
34
  token: str = None,
34
35
  root_certificates_path: str = None,
@@ -0,0 +1,114 @@
1
+ """Base class for creating Model Context Protocol (MCP) servers."""
2
+
3
+ import asyncio
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
10
+
11
+ from clarifai.runners.models.model_class import ModelClass
12
+
13
+
14
+ class MCPModelClass(ModelClass):
15
+ """Base class for wrapping FastMCP servers as a model running in Clarfai. This handles
16
+ all the transport between the API and the MCP server here. Simply subclass this and implement
17
+ the get_server() method to return the FastMCP server instance. The server is then used to
18
+ handle all the requests and responses.
19
+ """
20
+
21
+ def load_model(self):
22
+ # in memory transport provided in fastmcp v2 so we can easily use the client functions.
23
+ self.client = Client(self.get_server())
24
+
25
+ def get_server(self) -> FastMCP:
26
+ """Required method for each subclass to implement to return the FastMCP server to use."""
27
+ raise NotImplementedError("Subclasses must implement get_server() method")
28
+
29
+ @ModelClass.method
30
+ def mcp_transport(self, msg: str) -> str:
31
+ """The single model method to get the jsonrpc message and send it to the FastMCP server then
32
+ return it's response.
33
+
34
+ """
35
+
36
+ async def send_notification(client_message: types.ClientNotification) -> None:
37
+ async with self.client:
38
+ # Strip the jsonrpc field since send_notification will also pass it in for some reason.
39
+ client_message = types.ClientNotification.model_validate(
40
+ client_message.model_dump(
41
+ by_alias=True, mode="json", exclude_none=True, exclude={"jsonrpc"}
42
+ )
43
+ )
44
+ try:
45
+ return await self.client.session.send_notification(client_message)
46
+ except McpError as e:
47
+ return types.JSONRPCError(jsonrpc="2.0", error=e.error)
48
+
49
+ async def send_request(client_message: types.ClientRequest, id: str) -> Any:
50
+ async with self.client:
51
+ # Strip the jsonrpc and id fields as send_request sets them again too.
52
+ client_message = types.ClientRequest.model_validate(
53
+ client_message.model_dump(
54
+ by_alias=True, mode="json", exclude_none=True, exclude={"jsonrpc", "id"}
55
+ )
56
+ )
57
+
58
+ result_type = None
59
+ if isinstance(client_message.root, types.PingRequest):
60
+ result_type = types.EmptyResult
61
+ elif isinstance(client_message.root, types.InitializeRequest):
62
+ return await self.client.session.initialize()
63
+ elif isinstance(client_message.root, types.SetLevelRequest):
64
+ result_type = types.EmptyResult
65
+ elif isinstance(client_message.root, types.ListResourcesRequest):
66
+ result_type = types.ListResourcesResult
67
+ elif isinstance(client_message.root, types.ListResourceTemplatesRequest):
68
+ result_type = types.ListResourceTemplatesResult
69
+ elif isinstance(client_message.root, types.ReadResourceRequest):
70
+ result_type = types.ReadResourceResult
71
+ elif isinstance(client_message.root, types.SubscribeRequest):
72
+ result_type = types.EmptyResult
73
+ elif isinstance(client_message.root, types.UnsubscribeRequest):
74
+ result_type = types.EmptyResult
75
+ elif isinstance(client_message.root, types.ListPromptsRequest):
76
+ result_type = types.ListPromptsResult
77
+ elif isinstance(client_message.root, types.GetPromptRequest):
78
+ result_type = types.GetPromptResult
79
+ elif isinstance(client_message.root, types.CompleteRequest):
80
+ result_type = types.CompleteResult
81
+ elif isinstance(client_message.root, types.ListToolsRequest):
82
+ result_type = types.ListToolsResult
83
+ elif isinstance(client_message.root, types.CallToolRequest):
84
+ result_type = types.CallToolResult
85
+ else:
86
+ # this is a special case where we need to return the list of tools.
87
+ raise NotImplementedError(f"Method {client_message.method} not implemented")
88
+ # Call the mcp server using send_request() or send_notification() depending on the method.
89
+ try:
90
+ return await self.client.session.send_request(client_message, result_type)
91
+ except McpError as e:
92
+ return types.JSONRPCError(jsonrpc="2.0", id=id, error=e.error)
93
+
94
+ # The message coming here is the generic request. We look at it's .method
95
+ # to determine which client function to call and to further subparse the params.
96
+ # Note(zeiler): unfortunately the pydantic types in mcp/types.py are not consistent.
97
+ # The JSONRPCRequest are supposed to have an id but the InitializeRequest
98
+ # does not have it.
99
+ d = json.loads(msg)
100
+ id = d.get('id', "")
101
+
102
+ # If we have an id it's a JSONRPCRequest
103
+ if not d.get('method', '').startswith("notifications/"):
104
+ client_message = types.ClientRequest.model_validate(d)
105
+ response = asyncio.run(send_request(client_message, id=id))
106
+ else: # JSONRPCRequest
107
+ client_message = types.ClientNotification.model_validate(d)
108
+ response = asyncio.run(send_notification(client_message))
109
+ if response is None:
110
+ response = types.JSONRPCError(
111
+ jsonrpc="2.0", id=id, error="Got empty response from MCP server."
112
+ )
113
+ # return as a serialized json string
114
+ return response.model_dump_json(by_alias=True, exclude_none=True)
@@ -410,11 +410,17 @@ class ModelBuilder:
410
410
  signatures = {method.name: method.signature for method in method_info.values()}
411
411
  return signatures_to_yaml(signatures)
412
412
 
413
- def get_method_signatures(self):
413
+ def get_method_signatures(self, mocking=True):
414
414
  """
415
415
  Returns the method signatures for the model class.
416
+
417
+ Args:
418
+ mocking (bool): Whether to mock the model class or not. Defaults to False.
419
+
420
+ Returns:
421
+ list: A list of method signatures for the model class.
416
422
  """
417
- model_class = self.load_model_class(mocking=True)
423
+ model_class = self.load_model_class(mocking=mocking)
418
424
  method_info = model_class._get_method_info()
419
425
  signatures = [method.signature for method in method_info.values()]
420
426
  return signatures
@@ -436,22 +442,42 @@ class ModelBuilder:
436
442
  return self._client
437
443
 
438
444
  @property
439
- def model_url(self):
445
+ def model_ui_url(self):
446
+ url_helper = ClarifaiUrlHelper(self._client.auth_helper)
447
+ # Note(zeiler): the UI experience isn't the best when including version id right now.
448
+ # if self.model_version_id is not None:
449
+ # return url_helper.clarifai_url(
450
+ # self.client.user_app_id.user_id,
451
+ # self.client.user_app_id.app_id,
452
+ # "models",
453
+ # self.model_id,
454
+ # self.model_version_id,
455
+ # )
456
+ # else:
457
+ return url_helper.clarifai_url(
458
+ self.client.user_app_id.user_id,
459
+ self.client.user_app_id.app_id,
460
+ "models",
461
+ self.model_id,
462
+ )
463
+
464
+ @property
465
+ def model_api_url(self):
440
466
  url_helper = ClarifaiUrlHelper(self._client.auth_helper)
441
467
  if self.model_version_id is not None:
442
- return url_helper.clarifai_url(
468
+ return url_helper.api_url(
443
469
  self.client.user_app_id.user_id,
444
470
  self.client.user_app_id.app_id,
445
471
  "models",
446
472
  self.model_id,
473
+ self.model_version_id,
447
474
  )
448
475
  else:
449
- return url_helper.clarifai_url(
476
+ return url_helper.api_url(
450
477
  self.client.user_app_id.user_id,
451
478
  self.client.user_app_id.app_id,
452
479
  "models",
453
480
  self.model_id,
454
- self.model_version_id,
455
481
  )
456
482
 
457
483
  def _get_model_proto(self):
@@ -930,10 +956,11 @@ class ModelBuilder:
930
956
  return
931
957
  self.model_version_id = response.model_version_id
932
958
  logger.info(f"Created Model Version ID: {self.model_version_id}")
933
- logger.info(f"Full url to that version is: {self.model_url}")
959
+ logger.info(f"Full url to that version is: {self.model_ui_url}")
934
960
  try:
935
961
  is_uploaded = self.monitor_model_build()
936
962
  if is_uploaded:
963
+ # python code to run the model.
937
964
  from clarifai.runners.utils import code_script
938
965
 
939
966
  method_signatures = self.get_method_signatures()
@@ -945,7 +972,7 @@ class ModelBuilder:
945
972
  )
946
973
  logger.info("""\n
947
974
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
948
- # Here is a code snippet to call this model:
975
+ # Here is a code snippet to use this model:
949
976
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
950
977
  """)
951
978
  logger.info(snippet)
@@ -1049,7 +1076,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1049
1076
  logger.info("Model build complete!")
1050
1077
  logger.info(f"Build time elapsed {time.time() - st:.1f}s)")
1051
1078
  logger.info(
1052
- f"Check out the model at {self.model_url} version: {self.model_version_id}"
1079
+ f"Check out the model at {self.model_ui_url} version: {self.model_version_id}"
1053
1080
  )
1054
1081
  return True
1055
1082
  else:
@@ -1074,10 +1101,12 @@ def upload_model(folder, stage, skip_dockerfile):
1074
1101
  exists = builder.check_model_exists()
1075
1102
  if exists:
1076
1103
  logger.info(
1077
- f"Model already exists at {builder.model_url}, this upload will create a new version for it."
1104
+ f"Model already exists at {builder.model_ui_url}, this upload will create a new version for it."
1078
1105
  )
1079
1106
  else:
1080
- logger.info(f"New model will be created at {builder.model_url} with it's first version.")
1107
+ logger.info(
1108
+ f"New model will be created at {builder.model_ui_url} with it's first version."
1109
+ )
1081
1110
 
1082
1111
  input("Press Enter to continue...")
1083
1112
  builder.upload_model_version()
@@ -4,6 +4,7 @@ from typing import List
4
4
  from clarifai_grpc.grpc.api import resources_pb2
5
5
 
6
6
  from clarifai.runners.utils import data_utils
7
+ from clarifai.urls.helper import ClarifaiUrlHelper
7
8
 
8
9
 
9
10
  def generate_client_script(
@@ -15,6 +16,38 @@ def generate_client_script(
15
16
  deployment_id: str = None,
16
17
  use_ctx: bool = False,
17
18
  ) -> str:
19
+ url_helper = ClarifaiUrlHelper()
20
+
21
+ # Provide an mcp client config
22
+ if len(method_signatures) == 1 and method_signatures[0].name == "mcp_transport":
23
+ api_url = url_helper.api_url(
24
+ user_id,
25
+ app_id,
26
+ "models",
27
+ model_id,
28
+ )
29
+
30
+ _CLIENT_TEMPLATE = """
31
+ import asyncio
32
+ import os
33
+ from fastmcp import Client
34
+ from fastmcp.client.transports import StreamableHttpTransport
35
+
36
+ transport = StreamableHttpTransport(url="%s/mcp",
37
+ headers={"Authorization": "Bearer " + os.environ["CLARIFAI_PAT"]})
38
+
39
+ async def main():
40
+ async with Client(transport) as client:
41
+ tools = await client.list_tools()
42
+ print(f"Available tools: {tools}")
43
+ result = await client.call_tool(tools[0].name, {"a": 5, "b": 3})
44
+ print(f"Result: {result[0].text}")
45
+
46
+ if __name__ == "__main__":
47
+ asyncio.run(main())
48
+ """
49
+ return _CLIENT_TEMPLATE % api_url
50
+
18
51
  _CLIENT_TEMPLATE = """\
19
52
  import os
20
53
 
@@ -35,8 +68,9 @@ from clarifai.runners.utils import data_types
35
68
  model_section = """
36
69
  model = Model.from_current_context()"""
37
70
  else:
71
+ model_ui_url = url_helper.clarifai_url(user_id, app_id, "models", model_id)
38
72
  model_section = f"""
39
- model = Model("https://clarifai.com/{user_id}/{app_id}/models/{model_id}",
73
+ model = Model({model_ui_url},
40
74
  deployment_id = {deployment_id}, # Only needed for dedicated deployed models
41
75
  {base_url_str}
42
76
  )
@@ -395,6 +395,22 @@ class Image(MessageData):
395
395
  raise ValueError("Image has no bytes")
396
396
  return PILImage.open(io.BytesIO(self.proto.base64))
397
397
 
398
+ def to_base64_str(self) -> str:
399
+ if not self.proto.base64:
400
+ raise ValueError("Image has no bytes")
401
+ if isinstance(self.proto.base64, str):
402
+ return self.proto.base64
403
+ if isinstance(self.proto.base64, bytes):
404
+ try:
405
+ # trying direct decode (if already a base64 bytes)
406
+ return self.proto.base64.decode('utf-8')
407
+ except UnicodeDecodeError:
408
+ import base64
409
+
410
+ return base64.b64encode(self.proto.base64).decode('utf-8')
411
+ else:
412
+ raise TypeError("Expected str or bytes for Image.base64")
413
+
398
414
  def to_numpy(self) -> np.ndarray:
399
415
  return np.asarray(self.to_pil())
400
416
 
@@ -466,6 +482,22 @@ class Audio(MessageData):
466
482
  def to_proto(self) -> AudioProto:
467
483
  return self.proto
468
484
 
485
+ def to_base64_str(self) -> str:
486
+ if not self.proto.base64:
487
+ raise ValueError("Audio has no bytes")
488
+ if isinstance(self.proto.base64, str):
489
+ return self.proto.base64
490
+ if isinstance(self.proto.base64, bytes):
491
+ try:
492
+ # trying direct decode (if already a base64 bytes)
493
+ return self.proto.base64.decode('utf-8')
494
+ except UnicodeDecodeError:
495
+ import base64
496
+
497
+ return base64.b64encode(self.proto.base64).decode('utf-8')
498
+ else:
499
+ raise TypeError("Expected str or bytes for Audio.base64")
500
+
469
501
  @classmethod
470
502
  def from_proto(cls, proto: AudioProto) -> "Audio":
471
503
  return cls(proto)
@@ -578,6 +610,22 @@ class Video(MessageData):
578
610
  def to_proto(self) -> VideoProto:
579
611
  return self.proto
580
612
 
613
+ def to_base64_str(self) -> str:
614
+ if not self.proto.base64:
615
+ raise ValueError("Video has no bytes")
616
+ if isinstance(self.proto.base64, str):
617
+ return self.proto.base64
618
+ if isinstance(self.proto.base64, bytes):
619
+ try:
620
+ # trying direct decode (if already a base64 bytes)
621
+ return self.proto.base64.decode('utf-8')
622
+ except UnicodeDecodeError:
623
+ import base64
624
+
625
+ return base64.b64encode(self.proto.base64).decode('utf-8')
626
+ else:
627
+ raise TypeError("Expected str or bytes for Video.base64")
628
+
581
629
  @classmethod
582
630
  def from_proto(cls, proto: VideoProto) -> "Video":
583
631
  return cls(proto)
@@ -1,63 +1,89 @@
1
+ import base64
1
2
  import json
2
3
  import math
3
4
  import operator
4
5
  from io import BytesIO
5
- from typing import List
6
+ from typing import Dict, List
6
7
 
8
+ import requests
7
9
  from clarifai_grpc.grpc.api import resources_pb2
8
10
  from clarifai_grpc.grpc.api.resources_pb2 import ModelTypeEnumOption, ModelTypeRangeInfo
9
11
  from clarifai_grpc.grpc.api.resources_pb2 import ModelTypeField as ParamProto
10
- from PIL import Image
12
+ from PIL import Image as PILImage
11
13
 
12
- from clarifai.runners.utils.data_types import MessageData
14
+ from clarifai.runners.utils.data_types import Audio, Image, MessageData, Video
13
15
 
14
16
 
15
- def image_to_bytes(img: Image.Image, format="JPEG") -> bytes:
17
+ def image_to_bytes(img: PILImage.Image, format="JPEG") -> bytes:
16
18
  buffered = BytesIO()
17
19
  img.save(buffered, format=format)
18
20
  img_str = buffered.getvalue()
19
21
  return img_str
20
22
 
21
23
 
22
- def bytes_to_image(bytes_img) -> Image.Image:
23
- img = Image.open(BytesIO(bytes_img))
24
+ def bytes_to_image(bytes_img) -> PILImage.Image:
25
+ img = PILImage.open(BytesIO(bytes_img))
24
26
  return img
25
27
 
26
28
 
27
- def is_openai_chat_format(messages):
28
- """
29
- Verify if the given argument follows the OpenAI chat messages format.
30
-
31
- Args:
32
- messages (list): A list of dictionaries representing chat messages.
33
-
34
- Returns:
35
- bool: True if valid, False otherwise.
36
- """
37
- if not isinstance(messages, list):
38
- return False
39
-
40
- valid_roles = {"system", "user", "assistant", "function"}
41
-
42
- for msg in messages:
43
- if not isinstance(msg, dict):
44
- return False
45
- if "role" not in msg or "content" not in msg:
46
- return False
47
- if msg["role"] not in valid_roles:
48
- return False
49
-
50
- content = msg["content"]
51
-
52
- # Content should be either a string (text message) or a multimodal list
53
- if isinstance(content, str):
54
- continue # Valid text message
55
-
56
- elif isinstance(content, list):
57
- for item in content:
58
- if not isinstance(item, dict):
59
- return False
60
- return True
29
+ def process_image(image: Image) -> Dict:
30
+ """Convert Clarifai Image object to OpenAI image format."""
31
+
32
+ if image.bytes:
33
+ b64_img = image.to_base64_str()
34
+ return {'type': 'image_url', 'image_url': {'url': f"data:image/jpeg;base64,{b64_img}"}}
35
+ elif image.url:
36
+ return {'type': 'image_url', 'image_url': {'url': image.url}}
37
+ else:
38
+ raise ValueError("Image must contain either bytes or URL")
39
+
40
+
41
+ def process_audio(audio: Audio) -> Dict:
42
+ """Convert Clarifai Audio object to OpenAI audio format."""
43
+
44
+ if audio.bytes:
45
+ audio = audio.to_base64_str()
46
+ audio = {
47
+ "type": "input_audio",
48
+ "input_audio": {"data": audio, "format": "wav"},
49
+ }
50
+ elif audio.url:
51
+ response = requests.get(audio.url)
52
+ if response.status_code != 200:
53
+ raise ValueError(f"Failed to fetch audio from URL: {audio.url}")
54
+ audio_base64_str = base64.b64encode(response.content).decode('utf-8')
55
+ audio = {
56
+ "type": "input_audio",
57
+ "input_audio": {"data": audio_base64_str, "format": "wav"},
58
+ }
59
+ else:
60
+ raise ValueError("Audio must contain either bytes or URL")
61
+
62
+ return audio
63
+
64
+
65
+ def process_video(video: Video) -> Dict:
66
+ """Convert Clarifai Video object to OpenAI video format."""
67
+
68
+ if video.bytes:
69
+ video = "data:video/mp4;base64," + video.to_base64_str()
70
+ video = {
71
+ "type": "video_url",
72
+ "video_url": {"url": video},
73
+ }
74
+ elif video.url:
75
+ response = requests.get(video.url)
76
+ if response.status_code != 200:
77
+ raise ValueError(f"Failed to fetch video from URL: {video.url}")
78
+ video_base64_str = base64.b64encode(response.content).decode('utf-8')
79
+ video = {
80
+ "type": "video_url",
81
+ "video_url": {"url": video_base64_str},
82
+ }
83
+ else:
84
+ raise ValueError("Video must contain either bytes or URL")
85
+
86
+ return video
61
87
 
62
88
 
63
89
  class Param(MessageData):
@@ -1,5 +1,9 @@
1
1
  import time
2
2
  import uuid
3
+ from typing import Dict, List
4
+
5
+ from clarifai.runners.utils.data_types import Audio, Image, Video
6
+ from clarifai.runners.utils.data_utils import process_audio, process_image, process_video
3
7
 
4
8
 
5
9
  def generate_id():
@@ -164,3 +168,102 @@ def openai_to_hf_messages(openai_messages):
164
168
  hf_messages.append({'role': role, 'content': hf_content})
165
169
 
166
170
  return hf_messages
171
+
172
+
173
+ def build_openai_messages(
174
+ prompt: str,
175
+ image: Image,
176
+ images: List[Image],
177
+ audio: Audio,
178
+ audios: List[Audio],
179
+ video: Video,
180
+ videos: List[Video],
181
+ messages: List[Dict],
182
+ ) -> List[Dict]:
183
+ """
184
+ Construct OpenAI-compatible messages from input components.
185
+ Args:
186
+ prompt (str): The prompt text.
187
+ image (Image): Clarifai Image object.
188
+ images (List[Image]): List of Clarifai Image objects.
189
+ audio (Audio): Clarifai Audio object.
190
+ audios (List[Audio]): List of Clarifai Audio objects.
191
+ video (Video): Clarifai Video object.
192
+ videos (List[Video]): List of Clarifai Video objects.
193
+ messages (List[Dict]): List of chat messages.
194
+ Returns:
195
+ List[Dict]: Formatted chat messages.
196
+ """
197
+
198
+ openai_messages = []
199
+ # Add previous conversation history
200
+ if messages and is_openai_chat_format(messages):
201
+ openai_messages.extend(messages)
202
+
203
+ content = []
204
+ if prompt.strip():
205
+ # Build content array for current message
206
+ content.append({'type': 'text', 'text': prompt})
207
+ # Add single image if present
208
+ if image:
209
+ content.append(process_image(image))
210
+ # Add multiple images if present
211
+ if images:
212
+ for img in images:
213
+ content.append(process_image(img))
214
+ # Add single audio if present
215
+ if audio:
216
+ content.append(process_audio(audio))
217
+ # Add multiple audios if present
218
+ if audios:
219
+ for audio in audios:
220
+ content.append(process_audio(audio))
221
+ # Add single video if present
222
+ if video:
223
+ content.append(process_video(video))
224
+ # Add multiple videos if present
225
+ if videos:
226
+ for video in videos:
227
+ content.append(process_video(video))
228
+
229
+ if content:
230
+ # Append complete user message
231
+ openai_messages.append({'role': 'user', 'content': content})
232
+
233
+ return openai_messages
234
+
235
+
236
+ def is_openai_chat_format(messages):
237
+ """
238
+ Verify if the given argument follows the OpenAI chat messages format.
239
+
240
+ Args:
241
+ messages (list): A list of dictionaries representing chat messages.
242
+
243
+ Returns:
244
+ bool: True if valid, False otherwise.
245
+ """
246
+ if not isinstance(messages, list):
247
+ return False
248
+
249
+ valid_roles = {"system", "user", "assistant", "function"}
250
+
251
+ for msg in messages:
252
+ if not isinstance(msg, dict):
253
+ return False
254
+ if "role" not in msg or "content" not in msg:
255
+ return False
256
+ if msg["role"] not in valid_roles:
257
+ return False
258
+
259
+ content = msg["content"]
260
+
261
+ # Content should be either a string (text message) or a multimodal list
262
+ if isinstance(content, str):
263
+ continue # Valid text message
264
+
265
+ elif isinstance(content, list):
266
+ for item in content:
267
+ if not isinstance(item, dict):
268
+ return False
269
+ return True
clarifai/urls/helper.py CHANGED
@@ -1,10 +1,25 @@
1
+ import os
2
+ from collections import namedtuple
1
3
  from urllib.parse import urlparse
2
4
 
5
+ from clarifai.utils.constants import DEFAULT_BASE, DEFAULT_UI
6
+
7
+ # To help with using ClarifaiUrlHelper with defaults as ClarifaiUrlHelper()
8
+ 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
+
3
14
 
4
15
  class ClarifaiUrlHelper(object):
5
16
  """Lots of helper functionality for dealing with urls around modules."""
6
17
 
7
- def __init__(self, auth, module_manager_imv_id="module_manager_install"):
18
+ def __init__(
19
+ self,
20
+ auth=default_auth,
21
+ module_manager_imv_id: str = "module_manager_install",
22
+ ):
8
23
  """
9
24
  Args:
10
25
  auth: a ClarifaiAuthHelper object.
@@ -13,13 +28,17 @@ class ClarifaiUrlHelper(object):
13
28
  self._module_manager_imv_id = module_manager_imv_id
14
29
 
15
30
  @property
16
- def auth(self):
17
- return self._auth
31
+ def ui(self):
32
+ return self._auth.ui
33
+
34
+ @property
35
+ def base(self):
36
+ return self._auth.base
18
37
 
19
38
  def module_ui_url(self, user_id, app_id, module_id, module_version_id):
20
39
  """This is the path to the module in community."""
21
40
  return "%s/%s/%s/modules/%s/versions/%s" % (
22
- self.auth.ui,
41
+ self.ui,
23
42
  user_id,
24
43
  app_id,
25
44
  module_id,
@@ -30,7 +49,7 @@ class ClarifaiUrlHelper(object):
30
49
  """This is a url that allows for installation of the module from the community at 'module_url'
31
50
  into the destination app_id of the destination user_id."""
32
51
  return "%s/%s/%s/installed_module_versions/%s/install?install=%s" % (
33
- self.auth.ui,
52
+ self.ui,
34
53
  dest_user_id,
35
54
  dest_app_id,
36
55
  self._module_manager_imv_id,
@@ -39,22 +58,51 @@ class ClarifaiUrlHelper(object):
39
58
 
40
59
  def imv_ui_url(self, dest_user_id, dest_app_id, imv_id):
41
60
  return "%s/%s/%s/installed_module_versions/%s" % (
42
- self.auth.ui,
61
+ self.ui,
43
62
  dest_user_id,
44
63
  dest_app_id,
45
64
  imv_id,
46
65
  )
47
66
 
48
- def clarifai_url(self, user_id, app_id, resource_type, resource_id, version_id: str = None):
49
- """This is the path to the resource in community.
67
+ def api_url(self, user_id, app_id, resource_type, resource_id, version_id: str = None):
68
+ """This is the path to the resource in the API.
69
+
70
+ Example:
71
+ https://api.clarifai.com/v2/zeiler/app/modules/module1/versions/2
72
+ https://api.clarifai.com/v2/zeiler/app/models/model1/versions/2
73
+ https://api.clarifai.com/v2/zeiler/app/concepts/concept1
74
+ https://api.clarifai.com/v2/zeiler/app/workflows/workflow1
75
+ https://api.clarifai.com/v2/zeiler/app/tasks/task1
76
+ https://api.clarifai.com/v2/zeiler/app/installed_module_versions/module_manager_install
50
77
 
51
78
  Args:
52
79
  user_id: the author of the resource.
53
80
  app_id: the author's app the resource was created in.
54
- resource_type: the type of resource. One of "modules", "models", "concepts", "inputs", "workflows", "tasks", "installed_module_versions"
81
+ resource_type: the type of resource. One of "modules", "models", "concepts", "inputs", "workflows", "tasks"
55
82
  resource_id: the resource ID
56
- version_id: the version of the resource.
57
83
  """
84
+ self._validate_resource_type(resource_type)
85
+ if version_id is None:
86
+ return "%s/v2/users/%s/apps/%s/%s/%s" % (
87
+ self.base,
88
+ user_id,
89
+ app_id,
90
+ resource_type,
91
+ resource_id,
92
+ )
93
+
94
+ if resource_type in ["concepts", "tasks", "installed_module_versions"]:
95
+ raise ValueError(f"{resource_type} do not have versions.")
96
+ return "%s/v2/users/%s/apps/%s/%s/%s/versions/%s" % (
97
+ self.base,
98
+ user_id,
99
+ app_id,
100
+ resource_type,
101
+ resource_id,
102
+ version_id,
103
+ )
104
+
105
+ def _validate_resource_type(self, resource_type):
58
106
  if resource_type not in [
59
107
  "modules",
60
108
  "models",
@@ -68,10 +116,30 @@ class ClarifaiUrlHelper(object):
68
116
  "resource_type must be one of modules, models, concepts, inputs, workflows, tasks, installed_module_versions but was %s"
69
117
  % resource_type
70
118
  )
119
+
120
+ def clarifai_url(self, user_id, app_id, resource_type, resource_id, version_id: str = None):
121
+ """This is the path to the resource in community UI.
122
+
123
+ Example:
124
+ https://clarifai.com/zeiler/modules/module1/versions/2
125
+ https://clarifai.com/zeiler/models/model1/versions/2
126
+ https://clarifai.com/zeiler/concepts/concept1
127
+ https://clarifai.com/zeiler/workflows/workflow1
128
+ https://clarifai.com/zeiler/tasks/task1
129
+ https://clarifai.com/zeiler/installed_module_versions/module_manager_install
130
+
131
+ Args:
132
+ user_id: the author of the resource.
133
+ app_id: the author's app the resource was created in.
134
+ resource_type: the type of resource. One of "modules", "models", "concepts", "inputs", "workflows", "tasks", "installed_module_versions"
135
+ resource_id: the resource ID
136
+ version_id: the version of the resource.
137
+ """
138
+ self._validate_resource_type(resource_type)
71
139
  if version_id is None:
72
- return "%s/%s/%s/%s/%s" % (self.auth.ui, user_id, app_id, resource_type, resource_id)
140
+ return "%s/%s/%s/%s/%s" % (self.ui, user_id, app_id, resource_type, resource_id)
73
141
  return "%s/%s/%s/%s/%s/versions/%s" % (
74
- self.auth.ui,
142
+ self.ui,
75
143
  user_id,
76
144
  app_id,
77
145
  resource_type,
clarifai/utils/config.py CHANGED
@@ -102,6 +102,25 @@ class Context(OrderedDict):
102
102
  envvar_name = 'CLARIFAI_' + envvar_name
103
103
  os.environ[envvar_name] = str(v)
104
104
 
105
+ def print_env_vars(self):
106
+ """prints the context env vars to the terminal
107
+
108
+ Example:
109
+ # This is helpful in scripts so you can do
110
+
111
+ from clarifai.utils.config import Config
112
+
113
+ Config.from_yaml().current.print_env_vars()
114
+
115
+ """
116
+ for k, v in sorted(self['env'].items()):
117
+ if isinstance(v, dict):
118
+ continue
119
+ envvar_name = k.upper()
120
+ if not envvar_name.startswith('CLARIFAI_'):
121
+ envvar_name = 'CLARIFAI_' + envvar_name
122
+ print(f"export {envvar_name}=\"{v}\"")
123
+
105
124
 
106
125
  @dataclass
107
126
  class Config:
@@ -1,5 +1,9 @@
1
1
  import os
2
2
 
3
+ DEFAULT_UI = os.environ.get("CLARIFAI_UI", "https://clarifai.com")
4
+ DEFAULT_BASE = os.environ.get("CLARIFAI_API_BASE", "https://api.clarifai.com")
5
+
6
+
3
7
  CLARIFAI_PAT_ENV_VAR = "CLARIFAI_PAT"
4
8
  CLARIFAI_SESSION_TOKEN_ENV_VAR = "CLARIFAI_SESSION_TOKEN"
5
9
  CLARIFAI_USER_ID_ENV_VAR = "CLARIFAI_USER_ID"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clarifai
3
- Version: 11.4.2
3
+ Version: 11.4.3
4
4
  Home-page: https://github.com/Clarifai/clarifai-python
5
5
  Author: Clarifai
6
6
  Author-email: support@clarifai.com
@@ -1,33 +1,33 @@
1
- clarifai/__init__.py,sha256=pSlLOPfmtDJ3YrQTjGkuoViOvxNtsJ3x0m9xPOysJPQ,23
1
+ clarifai/__init__.py,sha256=WC-Q7uwDymQD3TdIUlUDTeqf9vxRXHJ_xDIRpvTVvsw,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=Bp5wWHTFNPgruQ5qitPzYFSK-w6LqWQg0kS49bdB1Ls,8067
8
+ clarifai/cli/base.py,sha256=Rk87oliNs3q-pVX2ubyjycgo4Gtd0hcDSgr9MqDxyko,8227
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=TXpjcm1My5F1QK8mMW1FwRx1EkS1GRpm1y6gR1qYfV4,27433
11
+ clarifai/cli/model.py,sha256=M1O0QCdIyTcn2GzrkROAtgWR-8SX2WV9nz0l84rkiG4,27375
12
12
  clarifai/cli/nodepool.py,sha256=H6OIdUW_EiyDUwZogzEDoYmVwEjLMsgoDlPyE7gjIuU,4245
13
13
  clarifai/client/__init__.py,sha256=NhpNFRJY6mTi8ca-5hUeTEmYeDKHDNXY48FN63pDuos,703
14
- clarifai/client/app.py,sha256=LmIz06Tf1SVNd5SdLV5iqttEiY1x_8r5zX85MCfuXlo,41344
14
+ clarifai/client/app.py,sha256=D0FG9v07g1dExLnQsYt0OQjsJCkVvuw76BOpcqaCzfM,41380
15
15
  clarifai/client/base.py,sha256=zOmB5HJP_-NmF2BPka14W7VUeJ1OF-fNxeacLsaRj3E,8775
16
- clarifai/client/compute_cluster.py,sha256=Tf4Svvx96pC2dMwdtpUsMiope-Nq941dpOViH4uDx5k,10218
17
- clarifai/client/dataset.py,sha256=lsHrY2yxN_zfTGWzE_Gp7W_oi2G6BW2kPvAmKv6FyVE,35263
18
- clarifai/client/deployment.py,sha256=MQ3Kd8Ldp_JTC1hqhRqPiHe0RVUjb6iBY5DiA2Q7NxA,2839
19
- clarifai/client/input.py,sha256=Oswf3sp8PLs39jGIqfbJ89afh9GTCSsaQ--lYPtEtRo,51169
16
+ clarifai/client/compute_cluster.py,sha256=ViPyh-FibXL1J0ypsVOTaQnR1ymKohmZEuA13RwA-hc,10254
17
+ clarifai/client/dataset.py,sha256=OgdpZkQ_vYmRxL8-qphcNozpvPV1bWTlte9Jv6UkKb8,35299
18
+ clarifai/client/deployment.py,sha256=QBf0tzkKBEpzNgmOEmWUJMOlVWdFEFc70Y44o8y75Gs,2875
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=6KHMxKBb5EbOS_FC3lf3q9jl8p2-mitDhaZ1PejKVo8,86231
22
- clarifai/client/model_client.py,sha256=YCmbUQjMy3pzDzIB7_4hMro7sZCZTMwBFvNpyHpmPtY,22380
23
- clarifai/client/module.py,sha256=LDFpJciJUX-y9EzmLwAY86UGKlzabMSzUXxujoh8C9o,4501
24
- clarifai/client/nodepool.py,sha256=DXTa5Ap-TgcUiwd2tcdHuBFaqe3IgW4RkG05n9iUm98,16379
25
- clarifai/client/runner.py,sha256=Bs7kNk0YiLFKKOWkLZIyAHZxVWf1B5YIl2bsMhIYJw8,2217
26
- clarifai/client/search.py,sha256=Z-8PJJYAw5IlF0ac1M0GCz6TRQhouI8fwZryOJZkP1Q,15625
27
- clarifai/client/user.py,sha256=Ey-sqW5dFE_3HLC-E4mHncKth-nQ1j_q6Tp80TMOfwI,18358
28
- clarifai/client/workflow.py,sha256=55fO5IYhER-kHL1FwP3IiWjWEZYNG3jpe7vBRIi_PJQ,13381
21
+ clarifai/client/model.py,sha256=XptnbfvNnaRCvv6Sdc7hfZnEPL8z9_-nFB3OmKujfAE,86267
22
+ clarifai/client/model_client.py,sha256=V0rDa1Qnol2qfAvJI6SwZfbtXqNowPRRMOYBH2Em0so,22386
23
+ clarifai/client/module.py,sha256=jLViQYvVV3FmRN_ivvbk83uwsx7CgYGeEx4dYAr6yD4,4537
24
+ clarifai/client/nodepool.py,sha256=Y5zQ0JLdTjAp2TmVnx7AAOwaB2YUslk3nl7s6BQ90FQ,16415
25
+ clarifai/client/runner.py,sha256=5xCiqByGGscfNm0IjHelhDTx8-9l8G0C3HL-3YZogK8,2253
26
+ clarifai/client/search.py,sha256=3LLfATrdU43a0mRNITmJV-E53bhfafZkYsbwkTtlnyU,15661
27
+ clarifai/client/user.py,sha256=VqY5j75wJqw1AeaWpZLvbNfSjQHGQm_bdjknDjX_fQY,18394
28
+ clarifai/client/workflow.py,sha256=Bqh8lAmJcSbviJebckchTxucYlU11UQEhFSov7elpsk,13417
29
29
  clarifai/client/auth/__init__.py,sha256=7EwR0NrozkAUwpUnCsqXvE_p0wqx_SelXlSpKShKJK0,136
30
- clarifai/client/auth/helper.py,sha256=10Ow_eCgWMKURYW2aT46GLEax-GYoEUHwbcdt7tX6jg,15199
30
+ clarifai/client/auth/helper.py,sha256=kHBi8GTX19EUiD9n_QgCqilv127TDIpP0_o5MMtzFdY,15167
31
31
  clarifai/client/auth/register.py,sha256=pyY-Kg_64WpN6rXz1SyEzfqL14BS4yADtuYMxLJ4jx4,554
32
32
  clarifai/client/auth/stub.py,sha256=pU4FYeghooCBZmCRyNTdDfJaVe4WyeRBqE3xVwfmMTY,5388
33
33
  clarifai/constants/base.py,sha256=ogmFSZYoF0YhGjHg5aiOc3MLqPr_poKAls6xaD0_C3U,89
@@ -67,7 +67,8 @@ clarifai/runners/__init__.py,sha256=cDJ31l41dDsqW4Xn6sFMkKxxdIMTnGH9IW6sVkq0TNw,
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/model_builder.py,sha256=8Rj5nxDUa_jpDBQ04tD0hztenzXKcx2OEg4ce9ELY4I,48237
70
+ clarifai/runners/models/mcp_class.py,sha256=trtChVaesNSrb-Z3hdr0sdJjX7U7TlX4EMuO00vznTA,5960
71
+ clarifai/runners/models/model_builder.py,sha256=PiqPyTGPSKsYvOQNpBzs4e1_wuEbtE-P3yEkLE4Py10,49231
71
72
  clarifai/runners/models/model_class.py,sha256=OHVd0tMOXDyl9v1vWeHOmYGx_dvP77N4zlLGMyTakag,15575
72
73
  clarifai/runners/models/model_run_locally.py,sha256=6-6WjEKc0ba3gAv4wOLdMs2XOzS3b-2bZHJS0wdVqJY,20088
73
74
  clarifai/runners/models/model_runner.py,sha256=SccX-RxTgruSpQaM21uMSl-z1x6fOa13fQZMQW8NNRY,7297
@@ -75,22 +76,22 @@ clarifai/runners/models/model_servicer.py,sha256=rRd_fNEXwqiBSzTUtPI2r07EBdcCPd8
75
76
  clarifai/runners/models/visual_classifier_class.py,sha256=f9ZP8KFamMUdMpUG3AlL9nVCdcggy_E5n9RJY3ixR1U,2739
76
77
  clarifai/runners/models/visual_detector_class.py,sha256=ky4oFAkGCKPpGPdgaOso-n6D3HcmnbKee_8hBsNiV8U,2883
77
78
  clarifai/runners/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
- clarifai/runners/utils/code_script.py,sha256=M3S8c5XBMdY_VFsirLxdFRrC-PCqk1Tul0_qbm_89l4,10394
79
+ clarifai/runners/utils/code_script.py,sha256=3_zBuMKs-eKfC4rbxKf-atVMz36djKH5QvDY2Fgs6Jk,11418
79
80
  clarifai/runners/utils/const.py,sha256=Q4Ps6gIEJCyTdQCfmT6PaS61WHmhT25XigV1NugWz-E,1544
80
- clarifai/runners/utils/data_utils.py,sha256=phrqojg3zhKDZxgXFdxL0NVLkuV0abbOLOSQPIGPPVI,19744
81
+ clarifai/runners/utils/data_utils.py,sha256=GspFOTgUPdqSQG-Zy6lrB1V55gQhGDG3Co6AxiqAr1I,20792
81
82
  clarifai/runners/utils/loader.py,sha256=K5Y8MPbIe5STw2gDnrL8KqFgKNxEo7bz-RV0ip1T4PM,10900
82
83
  clarifai/runners/utils/method_signatures.py,sha256=3EqrTLxynaBC4clj23iw9fZApFDQeapCzVlre6uybbI,19152
83
- clarifai/runners/utils/openai_convertor.py,sha256=QhbytqGU856gEFggaamZy01hhCwjiWz8ju48iubvbeI,5074
84
+ clarifai/runners/utils/openai_convertor.py,sha256=2emAgi9YXpxlOGRw5MG9jhyUUrdDjD9Sv3mnGtwcgts,8192
84
85
  clarifai/runners/utils/serializers.py,sha256=pI7GqMTC0T3Lu_X8v8TO4RiplO-gC_49Ns37jYwsPtg,7908
85
86
  clarifai/runners/utils/url_fetcher.py,sha256=Segkvi-ktPa3-koOpUu8DNZeWOaK6G82Ya9b7_oIKwo,1778
86
87
  clarifai/runners/utils/data_types/__init__.py,sha256=iBtmPkIoLK9ew6ZiXElGt2YBBTDLEA0fmxE_eOYzvhk,478
87
- clarifai/runners/utils/data_types/data_types.py,sha256=4o2sReR4kMu8B8mLkFntNbBR4tFpfp5hi7dm2MEg5Qs,17948
88
+ clarifai/runners/utils/data_types/data_types.py,sha256=UBHTNPshr94qUs2KqkYis0VlArm23rcSG_EyO_Uetgc,19823
88
89
  clarifai/schema/search.py,sha256=o9-ct8ulLZByB3RCVwZWPgaDwdcW7cM5s-g8oyAz89s,2421
89
- clarifai/urls/helper.py,sha256=mnCWw2JcV67xWMS1Wf36lcEG1RwtDviYpYivpeRaNj8,5342
90
+ clarifai/urls/helper.py,sha256=UshKiEnBAZfMyoWiM3Xx6USXEYmXRFTd4kNpuQSQQM8,7868
90
91
  clarifai/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
92
  clarifai/utils/cli.py,sha256=7lHajIsWzyEU7jfgH1nykwYG63wcHCZ3ep7a6amWZH4,5413
92
- clarifai/utils/config.py,sha256=-mZwJEtv31pfchavgmiaNxRgwbcUhWpxJATH2hU8kwQ,4591
93
- clarifai/utils/constants.py,sha256=goxDnTuI6jtDhP2pWasMGcfd4FL3k6qUs2PE67EZOeM,1882
93
+ clarifai/utils/config.py,sha256=AguQ4UPFxNwDT8hTBrdV4grQ0t4m-pW2V9XKxBWfnNg,5177
94
+ clarifai/utils/constants.py,sha256=RYfDVFpaW7V5ISIvY7uvu5nqTvQ2NEwFOd0dcCytyGk,2030
94
95
  clarifai/utils/logging.py,sha256=0we53uTqUvzrulC86whu-oeWNxn1JjJL0OQ98Bwf9vo,15198
95
96
  clarifai/utils/misc.py,sha256=x7JP8oxU672Z9yAav47Y1anFiL4RD8WvlKBHMVlbyZM,3137
96
97
  clarifai/utils/model_train.py,sha256=0XSAoTkSsrwf4f-W9yw2mkXZtkal7LBLJSoi86CFCn4,9250
@@ -103,9 +104,9 @@ clarifai/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
103
104
  clarifai/workflows/export.py,sha256=Oq3RVNKvv1iH46U6oIjXa-MXWJ4sTlXr_NSfwoxr3H4,2149
104
105
  clarifai/workflows/utils.py,sha256=ESL3INcouNcLKCh-nMpfXX-YbtCzX7tz7hT57_RGQ3M,2079
105
106
  clarifai/workflows/validate.py,sha256=UhmukyHkfxiMFrPPeBdUTiCOHQT5-shqivlBYEyKTlU,2931
106
- clarifai-11.4.2.dist-info/licenses/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
107
- clarifai-11.4.2.dist-info/METADATA,sha256=8ezcxLu3AgPvciJRlpPHEXWoJCfmzrliSXPhTN9nwaM,22398
108
- clarifai-11.4.2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
109
- clarifai-11.4.2.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
110
- clarifai-11.4.2.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
111
- clarifai-11.4.2.dist-info/RECORD,,
107
+ clarifai-11.4.3.dist-info/licenses/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
108
+ clarifai-11.4.3.dist-info/METADATA,sha256=ZtwykytAB7xvh8sEZVa84cPoTAM7nX15TQowlnejPE0,22398
109
+ clarifai-11.4.3.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
110
+ clarifai-11.4.3.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
111
+ clarifai-11.4.3.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
112
+ clarifai-11.4.3.dist-info/RECORD,,