From f83a59057ab71e7f6b8af8f470ac3450be01d1fc Mon Sep 17 00:00:00 2001 From: ashishj1729 Date: Thu, 22 May 2025 13:11:47 +0530 Subject: [PATCH] Upgrading API Download and Upload Model to support ModelVersion and ArtifactVersion Issue-id: AIMLFW-208 Change-Id: Ia38af79eaf63fb6546b7b2dc580115e2fe29fe56 Signed-off-by: ashishj1729 --- apis/mmes_apis.go | 37 ++++++++++++++++++++++++++++++------- core/s3_manager.go | 15 ++++++++++----- routers/router.go | 28 ++++++++++++++-------------- 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/apis/mmes_apis.go b/apis/mmes_apis.go index e0aeda6..96f9c0d 100644 --- a/apis/mmes_apis.go +++ b/apis/mmes_apis.go @@ -23,6 +23,7 @@ import ( "io" "net/http" "os" + "strings" "gerrit.o-ran-sc.org/r/aiml-fw/awmf/modelmgmtservice/core" "gerrit.o-ran-sc.org/r/aiml-fw/awmf/modelmgmtservice/db" @@ -236,7 +237,14 @@ func (m *MmeApiHandler) GetModelInfoByName(cont *gin.Context) { logging.INFO("Get model info by name API ...") modelName := cont.Param("modelName") - bucketObj := m.dbmgr.GetBucketObject(modelName+os.Getenv("INFO_FILE_POSTFIX"), modelName) + bucketObj, err := m.dbmgr.GetBucketObject(modelName+os.Getenv("INFO_FILE_POSTFIX"), modelName) + if err != nil { + logging.ERROR("Unable to GetModelInfoByName: Error ", err) + cont.JSON(http.StatusInternalServerError, gin.H{ + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } modelInfoListResp := models.ModelInfoResponse{ Name: modelName, Data: string(bucketObj), @@ -249,11 +257,14 @@ func (m *MmeApiHandler) GetModelInfoByName(cont *gin.Context) { } // API to upload the trained model in zip format -// TODO : Model version as input - func (m *MmeApiHandler) UploadModel(cont *gin.Context) { logging.INFO("Uploading model API ...") modelName := cont.Param("modelName") + modelVersion := cont.Param("modelVersion") + artifactVersion := cont.Param("artifactVersion") + + modelKey := fmt.Sprintf("%s_%s_%s", modelName, modelVersion, artifactVersion) + exportBucket := strings.ToLower(modelName) //TODO convert multipart.FileHeader to []byted fileHeader, _ := cont.FormFile("file") //TODO : Accept only .zip file for trained model @@ -261,8 +272,8 @@ func (m *MmeApiHandler) UploadModel(cont *gin.Context) { defer file.Close() byteFile, _ := io.ReadAll((file)) - logging.INFO("Uploading model : " + modelName) - if err := m.dbmgr.UploadFile(byteFile, modelName+os.Getenv("MODEL_FILE_POSTFIX"), modelName); err != nil { + logging.INFO("Uploading model : " + modelKey) + if err := m.dbmgr.UploadFile(byteFile, modelKey+os.Getenv("MODEL_FILE_POSTFIX"), exportBucket); err != nil { logging.ERROR("Failed to Upload Model : ", err) cont.JSON(http.StatusInternalServerError, gin.H{ "code": http.StatusInternalServerError, @@ -283,9 +294,21 @@ Input: model name in path params as "modelName" func (m *MmeApiHandler) DownloadModel(cont *gin.Context) { logging.INFO("Download model API ...") modelName := cont.Param("modelName") - fileName := modelName + os.Getenv("MODEL_FILE_POSTFIX") - fileByes := m.dbmgr.GetBucketObject(fileName, modelName) + modelVersion := cont.Param("modelVersion") + artifactVersion := cont.Param("artifactVersion") + modelKey := fmt.Sprintf("%s_%s_%s", modelName, modelVersion, artifactVersion) + exportBucket := strings.ToLower(modelName) + + fileName := modelKey + os.Getenv("MODEL_FILE_POSTFIX") + fileByes, err := m.dbmgr.GetBucketObject(fileName, exportBucket) + if err != nil { + cont.JSON(http.StatusInternalServerError, gin.H{ + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + return + } //Return file in api reponse using byte slice cont.Header("Content-Disposition", "attachment;"+fileName) cont.Header("Content-Type", "application/zip") diff --git a/core/s3_manager.go b/core/s3_manager.go index 558b56d..5f25d89 100644 --- a/core/s3_manager.go +++ b/core/s3_manager.go @@ -46,7 +46,7 @@ type S3Manager struct { type DBMgr interface { CreateBucket(bucketName string) (err error) - GetBucketObject(objectName string, bucketName string) BucketObject + GetBucketObject(objectName string, bucketName string) (BucketObject, error) DeleteBucket(client *s3.S3, objectName string, bucketName string) DeleteBucketObject(client *s3.S3, objectName string, bucketName string) bool UploadFile(dataBytes []byte, file_name string, bucketName string) error @@ -115,7 +115,7 @@ func (s3manager *S3Manager) CreateBucket(bucketName string) (err error) { // objectName : Name of file/object under given bucket // bucketName : Name of s3 bucket // TODO: Return error -func (s3manager *S3Manager) GetBucketObject(objectName string, bucketName string) BucketObject { +func (s3manager *S3Manager) GetBucketObject(objectName string, bucketName string) (BucketObject, error) { var response []byte getInputs := &s3.GetObjectInput{ @@ -125,15 +125,16 @@ func (s3manager *S3Manager) GetBucketObject(objectName string, bucketName string result, err := s3manager.S3Client.GetObject(getInputs) if err != nil { logging.ERROR("Error, can't get fetch object..") - return response + return response, err } defer result.Body.Close() logging.INFO("Successfully retrieved object...") response, err = io.ReadAll(result.Body) if err != nil { logging.ERROR("Recived error while reading body:", err) + return nil, err } - return response + return response, nil } func (s3manager *S3Manager) DeleteBucket(client *s3.S3, objectName string, bucketName string) { @@ -229,7 +230,11 @@ func (s3manager *S3Manager) ListBucket(bucketObjPostfix string) ([]Bucket, error continue } - bucketObject := s3manager.GetBucketObject(*bucket.Name+bucketObjPostfix, *bucket.Name) + bucketObject, err := s3manager.GetBucketObject(*bucket.Name+bucketObjPostfix, *bucket.Name) + if err != nil { + logging.ERROR("Unable to list bucketname ", *bucket.Name, ": Error : ", err.Error()) + continue + } if len(bucketObject) == 0 { continue } diff --git a/routers/router.go b/routers/router.go index a8d0bff..07115ba 100644 --- a/routers/router.go +++ b/routers/router.go @@ -27,21 +27,21 @@ func InitRouter(handler *apis.MmeApiHandler) *gin.Engine { r.Use(gin.Logger()) r.Use(gin.Recovery()) api := r.Group("/ai-ml-model-registration/v1") - { - api.POST("/model-registrations", handler.RegisterModel) - api.POST("/model-registrations/updateArtifact/:modelname/:modelversion/:artifactversion", handler.UpdateArtifact) - api.GET("/model-registrations/:modelRegistrationId", handler.GetModelInfoById) - api.PUT("/model-registrations/:modelRegistrationId", handler.UpdateModel) - api.DELETE("/model-registrations/:modelRegistrationId", handler.DeleteModel) - api.GET("/getModelInfo/:modelName", handler.GetModelInfoByName) - api.POST("/uploadModel/:modelName", handler.UploadModel) - api.GET("/downloadModel/:modelName/model.zip", handler.DownloadModel) - } + { + api.POST("/model-registrations", handler.RegisterModel) + api.POST("/model-registrations/updateArtifact/:modelname/:modelversion/:artifactversion", handler.UpdateArtifact) + api.GET("/model-registrations/:modelRegistrationId", handler.GetModelInfoById) + api.PUT("/model-registrations/:modelRegistrationId", handler.UpdateModel) + api.DELETE("/model-registrations/:modelRegistrationId", handler.DeleteModel) + api.GET("/getModelInfo/:modelName", handler.GetModelInfoByName) + api.POST("/uploadModel/:modelName/:modelVersion/:artifactVersion", handler.UploadModel) + api.GET("/downloadModel/:modelName/:modelVersion/:artifactVersion/model.zip", handler.DownloadModel) + } // As per R1-AP v6 - modelDiscovery:= r.Group("/ai-ml-model-discovery/v1") - { - modelDiscovery.GET("/models", handler.GetModelInfo) - } + modelDiscovery := r.Group("/ai-ml-model-discovery/v1") + { + modelDiscovery.GET("/models", handler.GetModelInfo) + } return r } -- 2.16.6