clarifai 11.8.1__py3-none-any.whl → 11.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
clarifai/client/search.py CHANGED
@@ -91,14 +91,21 @@ class Search(Lister, BaseClient):
91
91
  )
92
92
  Lister.__init__(self, page_size=1000)
93
93
 
94
- def _get_annot_proto(self, **kwargs):
94
+ def _get_annot_proto(self, **kwargs) -> resources_pb2.Annotation:
95
95
  """Get an Annotation proto message based on keyword arguments.
96
96
 
97
97
  Args:
98
- **kwargs: Keyword arguments specifying the resource.
98
+ **kwargs: Keyword arguments specifying the annotation data.
99
+ Supported keys:
100
+ - image_bytes (bytes): Raw image bytes
101
+ - image_url (str): URL to an image
102
+ - concepts (List[Dict]): List of concept dictionaries
103
+ - metadata (Dict): Metadata dictionary
104
+ - geo_longitude (float): Geographic longitude
105
+ - geo_latitude (float): Geographic latitude
99
106
 
100
107
  Returns:
101
- resources_pb2.Annotation: An Annotation proto message.
108
+ resources_pb2.Annotation: An Annotation proto message with the specified data.
102
109
  """
103
110
  if not kwargs:
104
111
  return resources_pb2.Annotation()
@@ -139,14 +146,24 @@ class Search(Lister, BaseClient):
139
146
  raise UserError(f"kwargs contain key that is not supported: {key}")
140
147
  return resources_pb2.Annotation(data=self.data_proto)
141
148
 
142
- def _get_input_proto(self, **kwargs):
149
+ def _get_input_proto(self, **kwargs) -> resources_pb2.Input:
143
150
  """Get an Input proto message based on keyword arguments.
144
151
 
145
152
  Args:
146
- **kwargs: Keyword arguments specifying the resource.
153
+ **kwargs: Keyword arguments specifying the input data.
154
+ Supported keys:
155
+ - input_types (List[str]): List of input types ('image', 'text', 'audio', 'video')
156
+ - dataset_ids (List[str]): List of dataset IDs to filter by
157
+ - image_bytes (bytes): Raw image bytes
158
+ - image_url (str): URL to an image
159
+ - text_raw (str): Raw text content
160
+ - concepts (List[Dict]): List of concept dictionaries
161
+ - metadata (Dict): Metadata dictionary
162
+ - geo_longitude (float): Geographic longitude
163
+ - geo_latitude (float): Geographic latitude
147
164
 
148
165
  Returns:
149
- resources_pb2.Input: An Input proto message.
166
+ resources_pb2.Input: An Input proto message with the specified data.
150
167
  """
151
168
  if not kwargs:
152
169
  return resources_pb2.Input()
@@ -194,15 +211,22 @@ class Search(Lister, BaseClient):
194
211
  def _list_topk_generator(
195
212
  self, endpoint: Callable[..., Any], proto_message: Any, request_data: Dict[str, Any]
196
213
  ) -> Generator[Dict[str, Any], None, None]:
197
- """Lists all pages of a resource.
214
+ """Lists top-k results with pagination support.
215
+
216
+ This method handles pagination for search results when top_k is specified,
217
+ automatically calculating the required number of pages and per-page limits.
198
218
 
199
219
  Args:
200
- endpoint (Callable): The endpoint to call.
201
- proto_message (Any): The proto message to use.
202
- request_data (dict): The request data to use.
220
+ endpoint (Callable[..., Any]): The gRPC endpoint method to call for search.
221
+ proto_message (Any): The protobuf message class for the request.
222
+ request_data (Dict[str, Any]): The base request data dictionary.
203
223
 
204
224
  Yields:
