41
41
ModelProvenanceNotFoundError ,
42
42
OCIDataScienceModel ,
43
43
)
44
+ from ads .common import oci_client as oc
45
+ from ads .common .auth import default_signer
44
46
45
47
logger = logging .getLogger (__name__ )
46
48
@@ -1466,3 +1468,159 @@ def _download_file_description_artifact(self) -> Tuple[Union[str, List[str]], in
1466
1468
bucket_uri .append (uri )
1467
1469
1468
1470
return bucket_uri [0 ] if len (bucket_uri ) == 1 else bucket_uri , artifact_size
1471
+
1472
+ def add_artifact (
1473
+ self ,
1474
+ namespace : str ,
1475
+ bucket : str ,
1476
+ prefix : Optional [str ] = None ,
1477
+ files : Optional [List [str ]] = None ,
1478
+ ):
1479
+ """
1480
+ Adds information about objects in a specified bucket to the model description JSON.
1481
+
1482
+ Parameters:
1483
+ - namespace (str): The namespace of the object storage.
1484
+ - bucket (str): The name of the bucket containing the objects.
1485
+ - prefix (str, optional): The prefix used to filter objects within the bucket. Defaults to None.
1486
+ - files (list of str, optional): A list of file names to include in the model description.
1487
+ If provided, only objects with matching file names will be included. Defaults to None.
1488
+
1489
+ Returns:
1490
+ - None
1491
+
1492
+ Raises:
1493
+ - ValueError: If no files are found to add to the model description.
1494
+
1495
+ Note:
1496
+ - If `files` is not provided, it retrieves information about all objects in the bucket.
1497
+ If `files` is provided, it only retrieves information about objects with matching file names.
1498
+ - If no objects are found to add to the model description, a ValueError is raised.
1499
+ """
1500
+ if self .model_file_description == None :
1501
+ self .empty_json = {
1502
+ "version" : "1.0" ,
1503
+ "type" : "modelOSSReferenceDescription" ,
1504
+ "models" : [],
1505
+ }
1506
+ self .set_spec (self .CONST_MODEL_FILE_DESCRIPTION , self .empty_json )
1507
+
1508
+ # Get object storage client
1509
+ authData = default_signer ()
1510
+ self .object_storage_client = oc .OCIClientFactory (** authData ).object_storage
1511
+
1512
+ # Remove if the model already exists
1513
+ self .remove_artifact (namespace , bucket , prefix )
1514
+
1515
+ def check_if_file_exists (fileName ):
1516
+ isExists = False
1517
+ try :
1518
+ headResponse = self .object_storage_client .head_object (
1519
+ namespace , bucket , object_name = fileName
1520
+ )
1521
+ if headResponse .status == 200 :
1522
+ isExists = True
1523
+ except Exception as e :
1524
+ if hasattr (e , "status" ) and e .status == 404 :
1525
+ logger .error (f"File not found in bucket: { fileName } " )
1526
+ else :
1527
+ logger .error (f"An error occured: { e } " )
1528
+ return isExists
1529
+
1530
+ # Function to un-paginate the api call with while loop
1531
+ def list_obj_versions_unpaginated ():
1532
+ objectStorageList = []
1533
+ has_next_page , opc_next_page = True , None
1534
+ while has_next_page :
1535
+ response = self .object_storage_client .list_object_versions (
1536
+ namespace_name = namespace ,
1537
+ bucket_name = bucket ,
1538
+ prefix = prefix ,
1539
+ fields = "name,size" ,
1540
+ page = opc_next_page ,
1541
+ )
1542
+ objectStorageList .extend (response .data .items )
1543
+ has_next_page = response .has_next_page
1544
+ opc_next_page = response .next_page
1545
+ return objectStorageList
1546
+
1547
+ # Fetch object details and put it into the objects variable
1548
+ objectStorageList = []
1549
+ if files == None :
1550
+ objectStorageList = list_obj_versions_unpaginated ()
1551
+ else :
1552
+ for fileName in files :
1553
+ if check_if_file_exists (fileName = fileName ):
1554
+ objectStorageList .append (
1555
+ self .object_storage_client .list_object_versions (
1556
+ namespace_name = namespace ,
1557
+ bucket_name = bucket ,
1558
+ prefix = fileName ,
1559
+ fields = "name,size" ,
1560
+ ).data .items [0 ]
1561
+ )
1562
+
1563
+ objects = [
1564
+ {"name" : obj .name , "version" : obj .version_id , "sizeInBytes" : obj .size }
1565
+ for obj in objectStorageList
1566
+ if obj .size > 0
1567
+ ]
1568
+
1569
+ if len (objects ) == 0 :
1570
+ error_message = (
1571
+ f"No files to add in the bucket: { bucket } with namespace: { namespace } "
1572
+ f"and prefix: { prefix } . File names: { files } "
1573
+ )
1574
+ logger .error (error_message )
1575
+ raise ValueError (error_message )
1576
+
1577
+ tmp_model_file_description = self .model_file_description
1578
+ tmp_model_file_description ["models" ].append (
1579
+ {
1580
+ "namespace" : namespace ,
1581
+ "bucketName" : bucket ,
1582
+ "prefix" : prefix ,
1583
+ "objects" : objects ,
1584
+ }
1585
+ )
1586
+ self .set_spec (self .CONST_MODEL_FILE_DESCRIPTION , tmp_model_file_description )
1587
+
1588
+ def remove_artifact (self , namespace : str , bucket : str , prefix : Optional [str ] = None ):
1589
+ """
1590
+ Removes information about objects in a specified bucket from the model description JSON.
1591
+
1592
+ Parameters:
1593
+ - namespace (str): The namespace of the object storage.
1594
+ - bucket (str): The name of the bucket containing the objects.
1595
+ - prefix (str, optional): The prefix used to filter objects within the bucket. Defaults to None.
1596
+
1597
+ Returns:
1598
+ - None
1599
+
1600
+ Note:
1601
+ - This method removes information about objects in the specified bucket from the
1602
+ instance of the ModelDescription.
1603
+ - If a matching model (with the specified namespace, bucket name, and prefix) is found
1604
+ in the model description JSON, it is removed.
1605
+ - If no matching model is found, the method returns without making any changes.
1606
+ """
1607
+
1608
+ def findModelIdx ():
1609
+ for idx , model in enumerate (self .model_file_description ["models" ]):
1610
+ if (
1611
+ model ["namespace" ],
1612
+ model ["bucketName" ],
1613
+ (model ["prefix" ] if ("prefix" in model ) else None ),
1614
+ ) == (namespace , bucket , prefix ):
1615
+ return idx
1616
+ return - 1
1617
+
1618
+ if self .model_file_description == None :
1619
+ return
1620
+
1621
+ modelSearchIdx = findModelIdx ()
1622
+ if modelSearchIdx == - 1 :
1623
+ return
1624
+ else :
1625
+ # model found case
1626
+ self .model_file_description ["models" ].pop (modelSearchIdx )
0 commit comments