clarifai 11.4.2__tar.gz → 11.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {clarifai-11.4.2/clarifai.egg-info → clarifai-11.4.3}/PKG-INFO +1 -1
  2. clarifai-11.4.3/clarifai/__init__.py +1 -0
  3. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/base.py +7 -0
  4. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/model.py +6 -8
  5. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/app.py +2 -1
  6. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/auth/helper.py +6 -4
  7. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/compute_cluster.py +2 -1
  8. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/dataset.py +2 -1
  9. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/deployment.py +2 -1
  10. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/input.py +2 -1
  11. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/model.py +2 -1
  12. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/model_client.py +1 -1
  13. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/module.py +2 -1
  14. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/nodepool.py +2 -1
  15. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/runner.py +2 -1
  16. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/search.py +2 -1
  17. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/user.py +2 -1
  18. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/workflow.py +2 -1
  19. clarifai-11.4.3/clarifai/runners/models/mcp_class.py +114 -0
  20. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/model_builder.py +40 -11
  21. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/code_script.py +35 -1
  22. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/data_types/data_types.py +48 -0
  23. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/data_utils.py +66 -40
  24. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/openai_convertor.py +103 -0
  25. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/urls/helper.py +80 -12
  26. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/config.py +19 -0
  27. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/constants.py +4 -0
  28. {clarifai-11.4.2 → clarifai-11.4.3/clarifai.egg-info}/PKG-INFO +1 -1
  29. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai.egg-info/SOURCES.txt +1 -0
  30. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_modules.py +9 -18
  31. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_rag.py +1 -2
  32. clarifai-11.4.2/clarifai/__init__.py +0 -1
  33. {clarifai-11.4.2 → clarifai-11.4.3}/LICENSE +0 -0
  34. {clarifai-11.4.2 → clarifai-11.4.3}/MANIFEST.in +0 -0
  35. {clarifai-11.4.2 → clarifai-11.4.3}/README.md +0 -0
  36. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/README.md +0 -0
  37. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/__init__.py +0 -0
  38. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/__main__.py +0 -0
  39. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/compute_cluster.py +0 -0
  40. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/deployment.py +0 -0
  41. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli/nodepool.py +0 -0
  42. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/cli.py +0 -0
  43. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/__init__.py +0 -0
  44. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/auth/__init__.py +0 -0
  45. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/auth/register.py +0 -0
  46. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/auth/stub.py +0 -0
  47. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/base.py +0 -0
  48. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/client/lister.py +0 -0
  49. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/base.py +0 -0
  50. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/dataset.py +0 -0
  51. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/input.py +0 -0
  52. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/model.py +0 -0
  53. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/rag.py +0 -0
  54. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/search.py +0 -0
  55. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/constants/workflow.py +0 -0
  56. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/__init__.py +0 -0
  57. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/export/__init__.py +0 -0
  58. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/export/inputs_annotations.py +0 -0
  59. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/__init__.py +0 -0
  60. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/base.py +0 -0
  61. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/features.py +0 -0
  62. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/image.py +0 -0
  63. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/loaders/README.md +0 -0
  64. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/loaders/__init__.py +0 -0
  65. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/loaders/coco_captions.py +0 -0
  66. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/loaders/coco_detection.py +0 -0
  67. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/loaders/imagenet_classification.py +0 -0
  68. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/loaders/xview_detection.py +0 -0
  69. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/multimodal.py +0 -0
  70. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/text.py +0 -0
  71. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/datasets/upload/utils.py +0 -0
  72. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/errors.py +0 -0
  73. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/models/__init__.py +0 -0
  74. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/models/api.py +0 -0
  75. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/modules/README.md +0 -0
  76. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/modules/__init__.py +0 -0
  77. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/modules/css.py +0 -0
  78. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/modules/pages.py +0 -0
  79. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/modules/style.css +0 -0
  80. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/rag/__init__.py +0 -0
  81. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/rag/rag.py +0 -0
  82. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/rag/utils.py +0 -0
  83. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/__init__.py +0 -0
  84. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/dockerfile_template/Dockerfile.template +0 -0
  85. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/__init__.py +0 -0
  86. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/model_class.py +0 -0
  87. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/model_run_locally.py +0 -0
  88. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/model_runner.py +0 -0
  89. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/model_servicer.py +0 -0
  90. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/visual_classifier_class.py +0 -0
  91. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/models/visual_detector_class.py +0 -0
  92. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/server.py +0 -0
  93. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/__init__.py +0 -0
  94. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/const.py +0 -0
  95. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/data_types/__init__.py +0 -0
  96. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/loader.py +0 -0
  97. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/method_signatures.py +0 -0
  98. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/serializers.py +0 -0
  99. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/runners/utils/url_fetcher.py +0 -0
  100. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/schema/search.py +0 -0
  101. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/__init__.py +0 -0
  102. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/cli.py +0 -0
  103. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/evaluation/__init__.py +0 -0
  104. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/evaluation/helpers.py +0 -0
  105. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/evaluation/main.py +0 -0
  106. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/evaluation/testset_annotation_parser.py +0 -0
  107. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/logging.py +0 -0
  108. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/misc.py +0 -0
  109. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/model_train.py +0 -0
  110. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/utils/protobuf.py +0 -0
  111. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/versions.py +0 -0
  112. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/workflows/__init__.py +0 -0
  113. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/workflows/export.py +0 -0
  114. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/workflows/utils.py +0 -0
  115. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai/workflows/validate.py +0 -0
  116. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai.egg-info/dependency_links.txt +0 -0
  117. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai.egg-info/entry_points.txt +0 -0
  118. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai.egg-info/requires.txt +0 -0
  119. {clarifai-11.4.2 → clarifai-11.4.3}/clarifai.egg-info/top_level.txt +0 -0
  120. {clarifai-11.4.2 → clarifai-11.4.3}/pyproject.toml +0 -0
  121. {clarifai-11.4.2 → clarifai-11.4.3}/requirements.txt +0 -0
  122. {clarifai-11.4.2 → clarifai-11.4.3}/setup.cfg +0 -0
  123. {clarifai-11.4.2 → clarifai-11.4.3}/setup.py +0 -0
  124. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_app.py +0 -0
  125. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_auth.py +0 -0
  126. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_data_upload.py +0 -0
  127. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_eval.py +0 -0
  128. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_misc.py +0 -0
  129. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_model_predict.py +0 -0
  130. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_model_train.py +0 -0
  131. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_search.py +0 -0
  132. {clarifai-11.4.2 → clarifai-11.4.3}/tests/test_stub.py +0 -0
@@ -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
@@ -0,0 +1 @@
1
+ __version__ = "11.4.3"
@@ -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')
@@ -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...
@@ -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,
@@ -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,
@@ -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
 
@@ -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,
@@ -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,
@@ -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,
@@ -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)