From 6f23b030b6d1df81f2c3c05a32b75e5780610fc3 Mon Sep 17 00:00:00 2001 From: Lee Clement Date: Fri, 29 May 2026 17:17:28 -0230 Subject: [PATCH 1/3] fix(rfdetr): accept dict-form checkpoint args in classname extraction `get_classnames_txt_for_rfdetr` called `vars(checkpoint["args"])`, which raises TypeError when `args` is a plain dict rather than an argparse.Namespace (e.g. rf-detr EMA checkpoints). Normalize to a dict before lookups. Add tests covering both shapes. Co-Authored-By: Claude Opus 4.8 --- requirements-slim.txt | 6 +++++- requirements.txt | 7 ++++++- roboflow/util/model_processor.py | 4 +++- tests/util/test_model_processor.py | 20 ++++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/requirements-slim.txt b/requirements-slim.txt index 9709e296..a1cfbfec 100644 --- a/requirements-slim.txt +++ b/requirements-slim.txt @@ -6,7 +6,11 @@ tqdm>=4.41.0 PyYAML>=5.3.1 requests_toolbelt filetype -typer>=0.12.0 +# typer 0.26 vendors click as typer._click and drops the external click dep, breaking +# roboflow.cli imports (test_slim_compat imports it). 0.25.x still uses external click. +typer>=0.12.0,<0.26 +# CLI imports click directly; declare it explicitly rather than relying on typer's transitive dep. +click>=8.0 python-dateutil python-dotenv six diff --git a/requirements.txt b/requirements.txt index 79cd4bb2..33e60989 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,4 +19,9 @@ tqdm>=4.41.0 PyYAML>=5.3.1 requests_toolbelt filetype -typer>=0.12.0 +# typer 0.26 vendors its own click as typer._click and drops the external click dep, which +# breaks roboflow/cli/_compat.py's SortedGroup (it subclasses TyperGroup using external click +# types). 0.25.x still depends on external click and type-checks; pin below 0.26. +typer>=0.12.0,<0.26 +# CLI imports click directly; declare it explicitly rather than relying on typer's transitive dep. +click>=8.0 diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 674170ae..4ff0ce1a 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -414,7 +414,9 @@ def get_classnames_txt_for_rfdetr(model_path: str, pt_file: str, checkpoint=None import torch checkpoint = torch.load(os.path.join(model_path, pt_file), map_location="cpu", weights_only=False) - args = vars(checkpoint["args"]) + raw_args = checkpoint["args"] + # args may be a plain dict in some checkpoints + args = raw_args if isinstance(raw_args, dict) else vars(raw_args) if "class_names" in args: with open(class_names_path, "w") as f: for class_name in args["class_names"]: diff --git a/tests/util/test_model_processor.py b/tests/util/test_model_processor.py index 951abe29..37ecb186 100644 --- a/tests/util/test_model_processor.py +++ b/tests/util/test_model_processor.py @@ -1,3 +1,5 @@ +import os +import tempfile import unittest from types import SimpleNamespace @@ -5,6 +7,7 @@ from roboflow.util.model_processor import ( _detect_rfdetr_task, _detect_yolo_task, + get_classnames_txt_for_rfdetr, task_of_model_type, ) @@ -84,5 +87,22 @@ def test_unrecognized_returns_none(self): self.assertIsNone(_detect_rfdetr_task({"args": SimpleNamespace(other=1)})) +class GetClassnamesTxtForRfdetrTest(unittest.TestCase): + def _classnames(self, args): + with tempfile.TemporaryDirectory() as model_path: + get_classnames_txt_for_rfdetr(model_path, "weights.pt", checkpoint={"args": args}) + with open(os.path.join(model_path, "class_names.txt")) as f: + return f.read().splitlines() + + def test_dict_args(self): + self.assertEqual(self._classnames({"class_names": ["cat", "dog"]}), ["background_class83422", "cat", "dog"]) + + def test_namespace_args(self): + self.assertEqual( + self._classnames(SimpleNamespace(class_names=["cat", "dog"])), + ["background_class83422", "cat", "dog"], + ) + + if __name__ == "__main__": unittest.main() From d2277f668c4bf063609b6fe2ecc6d30896269acc Mon Sep 17 00:00:00 2001 From: Lee Clement Date: Fri, 29 May 2026 17:17:29 -0230 Subject: [PATCH 2/3] chore(deps): pin typer<0.26 to keep external click for CLI typer 0.26 vendors its own click as `typer._click` and drops the external click dependency, so roboflow.cli imports fail (click absent) and _compat.py mistypes under mypy. typer 0.25.x still depends on external click. Pin typer<0.26 and declare click explicitly in requirements.txt and requirements-slim.txt so the build matrix and test-slim pass. Co-Authored-By: Claude Opus 4.8 --- requirements-slim.txt | 5 +---- requirements.txt | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/requirements-slim.txt b/requirements-slim.txt index a1cfbfec..9c4d021e 100644 --- a/requirements-slim.txt +++ b/requirements-slim.txt @@ -6,10 +6,7 @@ tqdm>=4.41.0 PyYAML>=5.3.1 requests_toolbelt filetype -# typer 0.26 vendors click as typer._click and drops the external click dep, breaking -# roboflow.cli imports (test_slim_compat imports it). 0.25.x still uses external click. -typer>=0.12.0,<0.26 -# CLI imports click directly; declare it explicitly rather than relying on typer's transitive dep. +typer>=0.12.0,<0.26 # 0.26 vendors click, dropping the external dep the CLI imports click>=8.0 python-dateutil python-dotenv diff --git a/requirements.txt b/requirements.txt index 33e60989..3984d070 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,9 +19,5 @@ tqdm>=4.41.0 PyYAML>=5.3.1 requests_toolbelt filetype -# typer 0.26 vendors its own click as typer._click and drops the external click dep, which -# breaks roboflow/cli/_compat.py's SortedGroup (it subclasses TyperGroup using external click -# types). 0.25.x still depends on external click and type-checks; pin below 0.26. -typer>=0.12.0,<0.26 -# CLI imports click directly; declare it explicitly rather than relying on typer's transitive dep. +typer>=0.12.0,<0.26 # 0.26 vendors click, dropping the external dep the CLI imports click>=8.0 From 15fec189b3c539a276a2cf9d9b7606d8d033020b Mon Sep 17 00:00:00 2001 From: Lee Clement Date: Fri, 29 May 2026 17:32:00 -0230 Subject: [PATCH 3/3] chore(changelog): record rfdetr fix and typer pin under 1.3.9 Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc6c4588..d29d96df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## Unreleased +## 1.3.9 ### Added — Model evaluations SDK & CLI @@ -45,6 +45,12 @@ The endpoints require the `model-eval:read` scope. The base URL is configurable via `API_URL` (set to `https://localapi.roboflow.one` to test against a local API server). +### Fixed +- rf-detr model upload: accept checkpoints whose `args` is a plain dict (e.g. EMA checkpoints) when extracting class names, instead of raising `TypeError` from `vars()`. + +### Changed +- Pin `typer<0.26` and declare `click` explicitly: typer 0.26 vendors its own click and drops the external dependency, which broke the CLI and its type checks. + ## 1.3.7 ### Added — Soft-delete / Trash support