From d20028080b63b89da998260a252c0ec7605775d7 Mon Sep 17 00:00:00 2001 From: Rodrigo Barbosa Date: Thu, 24 Sep 2026 20:43:18 -0300 Subject: [PATCH 1/2] Support Action Recognition projects and Cosmos training in the SDK get_model_format fell back to yolov5pytorch for Cosmos, so version.train exported the wrong format. Map cosmos3-edge to video-coco and its image sibling cosmos3-edge-vlm to jsonl, matching the server's model registry. Add the action-recognition project type to config and the CLI. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RXBUpHfmPSeNYibcqpPnpY --- CHANGELOG.md | 11 +++++++++++ roboflow/__init__.py | 2 +- roboflow/cli/handlers/project.py | 1 + roboflow/config.py | 1 + roboflow/util/versions.py | 3 +++ tests/cli/test_project_handler.py | 20 ++++++++++++++++++++ tests/util/test_versions.py | 2 ++ 7 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9f2bee6..fe2a11a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. +## 1.5.2 + +### Added + +- Action recognition support: + - `roboflow project create --type action-recognition` creates an action + recognition project. `roboflow.config.TYPE_ACTION_RECOGNITION` names the type. + - `Version.train(model_type="cosmos3-edge")` now exports `video-coco`, the + format the server trains Cosmos on. `cosmos3-edge-vlm` exports `jsonl`. + Both previously fell back to `yolov5pytorch`. + ## 1.5.1 ### Added diff --git a/roboflow/__init__.py b/roboflow/__init__.py index 6c556500..f829fd0c 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -21,7 +21,7 @@ CLIPModel = None # type: ignore[assignment,misc] GazeModel = None # type: ignore[assignment,misc] -__version__ = "1.5.1" +__version__ = "1.5.2" def check_key(api_key, model, notebook, num_retries=0): diff --git a/roboflow/cli/handlers/project.py b/roboflow/cli/handlers/project.py index fd072dc0..05e3070d 100644 --- a/roboflow/cli/handlers/project.py +++ b/roboflow/cli/handlers/project.py @@ -19,6 +19,7 @@ class ProjectType(str, Enum): instance_segmentation = "instance-segmentation" semantic_segmentation = "semantic-segmentation" keypoint_detection = "keypoint-detection" + action_recognition = "action-recognition" project_app = typer.Typer(cls=SortedGroup, help="Manage projects", no_args_is_help=True) diff --git a/roboflow/config.py b/roboflow/config.py index 800ef6ac..60147af6 100644 --- a/roboflow/config.py +++ b/roboflow/config.py @@ -74,6 +74,7 @@ def get_conditional_configuration_variable(key, default): TYPE_SEMANTIC_SEGMENTATION = "semantic-segmentation" TYPE_KEYPOINT_DETECTION = "keypoint-detection" TYPE_TEXT_IMAGE_PAIRS = "text-image-pairs" +TYPE_ACTION_RECOGNITION = "action-recognition" TASK_DET = "det" TASK_SEG = "seg" diff --git a/roboflow/util/versions.py b/roboflow/util/versions.py index f07bf006..b09289f4 100644 --- a/roboflow/util/versions.py +++ b/roboflow/util/versions.py @@ -126,6 +126,9 @@ def get_model_format(model_type: str) -> str: "rfdetr": "coco", "rf-detr": "coco", "deep": "png-mask-semantic", + # Substring match: the image VLM sibling must win before the video model's prefix. + "cosmos3-edge-vlm": "jsonl", + "cosmos3-edge": "video-coco", } for prefix, format in model_formats.items(): diff --git a/tests/cli/test_project_handler.py b/tests/cli/test_project_handler.py index 129a90a0..b3e58f06 100644 --- a/tests/cli/test_project_handler.py +++ b/tests/cli/test_project_handler.py @@ -50,6 +50,26 @@ def test_subcommands_visible(self) -> None: self.assertIn("restore", result.output) +class TestProjectCreateHandler(unittest.TestCase): + """project create passes the chosen type through to Workspace.create_project.""" + + def test_create_action_recognition_project(self) -> None: + from unittest.mock import MagicMock, patch + + workspace = MagicMock() + with patch("roboflow.Roboflow") as mock_rf: + mock_rf.return_value.workspace.return_value = workspace + result = runner.invoke(app, ["project", "create", "Clips", "--type", "action-recognition"]) + + self.assertEqual(result.exit_code, 0, result.output) + workspace.create_project.assert_called_once_with( + project_name="Clips", + project_type="action-recognition", + project_license="Private", + annotation="Clips", + ) + + class TestProjectDeleteHandler(unittest.TestCase): """project delete calls rfapi.delete_project and honors --yes.""" diff --git a/tests/util/test_versions.py b/tests/util/test_versions.py index 5a7803f2..6a9d6bef 100644 --- a/tests/util/test_versions.py +++ b/tests/util/test_versions.py @@ -36,6 +36,8 @@ def test_get_model_format_with_various_ids(self): ("resnet14", "folder"), ("resenet38", "yolov5pytorch"), ("invlid-type", "yolov5pytorch"), + ("cosmos3-edge", "video-coco"), + ("cosmos3-edge-vlm", "jsonl"), ] for model_type, expected_format in cases: From 5e3fc482dcd5c732292058b8e306b6675f67661e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 25 Sep 2026 21:09:41 +0000 Subject: [PATCH 2/2] Drop the mocked project create CLI test The test mocked Roboflow and the workspace to check one enum value. The enum itself shows the new type, and the other CLI tests keep covering the command's registration. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YFK32iFwJmoResQA754RPr --- tests/cli/test_project_handler.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/cli/test_project_handler.py b/tests/cli/test_project_handler.py index b3e58f06..129a90a0 100644 --- a/tests/cli/test_project_handler.py +++ b/tests/cli/test_project_handler.py @@ -50,26 +50,6 @@ def test_subcommands_visible(self) -> None: self.assertIn("restore", result.output) -class TestProjectCreateHandler(unittest.TestCase): - """project create passes the chosen type through to Workspace.create_project.""" - - def test_create_action_recognition_project(self) -> None: - from unittest.mock import MagicMock, patch - - workspace = MagicMock() - with patch("roboflow.Roboflow") as mock_rf: - mock_rf.return_value.workspace.return_value = workspace - result = runner.invoke(app, ["project", "create", "Clips", "--type", "action-recognition"]) - - self.assertEqual(result.exit_code, 0, result.output) - workspace.create_project.assert_called_once_with( - project_name="Clips", - project_type="action-recognition", - project_license="Private", - annotation="Clips", - ) - - class TestProjectDeleteHandler(unittest.TestCase): """project delete calls rfapi.delete_project and honors --yes."""