From 7d8b64e5ef827c716e0e91e51123b85f48b2987c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Hincapi=C3=A9=20M?= <43832784+shincap8@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:43:51 -0500 Subject: [PATCH] Feature - Heavy Evaluation (#316) * New model for heavy inference implemented also in the frontEnd * black changes * include creation of model in DB --- backend/app/api/endpoints/base/model.py | 21 ++++++ backend/app/domain/helpers/email.py | 3 +- backend/app/domain/services/base/model.py | 65 ++++++++++++++++++- .../app/infrastructure/repositories/task.py | 7 ++ .../pages/CreateSamples/CreateInterface.tsx | 15 ++--- .../pages/SubmitModel/SubmitModel.tsx | 25 +++---- .../pages/SubmitModel/useUploadFile.ts | 9 ++- 7 files changed, 122 insertions(+), 23 deletions(-) diff --git a/backend/app/api/endpoints/base/model.py b/backend/app/api/endpoints/base/model.py index 1a6a1bce..4b7fc2ee 100644 --- a/backend/app/api/endpoints/base/model.py +++ b/backend/app/api/endpoints/base/model.py @@ -87,6 +87,27 @@ async def upload_model_to_s3_and_evaluate( return "The model will be evaluated in the background" +@router.post("/heavy_evaluation") +def heavy_evaluation( + background_tasks: BackgroundTasks, + model: UploadModelToS3AndEvaluateRequest = Depends( + UploadModelToS3AndEvaluateRequest + ), +): + return ModelService().upload_model_to_s3( + model.model_name, + model.description, + model.num_paramaters, + model.languages, + model.license, + model.file_name, + model.user_id, + model.task_code, + model.file_to_upload, + background_tasks, + ) + + @router.get("/initiate_lambda_models") def initiate_lambda_models() -> None: return ModelService().initiate_lambda_models() diff --git a/backend/app/domain/helpers/email.py b/backend/app/domain/helpers/email.py index 28424d3a..f8136bad 100644 --- a/backend/app/domain/helpers/email.py +++ b/backend/app/domain/helpers/email.py @@ -64,6 +64,7 @@ def send( server.close() return f"e-mail sended to {contact}" - except Exception: + except Exception as e: print("Error sending e-mail") + print(e) return False diff --git a/backend/app/domain/services/base/model.py b/backend/app/domain/services/base/model.py index a50df2fc..d3c4d0af 100644 --- a/backend/app/domain/services/base/model.py +++ b/backend/app/domain/services/base/model.py @@ -4,13 +4,14 @@ import json import os +import re import secrets import time import boto3 import requests import yaml -from fastapi import HTTPException, UploadFile +from fastapi import BackgroundTasks, HTTPException, UploadFile from pydantic import Json from app.domain.helpers.email import EmailHelper @@ -174,6 +175,68 @@ def upload_model_to_s3_and_evaluate( def single_model_prediction(self, model_url: str, model_input: dict): return requests.post(model_url, json=model_input).json() + def upload_model_to_s3( + self, + model_name: str, + description: str, + num_paramaters: str, + languages: str, + license: str, + file_name: str, + user_id: str, + task_code: str, + file_to_upload: UploadFile, + background_tasks: BackgroundTasks, + ): + task_id = self.task_repository.get_task_id_by_task_code(task_code)[0] + task_s3_bucket = self.task_repository.get_s3_bucket_by_task_id(task_id)[0] + user_email = self.user_repository.get_user_email(user_id)[0] + + file_name = file_name.lower() + file_name = file_name.replace("/", ":") + file_name = re.sub(r"\s+", "_", file_name) + clean_file_name = re.sub(r"_+", "_", file_name) + + model_name_clean = model_name.lower() + model_name_clean = model_name_clean.replace("/", ":") + model_name_clean = re.sub(r"\s+", "_", model_name_clean) + model_name_clean = re.sub(r"_+", "_", model_name_clean) + + model_path = f"{task_code}/submited_models/{task_id}-{user_id}-{model_name}-{clean_file_name}" + try: + self.s3.put_object( + Body=file_to_upload.file, + Bucket=task_s3_bucket, + Key=model_path, + ContentType=file_to_upload.content_type, + ) + self.user_repository.increment_model_submitted_count(user_id) + self.model_repository.create_new_model( + task_id=task_id, + user_id=user_id, + model_name=model_name, + shortname=model_name, + longdesc=description, + desc=description, + languages=languages, + license=license, + params=num_paramaters, + deployment_status="uploaded", + secret=secrets.token_hex(), + ) + background_tasks.add_task( + self.email_helper.send, + contact=user_email, + cc_contact=self.email_sender, + template_name="model_upload_successful.txt", + msg_dict={"name": model_name}, + subject=f"Model {model_name} upload succeeded.", + ) + return "Model upload successfully" + except Exception as e: + print(f"An unexpected error occurred: {e}") + return "Model upload failed" + def single_model_prediction_submit( self, model_url: str, diff --git a/backend/app/infrastructure/repositories/task.py b/backend/app/infrastructure/repositories/task.py index 9341cd75..ea3021be 100644 --- a/backend/app/infrastructure/repositories/task.py +++ b/backend/app/infrastructure/repositories/task.py @@ -30,6 +30,13 @@ def get_task_id_by_task_code(self, task_code: str): .first() ) + def get_s3_bucket_by_task_id(self, task_id: int): + return ( + self.session.query(self.model.s3_bucket) + .filter(self.model.id == task_id) + .first() + ) + def get_task_code_by_task_id(self, task_id: int): return ( self.session.query(self.model.task_code) diff --git a/frontends/web/src/new_front/pages/CreateSamples/CreateInterface.tsx b/frontends/web/src/new_front/pages/CreateSamples/CreateInterface.tsx index 33579197..50f8f4ea 100644 --- a/frontends/web/src/new_front/pages/CreateSamples/CreateInterface.tsx +++ b/frontends/web/src/new_front/pages/CreateSamples/CreateInterface.tsx @@ -42,7 +42,7 @@ const CreateInterface = () => { let { taskCode } = useParams<{ taskCode: string }>(); const { user } = useContext(UserContext); const { updateAmountExamplesCreatedToday } = useContext( - CreateInterfaceContext, + CreateInterfaceContext ); const history = useHistory(); const location = useLocation(); @@ -59,7 +59,7 @@ const CreateInterface = () => { { round_id: taskContextInfo?.real_round_id, user_id: user.id!, - }, + } ); if (!stillAllowedToSubmit) { Swal.fire({ @@ -89,13 +89,12 @@ const CreateInterface = () => { setTaskInfo(taskInfo); setTaskId(taskId); setIsGenerativeContext( - taskConfiguration.context.generative_context?.is_generative, + taskConfiguration.context.generative_context?.is_generative ); } }; const loadTaskContext = async () => { - console.log("in new method"); const configContext = taskConfiguration?.context as any; let needContext; if ("context_for_start" in configContext) { @@ -114,7 +113,7 @@ const CreateInterface = () => { const isLogin = async ( assignmentId: string | null, taskCode: string, - treatmentId: string | null, + treatmentId: string | null ) => { if (!user.id) { await checkUserIsLoggedIn( @@ -122,7 +121,7 @@ const CreateInterface = () => { `/`, assignmentId, taskCode, - treatmentId, + treatmentId ); } }; @@ -146,7 +145,7 @@ const CreateInterface = () => { if (taskContextInfo?.real_round_id) { updateAmountExamplesCreatedToday( taskContextInfo?.real_round_id, - user.id!, + user.id! ); } }, [taskContextInfo, user]); @@ -250,7 +249,7 @@ const CreateInterface = () => { isGenerativeContext={isGenerativeContext} userId={user.id!} accept_sandbox_creation={Boolean( - taskInfo.accept_sandbox_creation, + taskInfo.accept_sandbox_creation )} setModelOutput={setModelOutput} setIsGenerativeContext={setIsGenerativeContext} diff --git a/frontends/web/src/new_front/pages/SubmitModel/SubmitModel.tsx b/frontends/web/src/new_front/pages/SubmitModel/SubmitModel.tsx index 42c68a0b..bed5e4aa 100644 --- a/frontends/web/src/new_front/pages/SubmitModel/SubmitModel.tsx +++ b/frontends/web/src/new_front/pages/SubmitModel/SubmitModel.tsx @@ -51,15 +51,18 @@ const SubmitModel = () => { formData.append("user_id", user.id); formData.append("task_code", taskCode); formData.append("file_to_upload", modelData.file); - sendModelData(formData).then(() => { - setLoading({ loading: true, text: "Done" }); - Swal.fire({ - title: "Good job!", - text: "Your model will be uploaded and soon you will be able to see the results in the dynaboard (you will receive an email!)", - icon: "success", - confirmButtonColor: "#007bff", - }); - }); + + sendModelData(formData, configYaml.evaluation?.type === "heavy").then( + () => { + setLoading({ loading: true, text: "Done" }); + Swal.fire({ + title: "Good job!", + text: "Your model will be uploaded and soon you will be able to see the results in the dynaboard (you will receive an email!)", + icon: "success", + confirmButtonColor: "#007bff", + }); + } + ); } else { setLoadingFile(true); alert("Please upload a model"); @@ -70,12 +73,12 @@ const SubmitModel = () => { const getTaskData = async () => { const taskId = await get(`/task/get_task_id_by_task_code/${taskCode}`); const taskData = await get( - `/task/get_task_with_round_info_by_task_id/${taskId}`, + `/task/get_task_with_round_info_by_task_id/${taskId}` ); const dynalabModel = await get(`/model/get_dynalab_model/${taskCode}`); if (response.ok) { setConfigYaml( - JSON.parse(JSON.stringify(yaml.load(taskData.config_yaml))), + JSON.parse(JSON.stringify(yaml.load(taskData.config_yaml))) ); setDynalabModel(dynalabModel); } diff --git a/frontends/web/src/new_front/pages/SubmitModel/useUploadFile.ts b/frontends/web/src/new_front/pages/SubmitModel/useUploadFile.ts index 2d440c46..904b8e26 100644 --- a/frontends/web/src/new_front/pages/SubmitModel/useUploadFile.ts +++ b/frontends/web/src/new_front/pages/SubmitModel/useUploadFile.ts @@ -5,11 +5,16 @@ import Swal from "sweetalert2"; const useUploadFile = () => { const [progress, setProgress] = useState(0); - const sendModelData = (formData: FormData) => { + const sendModelData = (formData: FormData, heavy: boolean = false) => { + const url = `${process.env.REACT_APP_API_HOST_2}${ + heavy + ? "/model/heavy_evaluation" + : "/model/upload_model_to_s3_and_evaluate" + }`; return axios .request({ method: "post", - url: `${process.env.REACT_APP_API_HOST_2}/model/upload_model_to_s3_and_evaluate`, + url: url, data: formData, onUploadProgress: (p) => { setProgress(p.loaded / p.total);