diff --git a/roboflow/core/version.py b/roboflow/core/version.py index 1ffe74e4..4a2904f5 100644 --- a/roboflow/core/version.py +++ b/roboflow/core/version.py @@ -3,14 +3,12 @@ import copy import json import os -import shutil import sys import time import zipfile from typing import TYPE_CHECKING, Optional, Union import requests -import yaml from dotenv import load_dotenv from tqdm import tqdm @@ -34,7 +32,8 @@ from roboflow.models.semantic_segmentation import SemanticSegmentationModel from roboflow.util.annotations import amend_data_yaml from roboflow.util.general import write_line -from roboflow.util.versions import get_wrong_dependencies_versions, print_warn_for_wrong_dependencies_versions +from roboflow.util.model_processor import process +from roboflow.util.versions import get_wrong_dependencies_versions if TYPE_CHECKING: import numpy as np @@ -478,294 +477,14 @@ def deploy(self, model_type: str, model_path: str, filename: str = "weights/best model_path (str): File path to the model weights to be uploaded. filename (str, optional): The name of the weights file. Defaults to "weights/best.pt". """ - if model_type.startswith("yolo11"): - model_type = model_type.replace("yolo11", "yolov11") - - supported_models = [ - "yolov5", - "yolov7-seg", - "yolov8", - "yolov9", - "yolonas", - "paligemma", - "paligemma2", - "yolov10", - "florence-2", - "yolov11", - ] - - if not any(supported_model in model_type for supported_model in supported_models): - raise (ValueError(f"Model type {model_type} not supported. Supported models are" f" {supported_models}")) - - if model_type.startswith(("paligemma", "paligemma2", "florence-2")): - if any(model in model_type for model in ["paligemma", "paligemma2", "florence-2"]): - supported_hf_types = [ - "florence-2-base", - "florence-2-large", - "paligemma-3b-pt-224", - "paligemma-3b-pt-448", - "paligemma-3b-pt-896", - "paligemma2-3b-pt-224", - "paligemma2-3b-pt-448", - "paligemma2-3b-pt-896", - ] - if model_type not in supported_hf_types: - raise RuntimeError( - f"{model_type} not supported for this type of upload." - f"Supported upload types are {supported_hf_types}" - ) - self.deploy_huggingface(model_type, model_path, filename) - return - - if "yolonas" in model_type: - self.deploy_yolonas(model_type, model_path, filename) - return - - if "yolov8" in model_type: - try: - import torch - import ultralytics - - except ImportError: - raise RuntimeError( - "The ultralytics python package is required to deploy yolov8" - " models. Please install it with `pip install ultralytics`" - ) - - print_warn_for_wrong_dependencies_versions([("ultralytics", "==", "8.0.196")], ask_to_continue=True) - - elif "yolov10" in model_type: - try: - import torch - import ultralytics - - except ImportError: - raise RuntimeError( - "The ultralytics python package is required to deploy yolov10" - " models. Please install it with `pip install ultralytics`" - ) - - elif "yolov5" in model_type or "yolov7" in model_type or "yolov9" in model_type: - try: - import torch - except ImportError: - raise RuntimeError( - "The torch python package is required to deploy yolov5 models." - " Please install it with `pip install torch`" - ) - - elif "yolov11" in model_type: - try: - import torch - import ultralytics - - except ImportError: - raise RuntimeError( - "The ultralytics python package is required to deploy yolov10" - " models. Please install it with `pip install ultralytics`" - ) + zip_file_name = process(model_type, model_path, filename) - print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) - - model = torch.load(os.path.join(model_path, filename)) - - if isinstance(model["model"].names, list): - class_names = model["model"].names - else: - class_names = [] - for i, val in enumerate(model["model"].names): - class_names.append((val, model["model"].names[val])) - class_names.sort(key=lambda x: x[0]) - class_names = [x[1] for x in class_names] - - if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: - # try except for backwards compatibility with older versions of ultralytics - if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): - nc = model["model"].yaml["nc"] - args = model["train_args"] - else: - nc = model["model"].nc - args = model["model"].args - try: - model_artifacts = { - "names": class_names, - "yaml": model["model"].yaml, - "nc": nc, - "args": {k: val for k, val in args.items() if ((k == "model") or (k == "imgsz") or (k == "batch"))}, - "ultralytics_version": ultralytics.__version__, - "model_type": model_type, - } - except Exception: - model_artifacts = { - "names": class_names, - "yaml": model["model"].yaml, - "nc": nc, - "args": { - k: val - for k, val in args.__dict__.items() - if ((k == "model") or (k == "imgsz") or (k == "batch")) - }, - "ultralytics_version": ultralytics.__version__, - "model_type": model_type, - } - elif "yolov5" in model_type or "yolov7" in model_type or "yolov9" in model_type: - # parse from yaml for yolov5 - - with open(os.path.join(model_path, "opt.yaml")) as stream: - opts = yaml.safe_load(stream) - - model_artifacts = { - "names": class_names, - "nc": model["model"].nc, - "args": { - "imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"], - "batch": opts["batch_size"], - }, - "model_type": model_type, - } - if hasattr(model["model"], "yaml"): - model_artifacts["yaml"] = model["model"].yaml - - with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp: - json.dump(model_artifacts, fp) - - torch.save(model["model"].state_dict(), os.path.join(model_path, "state_dict.pt")) - - list_files = [ - "results.csv", - "results.png", - "model_artifacts.json", - "state_dict.pt", - ] - - with zipfile.ZipFile(os.path.join(model_path, "roboflow_deploy.zip"), "w") as zipMe: - for file in list_files: - if os.path.exists(os.path.join(model_path, file)): - zipMe.write( - os.path.join(model_path, file), - arcname=file, - compress_type=zipfile.ZIP_DEFLATED, - ) - else: - if file in ["model_artifacts.json", "state_dict.pt"]: - raise (ValueError(f"File {file} not found. Please make sure to provide a" " valid model path.")) - - self.upload_zip(model_type, model_path) - - def deploy_huggingface( - self, model_type: str, model_path: str, filename: str = "fine-tuned-paligemma-3b-pt-224.f16.npz" - ) -> None: - # Check if model_path exists - if not os.path.exists(model_path): - raise FileNotFoundError(f"Model path {model_path} does not exist.") - model_files = os.listdir(model_path) - print(f"Model files found in {model_path}: {model_files}") - - files_to_deploy = [] - - # Find first .npz file in model_path - npz_filename = next((file for file in model_files if file.endswith(".npz")), None) - if any([file.endswith(".safetensors") for file in model_files]): - print(f"Found .safetensors file in model path. Deploying PyTorch {model_type} model.") - necessary_files = [ - "preprocessor_config.json", - "special_tokens_map.json", - "tokenizer_config.json", - "tokenizer.json", - ] - for file in necessary_files: - if file not in model_files: - print("Missing necessary file", file) - res = input("Do you want to continue? (y/n)") - if res.lower() != "y": - exit(1) - for file in model_files: - files_to_deploy.append(file) - elif npz_filename is not None: - print(f"Found .npz file {npz_filename} in model path. Deploying JAX PaliGemma model.") - files_to_deploy.append(npz_filename) - else: - raise FileNotFoundError(f"No .npz or .safetensors file found in model path {model_path}.") - - if len(files_to_deploy) == 0: - raise FileNotFoundError(f"No valid files found in model path {model_path}.") - print(f"Zipping files for deploy: {files_to_deploy}") - - import tarfile - - with tarfile.open(os.path.join(model_path, "roboflow_deploy.tar"), "w") as tar: - for file in files_to_deploy: - tar.add(os.path.join(model_path, file), arcname=file) - - print("Uploading to Roboflow... May take several minutes.") - self.upload_zip(model_type, model_path, "roboflow_deploy.tar") - - def deploy_yolonas(self, model_type: str, model_path: str, filename: str = "weights/best.pt") -> None: - try: - import torch - except ImportError: - raise RuntimeError( - "The torch python package is required to deploy yolonas models." - " Please install it with `pip install torch`" - ) - - model = torch.load(os.path.join(model_path, filename), map_location="cpu") - class_names = model["processing_params"]["class_names"] - - opt_path = os.path.join(model_path, "opt.yaml") - if not os.path.exists(opt_path): - raise RuntimeError( - f"You must create an opt.yaml file at {os.path.join(model_path, '')} of the format:\n" - f"imgsz: \n" - f"batch_size: \n" - f"architecture: \n" - ) - with open(os.path.join(model_path, "opt.yaml")) as stream: - opts = yaml.safe_load(stream) - required_keys = ["imgsz", "batch_size", "architecture"] - for key in required_keys: - if key not in opts: - raise RuntimeError(f"{opt_path} lacks required key {key}. Required keys: {required_keys}") - - model_artifacts = { - "names": class_names, - "nc": len(class_names), - "args": { - "imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"], - "batch": opts["batch_size"], - "architecture": opts["architecture"], - }, - "model_type": model_type, - } - - with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp: - json.dump(model_artifacts, fp) - - shutil.copy(os.path.join(model_path, filename), os.path.join(model_path, "state_dict.pt")) - - list_files = [ - "results.json", - "results.png", - "model_artifacts.json", - "state_dict.pt", - ] - - with zipfile.ZipFile(os.path.join(model_path, "roboflow_deploy.zip"), "w") as zipMe: - for file in list_files: - if os.path.exists(os.path.join(model_path, file)): - zipMe.write( - os.path.join(model_path, file), - arcname=file, - compress_type=zipfile.ZIP_DEFLATED, - ) - else: - if file in ["model_artifacts.json", filename]: - raise (ValueError(f"File {file} not found. Please make sure to provide a" " valid model path.")) + if zip_file_name is None: + raise RuntimeError("Failed to process model") - self.upload_zip(model_type, model_path) + self._upload_zip(model_type, model_path, zip_file_name) - def upload_zip(self, model_type: str, model_path: str, model_file_name: str = "roboflow_deploy.zip"): + def _upload_zip(self, model_type: str, model_path: str, model_file_name: str): res = requests.get( f"{API_URL}/{self.workspace}/{self.project}/{self.version}" f"/uploadModel?api_key={self.__api_key}&modelType={model_type}&nocache=true" diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index 9083c5d8..9e861f41 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import concurrent.futures import glob import json @@ -10,11 +12,12 @@ from roboflow.adapters import rfapi from roboflow.adapters.rfapi import AnnotationSaveError, ImageUploadError, RoboflowError -from roboflow.config import API_URL, CLIP_FEATURIZE_URL, DEMO_KEYS +from roboflow.config import API_URL, APP_URL, CLIP_FEATURIZE_URL, DEMO_KEYS from roboflow.core.project import Project from roboflow.util import folderparser from roboflow.util.active_learning_utils import check_box_size, clip_encode, count_comparisons from roboflow.util.image_utils import load_labelmap +from roboflow.util.model_processor import process from roboflow.util.two_stage_utils import ocr_infer @@ -566,6 +569,74 @@ def active_learning( prediction_results if type(raw_data_location) is not np.ndarray else prediction_results[-1]["predictions"] ) + def deploy_model( + self, + model_type: str, + model_path: str, + project_ids: list[str], + model_name: str, + filename: str = "weights/best.pt", + ): + """Uploads provided weights file to Roboflow. + + Args: + model_type (str): The type of the model to be deployed. + model_path (str): File path to the model weights to be uploaded. + project_ids (list[str]): List of project IDs to deploy the model to. + filename (str, optional): The name of the weights file. Defaults to "weights/best.pt". + """ + + if not project_ids: + raise ValueError("At least one project ID must be provided") + + # Validate if provided project URLs belong to user's projects + user_projects = set(project.split("/")[-1] for project in self.projects()) + for project_id in project_ids: + if project_id not in user_projects: + raise ValueError(f"Project {project_id} is not accessible in this workspace") + + zip_file_name = process(model_type, model_path, filename) + + if zip_file_name is None: + raise RuntimeError("Failed to process model") + + self._upload_zip(model_type, model_path, project_ids, model_name, zip_file_name) + + def _upload_zip( + self, + model_type: str, + model_path: str, + project_ids: list[str], + model_name: str, + model_file_name: str, + ): + # This endpoint returns a signed URL to upload the model + res = requests.post( + f"{API_URL}/{self.url}/models/prepareUpload?api_key={self.__api_key}&modelType={model_type}&modelName={model_name}&projectIds={','.join(project_ids)}&nocache=true" + ) + try: + res.raise_for_status() + except Exception as e: + print(f"An error occured when getting the model deployment URL: {e}") + return + + # Upload the model to the signed URL + res = requests.put( + res.json()["url"], + data=open(os.path.join(model_path, model_file_name), "rb"), + ) + try: + res.raise_for_status() + + for project_id in project_ids: + print( + f"View the status of your deployment for project {project_id} at:" + f" {APP_URL}/{self.url}/{project_id}/models" + ) + + except Exception as e: + print(f"An error occured when uploading the model: {e}") + def __str__(self): projects = self.projects() json_value = {"name": self.name, "url": self.url, "projects": projects} diff --git a/roboflow/roboflowpy.py b/roboflow/roboflowpy.py index 86e00832..b205e5ce 100755 --- a/roboflow/roboflowpy.py +++ b/roboflow/roboflowpy.py @@ -16,7 +16,10 @@ def login(args): - roboflow.login() + if args.force: + roboflow.login(force=True) + else: + roboflow.login() def _parse_url(url): @@ -78,10 +81,21 @@ def upload_image(args): def upload_model(args): rf = roboflow.Roboflow(args.api_key) workspace = rf.workspace(args.workspace) - project = workspace.project(args.project) - version = project.version(args.version_number) - print(args.model_type, args.model_path, args.filename) - version.deploy(str(args.model_type), str(args.model_path), str(args.filename)) + + if args.version_number is not None: + # Deploy to specific version + project = workspace.project(args.project) + version = project.version(args.version_number) + version.deploy(str(args.model_type), str(args.model_path), str(args.filename)) + else: + # Deploy to multiple projects + workspace.deploy_model( + model_type=str(args.model_type), + model_path=str(args.model_path), + project_ids=args.project, + model_name=str(args.model_name), + filename=str(args.filename), + ) def list_projects(args): @@ -478,13 +492,11 @@ def _add_upload_model_parser(subparsers): upload_model_parser.add_argument( "-p", dest="project", - help="project_id to upload the model into", + action="append", # Allow multiple projects + help="project_id to upload the model into (can be specified multiple times)", ) upload_model_parser.add_argument( - "-v", - dest="version_number", - type=int, - help="version number to upload the model to", + "-v", dest="version_number", type=int, help="version number to upload the model to (optional)", default=None ) upload_model_parser.add_argument( "-t", @@ -502,6 +514,11 @@ def _add_upload_model_parser(subparsers): default="weights/best.pt", help="name of the model file", ) + upload_model_parser.add_argument( + "-n", + dest="model_name", + help="name of the model", + ) upload_model_parser.set_defaults(func=upload_model) @@ -536,6 +553,12 @@ def _add_get_workspace_project_version_parser(subparsers): def _add_login_parser(subparsers): login_parser = subparsers.add_parser("login", help="Log in to Roboflow") + login_parser.add_argument( + "-f", + dest="force", + help="force login", + action="store_true", + ) login_parser.set_defaults(func=login) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py new file mode 100644 index 00000000..15f3e0c4 --- /dev/null +++ b/roboflow/util/model_processor.py @@ -0,0 +1,312 @@ +import json +import os +import shutil +import zipfile +from typing import Callable + +import yaml + +from roboflow.util.versions import print_warn_for_wrong_dependencies_versions + + +def process(model_type: str, model_path: str, filename: str) -> str: + processor = _get_processor_function(model_type) + return processor(model_type, model_path, filename) + + +def _get_processor_function(model_type: str) -> Callable: + if model_type.startswith("yolo11"): + model_type = model_type.replace("yolo11", "yolov11") + + supported_models = [ + "yolov5", + "yolov7-seg", + "yolov8", + "yolov9", + "yolonas", + "paligemma", + "paligemma2", + "yolov10", + "florence-2", + "yolov11", + ] + + if not any(supported_model in model_type for supported_model in supported_models): + raise (ValueError(f"Model type {model_type} not supported. Supported models are" f" {supported_models}")) + + if model_type.startswith(("paligemma", "paligemma2", "florence-2")): + if any(model in model_type for model in ["paligemma", "paligemma2", "florence-2"]): + supported_hf_types = [ + "florence-2-base", + "florence-2-large", + "paligemma-3b-pt-224", + "paligemma-3b-pt-448", + "paligemma-3b-pt-896", + "paligemma2-3b-pt-224", + "paligemma2-3b-pt-448", + "paligemma2-3b-pt-896", + ] + if model_type not in supported_hf_types: + raise RuntimeError( + f"{model_type} not supported for this type of upload." + f"Supported upload types are {supported_hf_types}" + ) + return _process_huggingface + + if "yolonas" in model_type: + return _process_yolonas + + return _process_yolo + + +def _process_yolo(model_type: str, model_path: str, filename: str) -> str: + if "yolov8" in model_type: + try: + import torch + import ultralytics + + except ImportError: + raise RuntimeError( + "The ultralytics python package is required to deploy yolov8" + " models. Please install it with `pip install ultralytics`" + ) + + print_warn_for_wrong_dependencies_versions([("ultralytics", "==", "8.0.196")], ask_to_continue=True) + + elif "yolov10" in model_type: + try: + import torch + import ultralytics + + except ImportError: + raise RuntimeError( + "The ultralytics python package is required to deploy yolov10" + " models. Please install it with `pip install ultralytics`" + ) + + elif "yolov5" in model_type or "yolov7" in model_type or "yolov9" in model_type: + try: + import torch + except ImportError: + raise RuntimeError( + f"The torch python package is required to deploy {model_type} models." + " Please install it with `pip install torch`" + ) + + elif "yolov11" in model_type: + try: + import torch + import ultralytics + + except ImportError: + raise RuntimeError( + "The ultralytics python package is required to deploy yolov11" + " models. Please install it with `pip install ultralytics`" + ) + + print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) + + model = torch.load(os.path.join(model_path, filename)) + + if isinstance(model["model"].names, list): + class_names = model["model"].names + else: + class_names = [] + for i, val in enumerate(model["model"].names): + class_names.append((val, model["model"].names[val])) + class_names.sort(key=lambda x: x[0]) + class_names = [x[1] for x in class_names] + + if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: + # try except for backwards compatibility with older versions of ultralytics + if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): + nc = model["model"].yaml["nc"] + args = model["train_args"] + else: + nc = model["model"].nc + args = model["model"].args + try: + model_artifacts = { + "names": class_names, + "yaml": model["model"].yaml, + "nc": nc, + "args": {k: val for k, val in args.items() if ((k == "model") or (k == "imgsz") or (k == "batch"))}, + "ultralytics_version": ultralytics.__version__, + "model_type": model_type, + } + except Exception: + model_artifacts = { + "names": class_names, + "yaml": model["model"].yaml, + "nc": nc, + "args": { + k: val for k, val in args.__dict__.items() if ((k == "model") or (k == "imgsz") or (k == "batch")) + }, + "ultralytics_version": ultralytics.__version__, + "model_type": model_type, + } + elif "yolov5" in model_type or "yolov7" in model_type or "yolov9" in model_type: + # parse from yaml for yolov5 + + with open(os.path.join(model_path, "opt.yaml")) as stream: + opts = yaml.safe_load(stream) + + model_artifacts = { + "names": class_names, + "nc": model["model"].nc, + "args": { + "imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"], + "batch": opts["batch_size"], + }, + "model_type": model_type, + } + if hasattr(model["model"], "yaml"): + model_artifacts["yaml"] = model["model"].yaml + + with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp: + json.dump(model_artifacts, fp) + + torch.save(model["model"].state_dict(), os.path.join(model_path, "state_dict.pt")) + + list_files = [ + "results.csv", + "results.png", + "model_artifacts.json", + "state_dict.pt", + ] + + zip_file_name = "roboflow_deploy.zip" + + with zipfile.ZipFile(os.path.join(model_path, zip_file_name), "w") as zipMe: + for file in list_files: + if os.path.exists(os.path.join(model_path, file)): + zipMe.write( + os.path.join(model_path, file), + arcname=file, + compress_type=zipfile.ZIP_DEFLATED, + ) + else: + if file in ["model_artifacts.json", "state_dict.pt"]: + raise (ValueError(f"File {file} not found. Please make sure to provide a" " valid model path.")) + + return zip_file_name + + +def _process_huggingface( + model_type: str, model_path: str, filename: str = "fine-tuned-paligemma-3b-pt-224.f16.npz" +) -> str: + # Check if model_path exists + if not os.path.exists(model_path): + raise FileNotFoundError(f"Model path {model_path} does not exist.") + model_files = os.listdir(model_path) + print(f"Model files found in {model_path}: {model_files}") + + files_to_deploy = [] + + # Find first .npz file in model_path + npz_filename = next((file for file in model_files if file.endswith(".npz")), None) + if any([file.endswith(".safetensors") for file in model_files]): + print(f"Found .safetensors file in model path. Deploying PyTorch {model_type} model.") + necessary_files = [ + "preprocessor_config.json", + "special_tokens_map.json", + "tokenizer_config.json", + "tokenizer.json", + ] + for file in necessary_files: + if file not in model_files: + print("Missing necessary file", file) + res = input("Do you want to continue? (y/n)") + if res.lower() != "y": + exit(1) + for file in model_files: + files_to_deploy.append(file) + elif npz_filename is not None: + print(f"Found .npz file {npz_filename} in model path. Deploying JAX PaliGemma model.") + files_to_deploy.append(npz_filename) + else: + raise FileNotFoundError(f"No .npz or .safetensors file found in model path {model_path}.") + + if len(files_to_deploy) == 0: + raise FileNotFoundError(f"No valid files found in model path {model_path}.") + print(f"Zipping files for deploy: {files_to_deploy}") + + import tarfile + + tar_file_name = "roboflow_deploy.tar" + + with tarfile.open(os.path.join(model_path, tar_file_name), "w") as tar: + for file in files_to_deploy: + tar.add(os.path.join(model_path, file), arcname=file) + + print("Uploading to Roboflow... May take several minutes.") + + return tar_file_name + + +def _process_yolonas(model_type: str, model_path: str, filename: str = "weights/best.pt") -> str: + try: + import torch + except ImportError: + raise RuntimeError( + "The torch python package is required to deploy yolonas models." + " Please install it with `pip install torch`" + ) + + model = torch.load(os.path.join(model_path, filename), map_location="cpu") + class_names = model["processing_params"]["class_names"] + + opt_path = os.path.join(model_path, "opt.yaml") + if not os.path.exists(opt_path): + raise RuntimeError( + f"You must create an opt.yaml file at {os.path.join(model_path, '')} of the format:\n" + f"imgsz: \n" + f"batch_size: \n" + f"architecture: \n" + ) + with open(os.path.join(model_path, "opt.yaml")) as stream: + opts = yaml.safe_load(stream) + required_keys = ["imgsz", "batch_size", "architecture"] + for key in required_keys: + if key not in opts: + raise RuntimeError(f"{opt_path} lacks required key {key}. Required keys: {required_keys}") + + model_artifacts = { + "names": class_names, + "nc": len(class_names), + "args": { + "imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"], + "batch": opts["batch_size"], + "architecture": opts["architecture"], + }, + "model_type": model_type, + } + + with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp: + json.dump(model_artifacts, fp) + + shutil.copy(os.path.join(model_path, filename), os.path.join(model_path, "state_dict.pt")) + + list_files = [ + "results.json", + "results.png", + "model_artifacts.json", + "state_dict.pt", + ] + + zip_file_name = "roboflow_deploy.zip" + + with zipfile.ZipFile(os.path.join(model_path, zip_file_name), "w") as zipMe: + for file in list_files: + if os.path.exists(os.path.join(model_path, file)): + zipMe.write( + os.path.join(model_path, file), + arcname=file, + compress_type=zipfile.ZIP_DEFLATED, + ) + else: + if file in ["model_artifacts.json", filename]: + raise (ValueError(f"File {file} not found. Please make sure to provide a" " valid model path.")) + + return zip_file_name