205
- response_dict: The next item in the listing.
225
+ Dict[str, Any]: Individual search result items from the API response.
226
+
227
+ Raises:
228
+ UserError: If pagination limits are exceeded or top_k is too large.
229
+ Exception: If the API request fails.
206
230
  """
207
231
  max_pages = ceil(self.top_k / self.default_page_size)
208
232
  total_hits = 0
clarifai/client/user.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Any, Dict, Generator, List
2
+ from typing import Any, Dict, Generator, List, Optional
3
3
 
4
4
  import yaml
5
5
  from clarifai_grpc.grpc.api import resources_pb2, service_pb2
@@ -53,14 +53,17 @@ class User(Lister, BaseClient):
53
53
  Lister.__init__(self)
54
54
 
55
55
  def list_apps(
56
- self, filter_by: Dict[str, Any] = {}, page_no: int = None, per_page: int = None
56
+ self,
57
+ filter_by: Dict[str, Any] = {},
58
+ page_no: Optional[int] = None,
59
+ per_page: Optional[int] = None,
57
60
  ) -> Generator[App, None, None]:
58
61
  """Lists all the apps for the user.
59
62
 
60
63
  Args:
61
- filter_by (dict): A dictionary of filters to be applied to the list of apps.
62
- page_no (int): The page number to list.
63
- per_page (int): The number of items per page.
64
+ filter_by (Dict[str, Any]): A dictionary of filters to be applied to the list of apps.
65
+ page_no (Optional[int]): The page number to list. If None, lists all pages.
66
+ per_page (Optional[int]): The number of items per page. If None, uses default.
64
67
 
65
68
  Yields:
66
69
  App: App objects for the user.
@@ -533,6 +536,178 @@ class User(Lister, BaseClient):
533
536
  ]
534
537
  return f"Clarifai User Details: \n{', '.join(attribute_strings)}\n"
535
538
 
