Align variable names in create_trainingjob() with R1 API standard 01/15101/6
authoryhm1620 <yhm1620@khu.ac.kr>
Wed, 1 Oct 2025 13:37:52 +0000 (13:37 +0000)
committeryhm1620 <yhm1620@khu.ac.kr>
Mon, 6 Oct 2025 11:04:43 +0000 (11:04 +0000)
Update trainingjob_controller.py, trainingjob_schema.py, test_trainingjob_controller.py, API_docs, and request.http to fully accommodate the training job creation API compatible with R1AP v8.0.

Issue-Id: AIMLFW-237
Change-Id: Iba9b70768aa33f1c3cd963128fe2d1b30217f325
Signed-off-by: yhm1620 <yhm1620@khu.ac.kr>
API_docs/postman/aimlfw.postman_collection.json
API_docs/training-manager.yaml
request.http
tests/test_trainingjob_controller.py
trainingmgr/controller/trainingjob_controller.py
trainingmgr/schemas/trainingjob_schema.py

index f9c31fe..0143b6a 100644 (file)
@@ -66,7 +66,7 @@
                                                "header": [],
                                                "body": {
                                                        "mode": "raw",
-                                                       "raw": "{\r\n\t\"modelId\": {\r\n        \"modelname\":\"qoe11\",\r\n        \"modelversion\":\"1\"\r\n    },\r\n    \"training_config\": {\r\n        \"description\": \"trainingjob for testing\",\r\n        \"dataPipeline\": {\r\n            \"feature_group_name\": \"testing_influxdb_01\",\r\n            \"query_filter\": \"\",\r\n            \"arguments\": {\"epochs\": 1}\r\n        },\r\n        \"trainingPipeline\": {\r\n                \"training_pipeline_name\": \"qoe_Pipeline_testing_1\", \r\n                \"training_pipeline_version\": \"qoe_Pipeline_testing_1\", \r\n                \"retraining_pipeline_name\":\"qoe_Pipeline_retrain\",\r\n                \"retraining_pipeline_version\":\"2\"\r\n        }\r\n    },\r\n    \"model_location\":\"\",\r\n    \"training_dataset\": \"\",\r\n    \"validation_dataset\": \"\",\r\n    \"notification_url\": \"\",\r\n    \"consumer_rapp_id\": \"\",\r\n    \"producer_rapp_id\": \"\"\r\n}",
+                                                       "raw": "{\r\n\t\"modelId\": {\r\n        \"modelName\":\"qoe11\",\r\n        \"modelVersion\":\"1\"\r\n    },\r\n    \"trainingConfig\": {\r\n        \"description\": \"trainingjob for testing\",\r\n        \"dataPipeline\": {\r\n            \"feature_group_name\": \"testing_influxdb_01\",\r\n            \"query_filter\": \"\",\r\n            \"arguments\": {\"epochs\": 1}\r\n        },\r\n        \"trainingPipeline\": {\r\n                \"training_pipeline_name\": \"qoe_Pipeline_testing_1\", \r\n                \"training_pipeline_version\": \"qoe_Pipeline_testing_1\", \r\n                \"retraining_pipeline_name\":\"qoe_Pipeline_retrain\",\r\n                \"retraining_pipeline_version\":\"2\"\r\n        }\r\n    },\r\n    \"modelLocation\":\"\",\r\n    \"trainingDataset\": \"\",\r\n    \"validationDataset\": \"\",\r\n    \"notificationDestination\": \"\",\r\n    \"consumerRAppId\": \"\",\r\n    \"producerRAppId\": \"\"\r\n}",
                                                        "options": {
                                                                "raw": {
                                                                        "language": "json"
index 90036ac..800a39c 100644 (file)
@@ -876,42 +876,42 @@ definitions:
   ModelTrainingRequest:
     type: object
     required:
-      - training_config
-      - model_id
+      - trainingConfig
+      - modelId
     properties:
-      model_location:
+      modelLocation:
         type: string
         description: Path to the model location
-      training_dataset:
+      trainingDataset:
         type: string
         description: Path to the training dataset
-      validation_dataset:
+      validationDataset:
         type: string
         description: Path to the validation dataset
-      training_config:
+      trainingConfig:
         $ref: '#/definitions/TrainingConfig'
-      notification_url:
+      notificationDestination:
         type: string
         description: Callback URL to notify after training or after any failure
-      consumer_rapp_id:
+      consumerRAppId:
         type: string
         description: Consumer RAPP ID
-      producer_rapp_id:
+      producerRAppId:
         type: string
         description: Producer RAPP ID
-      model_id:
+      modelId:
         $ref: '#/definitions/ModelID'
   ModelID:
     type: object
     required:
-      - modelname
-      - modelversion
+      - modelName
+      - modelVersion
     properties:
-      modelname:
+      modelName:
         type: string
-      modelversion:
+      modelVersion:
         type: string
-      artifactversion:
+      artifactVersion:
         type: string
   TrainingConfig:
     type: object
index af56ad6..0ff26ae 100644 (file)
@@ -88,11 +88,11 @@ Content-Type: application/json
 
 {
     "modelId":{
-        "modelname": "modeltest7",
-        "modelversion": "1"
+        "modelName": "modeltest7",
+        "modelVersion": "1"
     },
-    "model_location": "",
-    "training_config": {
+    "modelLocation": "",
+    "trainingConfig": {
         "description": "trainingjob for testing",
         "dataPipeline": {
             "feature_group_name": "testing_influxdb_01",
@@ -105,11 +105,11 @@ Content-Type: application/json
                 "enable_versioning": false
         }
     },
-    "training_dataset": "",
-    "validation_dataset": "",
-    "notification_url": "",
-    "consumer_rapp_id": "",
-    "producer_rapp_id": ""
+    "trainingDataset": "",
+    "validationDataset": "",
+    "notificationDestination": "",
+    "consumerRAppId": "",
+    "producerRAppId": ""
 }
 
 ### tm: get pipelines
index 3e31d24..945c07c 100644 (file)
@@ -61,12 +61,12 @@ class TestCreateTrainingJob:
         expected_data = {
             "title": "Bad Request",
             "status": 400,
-            "detail": "The 'training_config' field is missing."
+            "detail": "The 'trainingConfig' field is missing."
         }
         trainingjob_req = {
             "modelId": {
-                "modelname": "modeltest",
-                "modelversion": "1"
+                "modelName": "modeltest",
+                "modelVersion": "1"
             }
         }
         response = self.client.post("/training-jobs", data=json.dumps(trainingjob_req),
@@ -79,14 +79,14 @@ class TestCreateTrainingJob:
         expected_data = {
             "title": "Bad Request",
             "status": 400,
-            "detail": "The provided 'training_config' is not valid."
+            "detail": "The provided 'trainingConfig' is not valid."
         }
         trainingjob_req = {
             "modelId": {
-                "modelname": "modeltest",
-                "modelversion": "1"
+                "modelName": "modeltest",
+                "modelVersion": "1"
             },
-            "training_config": {
+            "trainingConfig": {
                 "description": "training job for testing"
             }
         }
@@ -105,11 +105,11 @@ class TestCreateTrainingJob:
         }
         trainingjob_req = {
             "modelId": {
-                "modelname": "test_model",
-                "modelversion": "1"
+                "modelName": "test_model",
+                "modelVersion": "1"
             },
-            "model_location": "",
-            "training_config": {
+            "modelLocation": "",
+            "trainingConfig": {
                 "description": "trainingjob for testing",
                 "dataPipeline": {
                     "feature_group_name": "testing_influxdb_03",
@@ -140,11 +140,11 @@ class TestCreateTrainingJob:
         }
         trainingjob_req = {
             "modelId": {
-                "modelname": "test_model",
-                "modelversion": "1"
+                "modelName": "test_model",
+                "modelVersion": "1"
             },
-            "model_location": "",
-            "training_config": {
+            "modelLocation": "",
+            "trainingConfig": {
                 "description": "trainingjob for testing",
                 "dataPipeline": {
                     "feature_group_name": "testing_influxdb_03",
index 78557f8..cf827b9 100644 (file)
@@ -62,13 +62,13 @@ def create_trainingjob():
         request_json = request.get_json()
         LOGGER.debug(f"Request for training job with JSON: {request_json}")
 
-        if not check_key_in_dictionary(["training_config"], request_json):
-            return ProblemDetails(400, "Bad Request", "The 'training_config' field is missing.").to_json()
-        request_json['training_config'] = json.dumps(request_json["training_config"])
+        if not check_key_in_dictionary(["trainingConfig"], request_json):
+            return ProblemDetails(400, "Bad Request", "The 'trainingConfig' field is missing.").to_json()
+        request_json['trainingConfig'] = json.dumps(request_json["trainingConfig"])
         trainingjob = trainingjob_schema.load(request_json)
-        trainingConfig = trainingjob.training_config
-        if not validateTrainingConfig(trainingConfig):
-            return ProblemDetails(400, "Bad Request", "The provided 'training_config' is not valid.").to_json()
+        training_config = trainingjob.training_config
+        if not validateTrainingConfig(training_config):
+            return ProblemDetails(400, "Bad Request", "The provided 'trainingConfig' is not valid.").to_json()
         model_id = trainingjob.modelId
         registered_model_list = get_modelinfo_by_modelId_service(model_id.modelname, model_id.modelversion)
         if registered_model_list is None:
@@ -83,7 +83,6 @@ def create_trainingjob():
         LOGGER.error(f"Error creating training job: {str(e)}")
         return ProblemDetails(500, "Internal Server Error", str(e)).to_json()
 
-
 @training_job_controller.route('/training-jobs/', methods=['GET'])
 def get_trainingjobs():
     LOGGER.debug(f'Fetching all training jobs')
index c0b27a8..9d44ca3 100644 (file)
@@ -49,8 +49,21 @@ class TrainingJobSchema(ma.SQLAlchemyAutoSchema):
 
     @pre_load
     def processModelId(self, data, **kwargs):
-        modelname = data['modelId']['modelname']
-        modelversion = data['modelId']['modelversion']
+        modelname = data['modelId']['modelName']
+        modelversion = data['modelId']['modelVersion']
+        
+        mapping = {
+            "modelLocation": "model_location",
+            "trainingConfig": "training_config",
+            "trainingDataset": "training_dataset",
+            "validationDataset": "validation_dataset",
+            "notificationDestination": "notification_url",
+            "consumerRAppId": "consumer_rapp_id",
+            "producerRAppId": "producer_rapp_id",
+        }
+        for src, dst in mapping.items():
+            if src in data and dst not in data:
+                data[dst] = data.pop(src)
 
         modeldict = dict(modelname=modelname, modelversion=modelversion)
         data['modelId'] = modeldict