From 0d3d11a8dcf8156d14ea4ad0f4b8c9120bcd1e85 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Tue, 28 Jan 2025 12:47:55 -0300 Subject: [PATCH 1/6] adds deploy_model functionality to workspace --- roboflow/core/workspace.py | 70 +++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index ebd20401..952cafd3 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -10,11 +10,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 @@ -565,6 +566,73 @@ def active_learning( return ( 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() From bce71bce00cad7dd831cfd7491d173d6033ffc12 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Tue, 28 Jan 2025 12:48:49 -0300 Subject: [PATCH 2/6] adds the ability of specific n projects and make the version option on models cli upload --- roboflow/roboflowpy.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/roboflow/roboflowpy.py b/roboflow/roboflowpy.py index 3e6929fe..d68bdaa9 100755 --- a/roboflow/roboflowpy.py +++ b/roboflow/roboflowpy.py @@ -79,10 +79,20 @@ def upload_model(args): rf = roboflow.Roboflow(args.api_key) workspace = rf.workspace(args.workspace) - # 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)) + 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): @@ -479,13 +489,15 @@ 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", + help="version number to upload the model to (optional)", + default=None, ) upload_model_parser.add_argument( "-t", @@ -503,6 +515,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) From 7fb7a92695f732c57aaa9ef2b2854350da087da7 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Tue, 28 Jan 2025 12:49:09 -0300 Subject: [PATCH 3/6] bump version --- roboflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/__init__.py b/roboflow/__init__.py index 177bf8fd..699c2b72 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -15,7 +15,7 @@ from roboflow.models import CLIPModel, GazeModel # noqa: F401 from roboflow.util.general import write_line -__version__ = "1.1.52" +__version__ = "1.1.53" def check_key(api_key, model, notebook, num_retries=0): From 3535482abe2dd3b2a15cc8f054f550411bb6d557 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:50:33 +0000 Subject: [PATCH 4/6] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto=20?= =?UTF-8?q?format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/core/workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index 952cafd3..07daf1ab 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -566,7 +566,7 @@ def active_learning( return ( prediction_results if type(raw_data_location) is not np.ndarray else prediction_results[-1]["predictions"] ) - + def deploy_model( self, model_type: str, From b31647bf242bfcb022759a5715ee199b22b5f27f Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Tue, 28 Jan 2025 12:53:28 -0300 Subject: [PATCH 5/6] ruff --- roboflow/core/workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index 952cafd3..07daf1ab 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -566,7 +566,7 @@ def active_learning( return ( prediction_results if type(raw_data_location) is not np.ndarray else prediction_results[-1]["predictions"] ) - + def deploy_model( self, model_type: str, From 5bd9b37b651c26fa272c2b5450d7e382bf62c2c2 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Tue, 28 Jan 2025 12:55:31 -0300 Subject: [PATCH 6/6] adds annotations --- roboflow/core/workspace.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index 07daf1ab..815c494f 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