539
+ def get_secret(self, secret_id: str) -> dict:
540
+ """Returns a secret object if exists.
541
+
542
+ Args:
543
+ secret_id (str): The secret ID to interact with
544
+
545
+ Returns:
546
+ Dict: A dictionary containing information about the existing secret ID.
547
+
548
+ Example:
549
+ >>> from clarifai.client.user import User
550
+ >>> client = User(user_id="user_id")
551
+ >>> secret_info = client.get_secret(secret_id="secret_id")
552
+ """
553
+ request = service_pb2.GetSecretRequest(user_app_id=self.user_app_id, id=secret_id)
554
+ response = self._grpc_request(self.STUB.GetSecret, request)
555
+ if response.status.code != status_code_pb2.SUCCESS:
556
+ raise Exception(
557
+ f"""Error getting secret, are you sure this is a valid secret id {secret_id} at the user_id
558
+ {self.user_app_id.user_id}.
559
+ Error: {response.status.description}"""
560
+ )
561
+
562
+ dict_response = MessageToDict(response, preserving_proto_field_name=True)
563
+ kwargs = self.process_response_keys(dict_response["secret"], "secret")
564
+
565
+ return dict(auth=self.auth_helper, **kwargs)
566
+
567
+ def list_secrets(
568
+ self, page_no: int = None, per_page: int = None
569
+ ) -> Generator[dict, None, None]:
570
+ """List all secrets for the user
571
+
572
+ Args:
573
+ page_no (int): The page number to list.
574
+ per_page (int): The number of items per page.
575
+
576
+ Yields:
577
+ Dict: Dictionaries containing information about the secrets.
578
+
579
+ Example:
580
+ >>> from clarifai.client.user import User
581
+ >>> client = User(user_id="user_id")
582
+ >>> all_secrets = list(client.list_secrets())
583
+
584
+ Note:
585
+ Defaults to 16 per page if page_no is specified and per_page is not specified.
586
+ If both page_no and per_page are None, then lists all the resources.
587
+ """
588
+ request_data = dict(user_app_id=self.user_app_id)
589
+ all_secrets_info = self.list_pages_generator(
590
+ self.STUB.ListSecrets,
591
+ service_pb2.ListSecretsRequest,
592
+ request_data,
593
+ per_page=per_page,
594
+ page_no=page_no,
595
+ )
596
+ for secret_info in all_secrets_info:
597
+ yield dict(auth=self.auth_helper, **secret_info)
598
+
599
+ def create_secrets(self, secrets: List[Dict[str, Any]]) -> List[dict]:
600
+ """Creates secrets for the user.
601
+
602
+ Args:
603
+ secrets (List[Dict[str, Any]]): List of secret configurations to create.
604
+ Each secret dict can contain:
605
+ - id (str): The name/ID of the secret (required)
606
+ - value (str): The secret value (required)
607
+ - description (str): Optional description of the secret
608
+ - expires_at (str): Optional expiration timestamp
609
+
610
+ Returns:
611
+ List[Dict]: List of dictionaries containing information about the created secrets.
612
+
613
+ Example:
614
+ >>> from clarifai.client.user import User
615
+ >>> client = User(user_id="user_id")
616
+ >>> secrets = [{"id": "secret1", "value": "secret_value", "description": "My Secret"}]
617
+ >>> created_secrets = client.create_secrets(secrets)
618
+ """
619
+ assert isinstance(secrets, list), "secrets param should be a list"
620
+
621
+ # Convert dict secrets to protobuf Secret objects
622
+ secret_objects = []
623
+ for secret_config in secrets:
624
+ secret_objects.append(resources_pb2.Secret(**secret_config))
625
+
626
+ request = service_pb2.PostSecretsRequest(
627
+ user_app_id=self.user_app_id, secrets=secret_objects
628
+ )
629
+ response = self._grpc_request(self.STUB.PostSecrets, request)
630
+ if response.status.code != status_code_pb2.SUCCESS:
631
+ raise Exception(response.status)
632
+
633
+ self.logger.info(f"Secrets created successfully:\n{response.status}")
634
+
635
+ # Convert response to list of dictionaries
636
+ dict_response = MessageToDict(response, preserving_proto_field_name=True)
637
+ created_secrets = []
638
+ for secret in dict_response.get("secrets", []):
639
+ kwargs = self.process_response_keys(secret, "secret")
640
+ created_secrets.append(dict(auth=self.auth_helper, **kwargs))
641
+
642
+ return created_secrets
643
+
644
+ def patch_secrets(
645
+ self, secrets: List[Dict[str, Any]], action: str = 'overwrite'
646
+ ) -> List[dict]:
647
+ """Patches secrets for the user.
648
+
649
+ Args:
650
+ secrets (List[Dict[str, Any]]): List of secret configurations to patch.
651
+ Each secret dict should contain:
652
+ - id (str): The name/ID of the secret to patch (required)
653
+ - value (str): Optional new secret value
654
+ - description (str): Optional new description
655
+ - expires_at (str): Optional new expiration timestamp
656
+ action (str): The action to perform on the secrets (overwrite/remove).
657
+
658
+ Returns:
659
+ List[Dict]: List of dictionaries containing information about the patched secrets.
660
+
661
+ Example:
662
+ >>> from clarifai.client.user import User
663
+ >>> client = User(user_id="user_id")
664
+ >>> secrets = [{"id": "secret1", "description": "Updated Secret Description"}]
665
+ >>> patched_secrets = client.patch_secrets(secrets, action="overwrite")
666
+ """
667
+ assert isinstance(secrets, list), "secrets param should be a list"
668
+
669
+ # Convert dict secrets to protobuf Secret objects
670
+ secret_objects = []
671
+ for secret_config in secrets:
672
+ secret_objects.append(resources_pb2.Secret(**secret_config))
673
+
674
+ request = service_pb2.PatchSecretsRequest(
675
+ user_app_id=self.user_app_id, secret=secret_objects, action=action
676
+ )
677
+ response = self._grpc_request(self.STUB.PatchSecrets, request)
678
+ if response.status.code != status_code_pb2.SUCCESS:
679
+ raise Exception(response.status)
680
+
681
+ self.logger.info(f"Secrets patched successfully:\n{response.status}")
682
+
683
+ # Convert response to list of dictionaries
684
+ dict_response = MessageToDict(response, preserving_proto_field_name=True)
685
+ patched_secrets = []
686
+ for secret in dict_response.get("secrets", []):
687
+ kwargs = self.process_response_keys(secret, "secret")
688
+ patched_secrets.append(dict(auth=self.auth_helper, **kwargs))
689
+
690
+ return patched_secrets
691
+
692
+ def delete_secrets(self, secret_ids: List[str]) -> None:
693
+ """Deletes a list of secrets for the user.
694
+
695
+ Args:
696
+ secret_ids (List[str]): The secret IDs of the user to delete.
697
+
698
+ Example:
699
+ >>> from clarifai.client.user import User
700
+ >>> client = User(user_id="user_id")
701
+ >>> client.delete_secrets(secret_ids=["secret_id1", "secret_id2"])
702
+ """
703
+ assert isinstance(secret_ids, list), "secret_ids param should be a list"
704
+
705
+ request = service_pb2.DeleteSecretsRequest(user_app_id=self.user_app_id, ids=secret_ids)
706
+ response = self._grpc_request(self.STUB.DeleteSecrets, request)
707
+ if response.status.code != status_code_pb2.SUCCESS:
708
+ raise Exception(response.status)
709
+ self.logger.info("\nSecrets Deleted\n%s", response.status)
710
+
536
711
  def list_models(
537
712
  self,
538
713
  user_id: str = None,
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import time
3
- from typing import Dict, Generator, List
3
+ from typing import Any, Dict, Generator, List, Optional
4
4
 
5
5
  from clarifai_grpc.grpc.api import resources_pb2, service_pb2
6
6
  from clarifai_grpc.grpc.api.resources_pb2 import Input
@@ -25,29 +25,30 @@ class Workflow(Lister, BaseClient):
25
25
 
26
26
  def __init__(
27
27
  self,
28
- url: str = None,
29
- workflow_id: str = None,
30
- workflow_version: Dict = {'id': ""},
31
- output_config: Dict = {'min_value': 0},
28
+ url: Optional[str] = None,
29
+ workflow_id: Optional[str] = None,
30
+ workflow_version: Dict[str, str] = {'id': ""},
31
+ output_config: Dict[str, Any] = {'min_value': 0},
32
32
  base_url: str = DEFAULT_BASE,
33
- pat: str = None,
34
- token: str = None,
35
- root_certificates_path: str = None,
33
+ pat: Optional[str] = None,
34
+ token: Optional[str] = None,
35
+ root_certificates_path: Optional[str] = None,
36
36
  **kwargs,
37
37
  ):
38
38
  """Initializes a Workflow object.
39
39
 
40
40
  Args:
41
- url (str): The URL to initialize the workflow object.
42
- workflow_id (str): The Workflow ID to interact with.
43
- workflow_version (dict): The Workflow Version to interact with.
44
- output_config (dict): The output config to interact with.
45
- min_value (float): The minimum value of the prediction confidence to filter.
46
- max_concepts (int): The maximum number of concepts to return.
47
- select_concepts (list[Concept]): The concepts to select.
48
- sample_ms (int): The number of milliseconds to sample.
41
+ url (Optional[str]): The URL to initialize the workflow object.
42
+ workflow_id (Optional[str]): The Workflow ID to interact with.
43
+ workflow_version (Dict[str, str]): The Workflow Version to interact with.
44
+ Defaults to {'id': ""} for latest version.
45
+ output_config (Dict[str, Any]): The output config to interact with.
46
+ - min_value (float): The minimum value of the prediction confidence to filter.
47
+ - max_concepts (int): The maximum number of concepts to return.
48
+ - select_concepts (List[Concept]): The concepts to select.
49
+ - sample_ms (int): The number of milliseconds to sample.
49
50
  base_url (str): Base API url. Default "https://api.clarifai.com"
50
- pat (str): A personal access token for authentication. Can be set as env var CLARIFAI_PAT
51
+ pat (Optional[str]): A personal access token for authentication. Can be set as env var CLARIFAI_PAT
51
52
  token (str): A session token for authentication. Accepts either a session token or a pat. Can be set as env var CLARIFAI_SESSION_TOKEN
52
53
  root_certificates_path (str): Path to the SSL root certificates file, used to establish secure gRPC connections.
53
54
  **kwargs: Additional keyword arguments to be passed to the Workflow.
@@ -102,6 +102,7 @@ class ModelBuilder:
102
102
  self.folder = self._validate_folder(folder)
103
103
  self.config = self._load_config(os.path.join(self.folder, 'config.yaml'))
104
104
  self._validate_config()
105
+ self._validate_config_secrets()
105
106
  self._validate_stream_options()
106
107
  self.model_proto = self._get_model_proto()
107
108
  self.model_id = self.model_proto.id
@@ -465,6 +466,115 @@ class ModelBuilder:
465
466
  "2) set_output_context"
466
467
  )
467
468
 
469
+ def _validate_config_secrets(self):
470
+ """
471
+ Validate the secrets section in the config file.
472
+ """
473
+ if "secrets" not in self.config:
474
+ return
475
+
476
+ secrets = self.config.get("secrets", [])
477
+ if not isinstance(secrets, list):
478
+ raise ValueError("The 'secrets' field must be an array.")
479
+
480
+ for i, secret in enumerate(secrets):
481
+ if not isinstance(secret, dict):
482
+ raise ValueError(f"Secret at index {i} must be a dictionary.")
483
+
484
+ # Validate required fields
485
+ if "id" not in secret or not secret["id"]:
486
+ raise ValueError(f"Secret at index {i} must have a non-empty 'id' field.")
487
+
488
+ if "type" not in secret or not secret["type"]:
489
+ secret["type"] = "env"
490
+
491
+ if "env_var" not in secret or not secret["env_var"]:
492
+ raise ValueError(f"Secret at index {i} must have a non-empty 'env_var' field.")
493
+ # Validate secret type
494
+ if secret["type"] != "env":
495
+ raise ValueError(
496
+ f"Secret at index {i} has invalid type '{secret['type']}'. Must be 'env'."
497
+ )
498
+
499
+ logger.info(f"Validated {len(secrets)} secrets in config file.")
500
+
501
+ def _process_secrets(self):
502
+ """
503
+ Process secrets from config file and create/validate them using the User client.
504
+ Returns the processed secrets array for inclusion in ModelVersion.OutputInfo.Params.
505
+ """
506
+ if "secrets" not in self.config:
507
+ return []
508
+
509
+ secrets = self.config.get("secrets", [])
510
+ if not secrets:
511
+ return []
512
+
513
+ # Get user client for secret operations
514
+ user = User(
515
+ user_id=self.config.get('model').get('user_id'),
516
+ pat=self.client.pat,
517
+ token=self.client.token,
518
+ base_url=self.client.base,
519
+ )
520
+
521
+ processed_secrets = []
522
+ secrets_to_create = []
523
+
524
+ for secret in secrets:
525
+ secret_id = secret["id"]
526
+ secret_type = secret.get("type", "env")
527
+ env_var = secret["env_var"]
528
+ secret_value = secret.get("value") # Optional for existing secrets
529
+
530
+ # Check if secret already exists
531
+ try:
532
+ existing_secret = user.get_secret(secret_id)
533
+ logger.info(f"Secret '{secret_id}' already exists, using existing secret.")
534
+
535
+ # Add to processed secrets without the value
536
+ processed_secret = {
537
+ "id": secret_id,
538
+ "type": secret_type,
539
+ "env_var": env_var,
540
+ }
541
+ processed_secrets.append(processed_secret)
542
+
543
+ except Exception:
544
+ # Secret doesn't exist, need to create it
545
+ if secret_value:
546
+ logger.info(f"Secret '{secret_id}' does not exist, will create it.")
547
+ secrets_to_create.append(
548
+ {
549
+ "id": secret_id,
550
+ "value": secret_value,
551
+ "description": secret.get("description", f"Secret for {env_var}"),
552
+ }
553
+ )
554
+
555
+ # Add to processed secrets
556
+ processed_secret = {
557
+ "id": secret_id,
558
+ "type": secret_type,
559
+ "env_var": env_var,
560
+ }
561
+ processed_secrets.append(processed_secret)
562
+ else:
563
+ raise ValueError(
564
+ f"Secret '{secret_id}' does not exist and no value provided for creation."
565
+ )
566
+
567
+ # Create new secrets if any
568
+ if secrets_to_create:
569
+ try:
570
+ created_secrets = user.create_secrets(secrets_to_create)
571
+ logger.info(f"Successfully created {len(created_secrets)} new secrets.")
572
+ except Exception as e:
573
+ logger.error(f"Failed to create secrets: {e}")
574
+ raise
575
+
576
+ return processed_secrets
577
+
468
578
  def _is_clarifai_internal(self):
469
579
  """
470
580
  Check if the current user is a Clarifai internal user based on email domain.
@@ -891,19 +1001,21 @@ class ModelBuilder:
891
1001
  )
