All notable changes to this project will be documented in this file.
- Native Action Recognition video uploads with
Project.upload_video(#535). Upload original MP4/MOV bytes with batch, tags, metadata, and split; read processing state withget_video_upload_statusor use bounded polling withwait_for_video_upload/upload_video(..., wait=True). Use the finaluploadedresponse'svideoId: deduplication can change the canonical Source ID and returnresolvedBatch: null. See the public upload example.
- CLI project creation honors
--api-keyand the selected workspace's stored credentials (#536). roboflow train start --type cosmos3-edgereturns atrainingIdthrough the v2 training API even without a custom recipe (#537).
- EU region selection for the SDK and CLI
(#513):
ROBOFLOW_REGION=us|eu(case-insensitive) selects the platform; an environment value takes precedence over the saved region.roboflow auth login --region euauthenticates against the EU data-residency platform and records the credentials' region.roboflow auth set-region <us|eu>saves the region;roboflow auth statusreports the region and environment.- Explicit per-URL settings such as
API_URLstill take precedence over the region. Hosted semantic segmentation has no EU deployment and raises an error in the EU region instead of sending images to the US.
project create --type action-recognitionin the CLI (#532). Requires a Roboflow platform release that accepts the type; older releases answer HTTP 422 and the CLI adds a hint.- Action Recognition training from the SDK
(#533):
Version.create_training(model_type="cosmos3-edge", ...)ensures thevideo-cocoexport and returns aTraininghandle.Version.train()is unchanged. add_to_datasetonProject.single_upload/Project.save_annotation(#531). PassFalseto store an annotation without moving the image out of its batch into the Dataset. The default (None) keeps the API's current behavior.
- Model evaluation comparison for a dataset version
(#529):
Workspace.compare_model_evaluations(project, version, frontier_metric=None)— compare test-set accuracy and median latency, including Pareto frontier membership and reasons models are excluded.roboflow --workspace <workspace> eval compare --project <project> --version <N>— display the comparison as a table; use--jsonfor the public API response. Use--frontier-metricto choose the metric for frontier membership.- Both surfaces read existing results without starting evaluations.
- All
roboflow evalcommands now return exit code2for HTTP 401/403 access errors (previously1).
- Hosted auto-label support, matching the Roboflow MCP tools:
Workspace.autolabel_models()— list the foundation-model catalog (gpt-6-astra-boxes,sam3-rle,gemini-boxes, ...) with availability, guidance and credits per image.Project.autolabel_preview(model, image, ontology=...)— free single-image preview to compare models before starting a job.imageaccepts an HTTPS URL, a local file path or a base64 string.Project.autolabel(batch_id, model, model_type="foundational" | "roboflow", ...)— start a job over a batch; returns{jobId, annotationJobId}. Theontologyis keyed by prompt ({"kitten": "cat", "tabby": "cat"}), so several prompts can share one output class.preserve_existing_annotations=Truekeeps annotations already on the images (the server default replaces them).Project.autolabel_job(job_id)/Workspace.autolabel_job(job_id)— poll per-subjob progress.roboflow autolabel models | preview | start | jobCLI commands.start --preserve-existingmirrors the SDK flag;job -p ws/projectresolves the workspace the same waystartdoes.
- HEIC/HEIF decoding is an optional extra:
pip install "roboflow[heic]"installspillow-heif>=1.7.0, andimport roboflowregisters its Pillow opener when it is installed. The default install no longer depends onpi-heif, which is discontinued upstream: its final release, 1.4.0, bundles libheif 1.23.0, which is affected by the security advisories fixed in libheif 1.23.2 and 1.23.3 (including CVE-2026-84383).roboflowno longer registerspi-heifeven when it is still installed.- Uploads and
Project.check_valid_image()handle HEIC without the extra. Decoding a local HEIC file, for example withmodel.predict("photo.heic"), needs it. - The extra is opt-in because pillow-heif's binary wheels bundle the x265 encoder, which makes them GPL-2.0 (#398).
- Uploads and
- Custom train recipes on v2 trainings
(#510):
Version.describe_train_recipe(model_type)— fetch the tunable hyperparameter schema, allowed online augmentation/preprocessing steps, and a ready-to-edit recipetemplatefor a model type.train_recipeonVersion.create_training(...)— pass an editeddescribe_train_recipetemplate for custom hyperparameters/online augmentation (the server dense-fills omitted defaults). A top-levelepochsis folded into the recipe's hyperparameters (the server resolves recipe epochs ahead of the body value).train_reciperequiresmodel_type— recipes are minted per model type, and without one the platform would train the project's default architecture.roboflow train recipe -p <project> -v <N> -m <model_type>— print the recipe schema and template as JSON.roboflow train start --train-recipe '<json>'— create the training through the v2 API and print the newtrainingId. Accepts inline JSON or a curl-style file reference (--train-recipe @train_recipe.json).
A dataset version can now own many trainings, and a training can produce many models (e.g. a NAS sweep). New object types expose this:
SDK (roboflow/core/training.py, roboflow/core/version.py):
Version.trainings()— list the version's training runs asTrainingobjects.Version.models()— every trained model for the version (the union across its trainings), asTrainedModelobjects. This is now the canonical way to get a version's models.Version.create_training(speed=, model_type=, checkpoint=, epochs=)— launch a run without blocking, returning aTraining.Training—.models,.refresh(),.cancel(),.stop(), plus.training_id/.status/.model_type.TrainedModel—.predict(),.predict_video(),.download(), plus.model_id/.model_type/.metrics. ATrainedModeldoes everything the oldversion.modelcould; you just reach it throughversion.models().
Adapters (roboflow/adapters/rfapi.py): v2 trainings endpoints —
list_trainings_for_version, get_training, create_training_v2,
cancel_training_v2, stop_training_v2, get_model_weights_url.
workspace.update_image_metadata()andworkspace.batch_update_image_metadata()(plus aproject.update_image_metadata()convenience alias) — public SDK wrappers for updating metadata and tags on existing images, previously only reachable via the internalrfapiadapter or the CLI. The batch method acceptswait=Trueto poll the async task until completion and return per-image results.- Upload raw rf-detr PyTorch-Lightning checkpoints (e.g.
checkpoint_best_ema.pth):upload_modeldetects them and rebuilds a deploy-ready bundle via rf-detr'sexport_for_roboflow(requiresrfdetr>=1.8.0) (#488)
- Keypoint detection inference now reports its prediction type correctly (previously mislabeled as classification), fixing rendering/plotting of keypoint predictions.
version.model(the singular attribute) is deprecated and emits aDeprecationWarning. It cannot represent a version with multiple models; useversion.models()instead.
roboflow api-keyCLI command group and SDK methods to create, list, get, update, protect, and revoke workspace API keys — including scoped keys, folder restrictions, and custom metadata (scoping/metadata require the Advanced API Keys plan feature).
- Weight upload support for yolo26-sem semantic segmentation models via
version.deploy()andworkspace.deploy_model()
Wraps the public /{workspace}/model-evals REST surface
(roboflow/roboflow#11636)
so users can read evaluation results — mAP, confidence sweep, per-class
performance, confusion matrix, vector clusters, per-image stats,
recommendations — from Python and from the CLI without hitting the API
directly. Companion docs:
roboflow-dev-reference#18.
SDK (roboflow/core/model_eval.py):
Workspace.evals(project=None, version=None, model=None, status=None, limit=None)— list evals asModelEvalinstances pre-populated with metadata from the list response.Workspace.eval(eval_id)— fetch a single eval (returns aModelEvalwith.summarypopulated when status isdone).ModelEval.refresh()— re-fetch the eval header.ModelEval.map_results(),.confidence_sweep(),.performance_by_class(split=None),.confusion_matrix(split=None, confidence=None),.vector_analysis(confidence=None),.image_predictions(split=None, confidence=None, limit=None, offset=None),.recommendations()— one method per panel; each returns the raw JSON dict.
CLI (roboflow/cli/handlers/eval.py):
roboflow eval list [--project P] [--version V] [--model M] [--status S] [--limit N]roboflow eval get <eval_id>roboflow eval map-results <eval_id>roboflow eval confidence-sweep <eval_id>roboflow eval performance-by-class <eval_id> [--split S]roboflow eval confusion-matrix <eval_id> [--split S] [--confidence N]roboflow eval vector-analysis <eval_id> [--confidence N]roboflow eval image-predictions <eval_id> [--split S] [--confidence N] [--limit N] [--offset N]roboflow eval recommendations <eval_id>
Exit codes are stable per error class so shell scripts and AI agents can
react without parsing message strings: 3 for model_eval_not_found
(404), 4 for model_eval_not_done (409), 5 for invalid_split /
invalid_confidence (400). Every command supports --json for
structured output.
Low-level (roboflow.adapters.rfapi):
list_model_evals,get_model_eval,get_model_eval_map_results,get_model_eval_confidence_sweep,get_model_eval_performance_by_class,get_model_eval_confusion_matrix,get_model_eval_vector_analysis,get_model_eval_image_predictions,get_model_eval_recommendations.- New typed exceptions
ModelEvalNotFoundError,ModelEvalNotDoneError,InvalidSplitError,InvalidConfidenceError(all subclasses ofRoboflowError) so callers can distinguish "eval doesn't exist" from "eval still running" from "bad argument" without parsing strings.
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).
- rf-detr model upload: accept checkpoints whose
argsis a plain dict (e.g. EMA checkpoints) when extracting class names, instead of raisingTypeErrorfromvars().
- Pin
typer<0.26and declareclickexplicitly: typer 0.26 vendors its own click and drops the external dependency, which broke the CLI and its type checks.
Mirrors the soft-delete and Trash features added to the Roboflow web app (roboflow/roboflow#11131). Deleting a project, version, or workflow now moves it to Trash with a 30-day retention window (and cancels any in-flight training jobs); items can be restored within that window. Companion docs: roboflow-dev-reference#5.
SDK (roboflow/):
Project.delete()/Project.restore()— soft-delete and restore by slug.Version.delete()/Version.restore()— same shape on a version handle.Workspace.trash()— list everything currently in a workspace's Trash, grouped byprojects/versions/workflows.Workspace.restore_from_trash(item_type, item_id, parent_id=None)— restore an item by id when you don't have a live SDK handle (or for workflows, which don't have a first-class object yet).
CLI (roboflow/cli/):
roboflow project delete/roboflow project restoreroboflow version delete/roboflow version restoreroboflow workflow delete/roboflow workflow restoreroboflow trash list
Destructive commands prompt for confirmation interactively and accept
--yes / -y for scripted use. Every command supports --json for
structured output and emits actionable error hints with stable exit codes.
Low-level (roboflow.adapters.rfapi):
delete_project,delete_version,delete_workflow,list_trash,restore_trash_item.RoboflowErrormessages now extract theerrorfield from JSON response bodies (e.g. "Not authorized to view trash") instead of the raw response text.
Permanent deletion is intentionally web-UI-only. Emptying Trash or immediately deleting a single Trash item destroys data irrecoverably, so those actions are not exposed on the SDK or CLI — they live only in the Roboflow app's Trash view, which has an explicit confirmation dialog. Items left in Trash are cleaned up automatically after 30 days.
Workspace.create_workflow() and roboflow workflow create --definition
auto-wrap bare workflow definitions in {"specification": ...} before
POSTing to the backend, matching what the web app does
(#460). Previously,
the user-facing flat shape ({version, inputs, steps, outputs}) was sent
verbatim, so POST /infer/workflows/... against the resulting workflow
returned HTTP 502 with MalformedWorkflowResponseError: Workflow specification not found in Roboflow API response.
Workflows already wrapped (top-level specification key) are passed
through unchanged. Non-workflow dicts and non-JSON strings are also
passed through verbatim so custom payloads aren't second-guessed.
Note: workflows that were stored with the bare shape before this fix will still 502 until re-saved. Run
roboflow workflow update <url> --definition <file>once per affected workflow to migrate.
upload_image now uploads original image bytes instead of re-encoding to
JPEG client-side (#464).
Purely additive on the public API surface. The new endpoints require
project:update, version:update, or workflow:update scopes — most
existing keys already have these.
- Added support for Palligema2 model uploads via
upload_modelcommand with the following model types:paligemma2-3b-pt-224paligemma2-3b-pt-448paligemma2-3b-pt-896