From f0db9c85c4af19c00b74abc1a76a8bc2762c77ab Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 12:17:34 +0530 Subject: [PATCH 01/13] code improvements --- docker-compose.gpu.yml | 1 + model-inference/constants.py | 1 + model-inference/model_inference.py | 27 +++++++++++++++++++++++--- model-inference/model_inference_api.py | 6 +++--- model_trainer/model_trainer.py | 5 +++-- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/docker-compose.gpu.yml b/docker-compose.gpu.yml index 34a3e003..7ad94ab2 100644 --- a/docker-compose.gpu.yml +++ b/docker-compose.gpu.yml @@ -311,6 +311,7 @@ services: - FIND_FINAL_FOLDER_ID_URL=http://hierarchy-validation:8009/find-folder-id - UPDATE_DATAMODEL_PROGRESS_URL=http://ruuter-private:8088/classifier/datamodel/progress/update - UPDATE_MODEL_TRAINING_STATUS_ENDPOINT=http://ruuter-private:8088/classifier/datamodel/update/training/status + - GET_DATASET_METADATA_ENDPOINT=http://ruuter-private:8088/classifier/datasetgroup/group/metadata ports: - "8003:8003" networks: diff --git a/model-inference/constants.py b/model-inference/constants.py index 77309872..7d2f5943 100644 --- a/model-inference/constants.py +++ b/model-inference/constants.py @@ -46,6 +46,7 @@ class UpdateRequest(BaseModel): bestBaseModel:str updateType: Optional[str] = None progressSessionId: int + dgId:Optional[int]= None class OutlookInferenceRequest(BaseModel): inputId:str diff --git a/model-inference/model_inference.py b/model-inference/model_inference.py index 61c5f8b1..4ccaf819 100644 --- a/model-inference/model_inference.py +++ b/model-inference/model_inference.py @@ -19,12 +19,13 @@ UPDATE_DATAMODEL_PROGRESS_URL = os.getenv("UPDATE_DATAMODEL_PROGRESS_URL") UPDATE_MODEL_TRAINING_STATUS_ENDPOINT = os.getenv("UPDATE_MODEL_TRAINING_STATUS_ENDPOINT") RUUTER_PRIVATE_URL = os.getenv("RUUTER_PRIVATE_URL") +GET_DATASET_METADATA_ENDPOINT=os.getenv("GET_DATASET_METADATA_ENDPOINT") class ModelInference: def __init__(self): pass - def get_class_hierarchy_by_model_id(self, model_id): + def get_outlook_class_hierarchy_by_model_id(self, model_id): try: logger.info(f"get_class_hierarchy_by_model_id - {model_id}") @@ -123,9 +124,9 @@ def validate_class_hierarchy(self, class_hierarchy, model_id): - def get_class_hierarchy_and_validate(self, model_id): + def get_outlook_class_hierarchy_and_validate(self, model_id): try: - class_hierarchy = self.get_class_hierarchy_by_model_id(model_id) + class_hierarchy = self.get_outlook_class_hierarchy_by_model_id(model_id) if class_hierarchy: is_valid = self.validate_class_hierarchy(class_hierarchy, model_id) return is_valid, class_hierarchy @@ -252,4 +253,24 @@ def create_inference(self, payload): raise RuntimeError(f"Failed to call create inference. Reason: {e}") + def get_class_hierarchy_by_dg_id(self, cookies, dg_id): + try: + logger.info(f"get_class_hierarchy_by_dg_id - {dg_id}") + + response_hierarchy = requests.get(GET_DATASET_METADATA_ENDPOINT, params={'groupId': dg_id}, cookies=cookies) + + if response_hierarchy.status_code == 200: + logger.info("DATASET HIERARCHY RETREIVAL SUCCESSFUL") + hierarchy = response_hierarchy.json() + class_hierarchy = hierarchy['response']['data'][0] + return class_hierarchy + + else: + logger.error(f"DATASET HIERARCHY RETRIEVAL FAILED: {response_hierarchy.status_code}") + raise RuntimeError(f"ERROR RESPONSE\n {response_hierarchy.text}") + + + except Exception as e: + logger.error(f"Failed to retrieve the class hierarchy Reason: {e}") + raise RuntimeError(f"Failed to retrieve the class hierarchy Reason: {e}") diff --git a/model-inference/model_inference_api.py b/model-inference/model_inference_api.py index 9627f33d..1b5dec9a 100644 --- a/model-inference/model_inference_api.py +++ b/model-inference/model_inference_api.py @@ -70,7 +70,7 @@ async def download_outlook_model(request: Request, model_data:UpdateRequest): model_progress_session_id = model_data.progressSessionId ## Get class hierarchy and validate it - is_valid, class_hierarchy = model_inference.get_class_hierarchy_and_validate(model_data.modelId) + is_valid, class_hierarchy = model_inference.get_outlook_class_hierarchy_and_validate(model_data.modelId) logger.info(f"IS VALID VALUE : {is_valid}") logger.info(f"CLASS HIERARCHY VALUE : {class_hierarchy}") @@ -230,7 +230,7 @@ async def download_jira_model(request: Request, model_data:UpdateRequest): logger.info("JUST ABOUT TO ENTER get_class_hierarchy_by_model_id") - class_hierarchy = model_inference.get_class_hierarchy_by_model_id(model_data.modelId) + class_hierarchy = model_inference.get_class_hierarchy_by_dg_id(cookies=cookie, dg_id=model_data.dgId) logger.info(f"JIRA UPDATE CLASS HIERARCHY - {class_hierarchy}") @@ -354,7 +354,7 @@ async def download_test_model(request: Request, model_data:UpdateRequest): logger.info("JUST ABOUT TO ENTER get_class_hierarchy_by_model_id") - class_hierarchy = model_inference.get_class_hierarchy_by_model_id(model_data.modelId) + class_hierarchy = model_inference.get_class_hierarchy_by_dg_id(cookies=cookie, dg_id=model_data.dgId) logger.info(f"TEST UPDATE CLASS HIERARCHY - {class_hierarchy}") diff --git a/model_trainer/model_trainer.py b/model_trainer/model_trainer.py index d27840b4..af12e5d8 100644 --- a/model_trainer/model_trainer.py +++ b/model_trainer/model_trainer.py @@ -157,7 +157,7 @@ def update_model_training_progress_session(self,training_status, return session_id - def deploy_model(self, best_model_name, progress_session_id): + def deploy_model(self, best_model_name, progress_session_id, dg_id): payload = {} payload["modelId"] = self.new_model_id @@ -167,6 +167,7 @@ def deploy_model(self, best_model_name, progress_session_id): payload["bestBaseModel"] = best_model_name payload["progressSessionId"] = progress_session_id payload["updateType"] = self.update_type + payload["dgId"] = dg_id if self.update_type == "retrain": payload["replaceDeploymentPlatform"] = self.current_deployment_platform @@ -411,7 +412,7 @@ def train(self): else: logger.info(f"INITIATING DEPLOYMENT TO {self.current_deployment_platform}") - self.deploy_model(best_model_name=best_model_name, progress_session_id=session_id) + self.deploy_model(best_model_name=best_model_name, progress_session_id=session_id,dg_id=dg_id) except Exception as e: self.send_error_progress_session(f"RUNTIME CRASHED - ERROR - {str(e)}") From c304f4c132ac381d01a42c7950818a51237a5d77 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 12:23:54 +0530 Subject: [PATCH 02/13] name improvements --- .../DSL/POST/classifier/datamodel/{re-train.yml => retrain.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename DSL/Ruuter.private/DSL/POST/classifier/datamodel/{re-train.yml => retrain.yml} (100%) diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/re-train.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/retrain.yml similarity index 100% rename from DSL/Ruuter.private/DSL/POST/classifier/datamodel/re-train.yml rename to DSL/Ruuter.private/DSL/POST/classifier/datamodel/retrain.yml From 844ab4290d8f5b056ca85feccca0dd689e2da679 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 13:11:32 +0530 Subject: [PATCH 03/13] debug update --- model-inference/model_inference.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/model-inference/model_inference.py b/model-inference/model_inference.py index 4ccaf819..e4a32a50 100644 --- a/model-inference/model_inference.py +++ b/model-inference/model_inference.py @@ -254,23 +254,30 @@ def create_inference(self, payload): def get_class_hierarchy_by_dg_id(self, cookies, dg_id): - + logger.info("********************************************************************") + logger.info(f"****** Calling function get_class_hierarchy_by_dg_id ******") try: logger.info(f"get_class_hierarchy_by_dg_id - {dg_id}") - + logger.info(f"cookie : {cookies}") + logger.info(f"GET_DATASET_METADATA_ENDPOINT : {GET_DATASET_METADATA_ENDPOINT}") + response_hierarchy = requests.get(GET_DATASET_METADATA_ENDPOINT, params={'groupId': dg_id}, cookies=cookies) - + + logger.info(f"response_hierarchy : {response_hierarchy}") + if response_hierarchy.status_code == 200: logger.info("DATASET HIERARCHY RETREIVAL SUCCESSFUL") hierarchy = response_hierarchy.json() + logger.info(f"DATASET HIERARCHY - {hierarchy}") class_hierarchy = hierarchy['response']['data'][0] + logger.info(f"CLASS HIERARCHY - {class_hierarchy}") return class_hierarchy else: logger.error(f"DATASET HIERARCHY RETRIEVAL FAILED: {response_hierarchy.status_code}") raise RuntimeError(f"ERROR RESPONSE\n {response_hierarchy.text}") - + except Exception as e: logger.error(f"Failed to retrieve the class hierarchy Reason: {e}") - raise RuntimeError(f"Failed to retrieve the class hierarchy Reason: {e}") + raise RuntimeError(f"Failed to retrieve the class hierarchy Reason: {e}") From bd389194c5dad45cf133f53fb514c3cc7979f34e Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 14:29:58 +0530 Subject: [PATCH 04/13] compose update --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 8b4394ec..1b861902 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -305,6 +305,7 @@ services: - FIND_FINAL_FOLDER_ID_URL=http://hierarchy-validation:8009/find-folder-id - UPDATE_DATAMODEL_PROGRESS_URL=http://ruuter-private:8088/classifier/datamodel/progress/update - UPDATE_MODEL_TRAINING_STATUS_ENDPOINT=http://ruuter-private:8088/classifier/datamodel/update/training/status + - GET_DATASET_METADATA_ENDPOINT=http://ruuter-private:8088/classifier/datasetgroup/group/metadata ports: - "8003:8003" networks: From f7aab52ee7ad1e4f02551c1a1be404536728e808 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 14:45:53 +0530 Subject: [PATCH 05/13] debug updates --- model_trainer/datapipeline.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/model_trainer/datapipeline.py b/model_trainer/datapipeline.py index d8aa3959..cb6d58fe 100644 --- a/model_trainer/datapipeline.py +++ b/model_trainer/datapipeline.py @@ -26,9 +26,16 @@ def __init__(self, dg_id,cookie): raise RuntimeError(f"ERROR RESPONSE {response.text}") + logger.info(f"###############################################################") + logger.info(f"****** Calling init function of DataPipeline Class ******") + logger.info(f"Endpoint : {GET_DATASET_METADATA_ENDPOINT}") + logger.info(f"Cookie : {cookies}") + logger.info(f"DGID : {dg_id}") response_hierarchy = requests.get(GET_DATASET_METADATA_ENDPOINT, params={'groupId': dg_id}, cookies=cookies) + logger.info(f"response_hierarchy : {response_hierarchy}") + if response_hierarchy.status_code == 200: logger.info("DATASET HIERARCHY RETREIVAL SUCCESSFUL") hierarchy = response_hierarchy.json() @@ -38,19 +45,6 @@ def __init__(self, dg_id,cookie): logger.error(f"DATASET HIERARCHY RETRIEVAL FAILED: {response_hierarchy.status_code}") logger.error(f"RESPONSE: {response.text}") raise RuntimeError(f"ERROR RESPONSE\n {response_hierarchy.text}") - - - def find_target_column(self,df , filter_list): - - value_set = set(filter_list) - columns_with_exact_or_subset_values = [] - - for column in df.columns: - unique_values = set(df[column].dropna().unique()) - if unique_values and unique_values.issubset(value_set): - columns_with_exact_or_subset_values.append(column) - - return columns_with_exact_or_subset_values def extract_input_columns(self): From 7b7e37828ed93b45897a11ce2a2079a0aeab705b Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 15:16:01 +0530 Subject: [PATCH 06/13] find target column update --- model_trainer/datapipeline.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/model_trainer/datapipeline.py b/model_trainer/datapipeline.py index cb6d58fe..0a273b0f 100644 --- a/model_trainer/datapipeline.py +++ b/model_trainer/datapipeline.py @@ -104,4 +104,16 @@ def create_dataframes(self): filtered_df = filtered_df.dropna() dfs.append(filtered_df) - return dfs \ No newline at end of file + return dfs + + def find_target_column(self,df , filter_list): + + value_set = set(filter_list) + columns_with_exact_or_subset_values = [] + + for column in df.columns: + unique_values = set(df[column].dropna().unique()) + if unique_values and unique_values.issubset(value_set): + columns_with_exact_or_subset_values.append(column) + + return columns_with_exact_or_subset_values \ No newline at end of file From a0e41a7c4a701f07cbe02ab993521f776db99485 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Thu, 27 Feb 2025 15:32:55 +0530 Subject: [PATCH 07/13] cookies issue fix --- model-inference/model_inference.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/model-inference/model_inference.py b/model-inference/model_inference.py index e4a32a50..516a1cf9 100644 --- a/model-inference/model_inference.py +++ b/model-inference/model_inference.py @@ -259,9 +259,11 @@ def get_class_hierarchy_by_dg_id(self, cookies, dg_id): try: logger.info(f"get_class_hierarchy_by_dg_id - {dg_id}") logger.info(f"cookie : {cookies}") + cookies_updated = {"customJwtCookie":cookies} + logger.info(f"cookie_updated : {cookies_updated}") logger.info(f"GET_DATASET_METADATA_ENDPOINT : {GET_DATASET_METADATA_ENDPOINT}") - response_hierarchy = requests.get(GET_DATASET_METADATA_ENDPOINT, params={'groupId': dg_id}, cookies=cookies) + response_hierarchy = requests.get(GET_DATASET_METADATA_ENDPOINT, params={'groupId': dg_id}, cookies=cookies_updated) logger.info(f"response_hierarchy : {response_hierarchy}") From 32c55e87f574f279b349720f9544cbd03fbc865c Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Fri, 28 Feb 2025 11:21:37 +0530 Subject: [PATCH 08/13] bug fix update --- docker-compose.gpu.yml | 2 +- docker-compose.yml | 2 +- model-inference/model_inference_api.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docker-compose.gpu.yml b/docker-compose.gpu.yml index 7ad94ab2..2c65845f 100644 --- a/docker-compose.gpu.yml +++ b/docker-compose.gpu.yml @@ -492,7 +492,7 @@ services: ports: - "3008:3008" env_file: - - jira_config.env + - ./jira-verification/.env environment: RUUTER_PUBLIC_JIRA_URL: http://ruuter-public:8086/internal/jira/accept networks: diff --git a/docker-compose.yml b/docker-compose.yml index 1b861902..2035573f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -474,7 +474,7 @@ services: ports: - "3008:3008" env_file: - - jira_config.env + - ./jira-verification/.env environment: RUUTER_PUBLIC_JIRA_URL: http://ruuter-public:8086/internal/jira/accept networks: diff --git a/model-inference/model_inference_api.py b/model-inference/model_inference_api.py index 1b5dec9a..21efc95d 100644 --- a/model-inference/model_inference_api.py +++ b/model-inference/model_inference_api.py @@ -556,13 +556,14 @@ async def outlook_inference(request:Request, inference_data:OutlookInferenceRequ async def jira_inference(request:Request, inferenceData:JiraInferenceRequest): try: - + logger.info("------------------------------------***********************************-----------------------------------------") logger.info(f"INFERENCE DATA IN JIRA INFERENCE - {inferenceData}") model_id = model_inference_wrapper.get_jira_model_id() - + logger.info("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") if(model_id): + logger.info("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") # 1 . Check whether the if the Inference Exists is_exist, inference_id = model_inference.check_inference_data_exists(input_id=inferenceData.inputId) From 7f2a57179c9d5a0895154e0ff67226b8f8574497 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Fri, 28 Feb 2025 11:41:33 +0530 Subject: [PATCH 09/13] bug fix --- model-inference/inference_pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/model-inference/inference_pipeline.py b/model-inference/inference_pipeline.py index 9bbfe854..537225a8 100644 --- a/model-inference/inference_pipeline.py +++ b/model-inference/inference_pipeline.py @@ -121,6 +121,7 @@ def predict_class(self,text_input): data = self.hierarchy_file + data = data['classHierarchy'] parent = 1 logger.info(f"DATA - {data}") From e2ba31041ddce26f8dd889ccfee93afd7fab38d8 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Fri, 28 Feb 2025 16:10:43 +0530 Subject: [PATCH 10/13] fixing pipeline --- model-inference/inference_pipeline.py | 8 ++++---- model-inference/inference_wrapper.py | 4 ++-- model-inference/test_inference_wrapper.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/model-inference/inference_pipeline.py b/model-inference/inference_pipeline.py index 537225a8..82b7e1fd 100644 --- a/model-inference/inference_pipeline.py +++ b/model-inference/inference_pipeline.py @@ -104,7 +104,7 @@ def find_missing_classes(self, main_classes, uploaded_classes): - def predict_class(self,text_input): + def predict_class(self,text_input, platform): logger.info("ENTERING PREDICT CLASS") @@ -117,11 +117,11 @@ def predict_class(self,text_input): self.base_model.to(self.device) logger.info(f"CLASS HIERARCHY FILE {self.hierarchy_file}") + logger.info(f"PLATFORM IN PREDICT CLASS {platform}") - - data = self.hierarchy_file - data = data['classHierarchy'] + if platform == 'jira': + data = data['classHierarchy'] parent = 1 logger.info(f"DATA - {data}") diff --git a/model-inference/inference_wrapper.py b/model-inference/inference_wrapper.py index 19dcdc5b..6eab9397 100644 --- a/model-inference/inference_wrapper.py +++ b/model-inference/inference_wrapper.py @@ -62,7 +62,7 @@ def inference(self, text:str, deployment_platform:str): if(deployment_platform == "jira" and self.active_jira_model): logger.info("ENTERING JIRA INFERENCE") - predicted_labels, probabilities = self.active_jira_model.predict_class(text) + predicted_labels, probabilities = self.active_jira_model.predict_class(text, deployment_platform) logger.info(f"PREDICTED LABELS INSIDE .inference() FUNCTION - {predicted_labels}") logger.info(f"PROBABILITIES INSIDE .inference() FUNCTION - {probabilities}") @@ -70,7 +70,7 @@ def inference(self, text:str, deployment_platform:str): if(deployment_platform == "outlook" and self.active_outlook_model): logger.info("ENTERING OUTLOOK INFERENCE") - predicted_labels, probabilities = self.active_outlook_model.predict_class(text) + predicted_labels, probabilities = self.active_outlook_model.predict_class(text, deployment_platform) logger.info(f"PREDICTED LABELS INSIDE .inference() FUNCTION - {predicted_labels}") diff --git a/model-inference/test_inference_wrapper.py b/model-inference/test_inference_wrapper.py index a8d0ca4b..652133e5 100644 --- a/model-inference/test_inference_wrapper.py +++ b/model-inference/test_inference_wrapper.py @@ -30,7 +30,7 @@ def inference(self, text: str, model_id: int): predicted_labels = None probabilities = None model = self.model_dictionary[model_id] - predicted_labels, probabilities = model.predict_class(text_input=text) + predicted_labels, probabilities = model.predict_class(text_input=text, platform="test") return predicted_labels, probabilities else: raise Exception(f"Model with ID {model_id} not found") From 8a1473a4e114e5dfe0b6b16a8cfa62551e6bb858 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Fri, 28 Feb 2025 17:02:51 +0530 Subject: [PATCH 11/13] removing the not needed marks --- model-inference/model_inference_api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/model-inference/model_inference_api.py b/model-inference/model_inference_api.py index 21efc95d..e625de79 100644 --- a/model-inference/model_inference_api.py +++ b/model-inference/model_inference_api.py @@ -561,9 +561,7 @@ async def jira_inference(request:Request, inferenceData:JiraInferenceRequest): model_id = model_inference_wrapper.get_jira_model_id() - logger.info("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") if(model_id): - logger.info("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") # 1 . Check whether the if the Inference Exists is_exist, inference_id = model_inference.check_inference_data_exists(input_id=inferenceData.inputId) From a6f2d97e4e703df8cccb2c82f3f7a912719d4913 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Fri, 28 Feb 2025 17:03:16 +0530 Subject: [PATCH 12/13] removing not needed logs --- model-inference/model_inference_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/model-inference/model_inference_api.py b/model-inference/model_inference_api.py index e625de79..3b9a0673 100644 --- a/model-inference/model_inference_api.py +++ b/model-inference/model_inference_api.py @@ -556,7 +556,6 @@ async def outlook_inference(request:Request, inference_data:OutlookInferenceRequ async def jira_inference(request:Request, inferenceData:JiraInferenceRequest): try: - logger.info("------------------------------------***********************************-----------------------------------------") logger.info(f"INFERENCE DATA IN JIRA INFERENCE - {inferenceData}") model_id = model_inference_wrapper.get_jira_model_id() From b36bc76ab9327cb657f0de2dc5f022c855bcbc48 Mon Sep 17 00:00:00 2001 From: pamodaDilranga Date: Fri, 28 Feb 2025 17:41:41 +0530 Subject: [PATCH 13/13] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd18ca6b..70b2f4af 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ This repo will primarily contain: - `JIRA_WEBHOOK_SECRET` – Jira webhook secret you got in **Create Jira Webhook** step. 4. **Create a `.env` file for Jira Configuration:** - - Create a `.env` file called `jira_config.env` and add the following: + - Create a `.env` file in the folder called `jira-verification` and add the following: ```env JIRA_WEBHOOK_SECRET=<> ```