892
1002
  torch_version = dependencies.get('torch', None)
893
1003
  if 'torch' in dependencies:
894
- if python_version != DEFAULT_PYTHON_VERSION:
895
- raise Exception(
896
- f"torch is not supported with Python version {python_version}, please use Python version {DEFAULT_PYTHON_VERSION} in your config.yaml"
897
- )
898
1004
  if not torch_version:
899
1005
  logger.info(
900
1006
  f"Setup: torch version not found in requirements.txt, using the default version {DEFAULT_AMD_TORCH_VERSION}"
901
1007
  )
902
1008
  torch_version = DEFAULT_AMD_TORCH_VERSION
903
- if torch_version not in [DEFAULT_AMD_TORCH_VERSION]:
904
- raise Exception(
905
- f"torch version {torch_version} not supported, please use one of the following versions: {DEFAULT_AMD_TORCH_VERSION} in your requirements.txt"
906
- )
1009
+ elif torch_version not in [DEFAULT_AMD_TORCH_VERSION]:
1010
+ # Currently, we have only one vLLM image built with the DEFAULT_AMD_TORCH_VERSION.
1011
+ # If the user requests a different PyTorch version, that specific version will be
1012
+ # installed during the requirements.txt installation step
1013
+ torch_version = DEFAULT_AMD_TORCH_VERSION
1014
+ else:
1015
+ logger.info(
1016
+ f"`torch` not found in requirements.txt, using the default torch=={DEFAULT_AMD_TORCH_VERSION}"
1017
+ )
1018
+ torch_version = DEFAULT_AMD_TORCH_VERSION
907
1019
  python_version = DEFAULT_PYTHON_VERSION
908
1020
  gpu_version = DEFAULT_AMD_GPU_VERSION
909
1021
  final_image = AMD_VLLM_BASE_IMAGE.format(
@@ -912,21 +1024,17 @@ class ModelBuilder:
912
1024
  gpu_version=gpu_version,
913
1025
  )
914
1026
  logger.info("Setup: Using vLLM base image to build the Docker image")
915
- elif 'torch' in dependencies:
1027
+ elif (
1028
+ 'torch' in dependencies
1029
+ and (dependencies['torch'] in [None, DEFAULT_AMD_TORCH_VERSION])
1030
+ and python_version == DEFAULT_PYTHON_VERSION
1031
+ ):
916
1032
  torch_version = dependencies['torch']
917
- if python_version != DEFAULT_PYTHON_VERSION:
918
- raise Exception(
919
- f"torch is not supported with Python version {python_version}, please use Python version {DEFAULT_PYTHON_VERSION} in your config.yaml"
920
- )
921
1033
  if not torch_version:
922
1034
  logger.info(
923
1035
  f"torch version not found in requirements.txt, using the default version {DEFAULT_AMD_TORCH_VERSION}"
924
1036
  )
925
1037
  torch_version = DEFAULT_AMD_TORCH_VERSION
926
- if torch_version not in [DEFAULT_AMD_TORCH_VERSION]:
927
- raise Exception(
928
- f"torch version {torch_version} not supported, please use one of the following versions: {DEFAULT_AMD_TORCH_VERSION} in your requirements.txt"
929
- )
930
1038
  python_version = DEFAULT_PYTHON_VERSION
931
1039
  gpu_version = DEFAULT_AMD_GPU_VERSION
932
1040
  final_image = AMD_TORCH_BASE_IMAGE.format(
@@ -1258,6 +1366,29 @@ class ModelBuilder:
1258
1366
  metadata_struct.update({'git_registry': git_info})
1259
1367
  model_version_proto.metadata.CopyFrom(metadata_struct)
1260
1368
 
1369
+ # Process and add secrets to output_info.params
1370
+ try:
1371
+ processed_secrets = self._process_secrets()
1372
+ if processed_secrets:
1373
+ # Initialize output_info.params if not already present
1374
+ if not model_version_proto.HasField("output_info"):
1375
+ model_version_proto.output_info.CopyFrom(resources_pb2.OutputInfo())
1376
+
1377
+ # Initialize params if not already present
1378
+ if not model_version_proto.output_info.HasField("params"):
1379
+ from google.protobuf.struct_pb2 import Struct
1380
+
1381
+ model_version_proto.output_info.params.CopyFrom(Struct())
1382
+
1383
+ # Add secrets to params
1384
+ model_version_proto.output_info.params.update({"secrets": processed_secrets})
1385
+ logger.info(
1386
+ f"Added {len(processed_secrets)} secrets to model version output_info.params"
1387
+ )
1388
+ except Exception as e:
1389
+ logger.error(f"Failed to process secrets: {e}")
1390
+ raise
1391
+
1261
1392
  model_type_id = self.config.get('model').get('model_type_id')
1262
1393
  if model_type_id in CONCEPTS_REQUIRED_MODEL_TYPE:
1263
1394
  if 'concepts' in self.config:
@@ -1382,6 +1513,7 @@ class ModelBuilder:
1382
1513
  user_id=self.client.user_app_id.user_id,
1383
1514
  app_id=self.client.user_app_id.app_id,
1384
1515
  model_id=self.model_proto.id,
1516
+ colorize=True,
1385
1517
  )
1386
1518
  logger.info("""\n
1387
1519
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -290,6 +290,7 @@ class ModelServer:
290
290
  model_id=context.model_id,
291
291
  deployment_id=context.deployment_id,
292
292
  base_url=context.api_base,
293
+ colorize=True,
293
294
  )
294
295
  logger.info(
295
296
  "✅ Your model is running locally and is ready for requests from the API...\n"
@@ -33,6 +33,7 @@ def generate_client_script(
33
33
  compute_cluster_id: str = None,
34
34
  nodepool_id: str = None,
35
35
  use_ctx: bool = False,
36
+ colorize: bool = False,
36
37
  ) -> str:
37
38
  url_helper = ClarifaiUrlHelper()
38
39
 
@@ -91,7 +92,7 @@ response = client.chat.completions.create(
91
92
  "content": "How do I check if a Python object is an instance of a class?",
92
93
  }},
93
94
  ],
94
- temperature=0.7,
95
+ temperature=1.0,
95
96
  stream=False, # stream=True also works, just iterator over the response
96
97
  )
97
98
  print(response)
@@ -203,6 +204,16 @@ model = Model.from_current_context()
203
204
  script_lines.append(method_signatures_str)
204
205
  script_lines.append("")
205
206
  script = "\n".join(script_lines)
207
+ if colorize:
208
+ try:
209
+ from pygments import highlight # type: ignore
210
+ from pygments.formatters import TerminalFormatter # type: ignore
211
+ from pygments.lexers import PythonLexer # type: ignore
212
+
213
+ return highlight(script, PythonLexer(), TerminalFormatter())
214
+ except Exception:
215
+ # Fallback to plain text if pygments is unavailable
216
+ return script
206
217
  return script
207
218
 
208
219