Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ All notable changes to this project will be documented in this file.
`upload_model` detects them and rebuilds a deploy-ready bundle via rf-detr's
`export_for_roboflow` (requires `rfdetr>=1.8.0`)
([#488](https://github.com/roboflow/roboflow-python/pull/488))
- v2 trainings with custom train recipes
([#510](https://github.com/roboflow/roboflow-python/pull/510)):
- `Version.describe_train_recipe(model_type)` — fetch the tunable
hyperparameter schema, allowed online augmentation/preprocessing steps,
and a ready-to-edit recipe `template` for a model type.
- `Version.start_training(...)` — non-blocking training creation via the v2
trainings API; builds a full `trainRecipe` from the template when
`hyperparameters`/`online_augmentation` overrides are given, or submits a
caller-supplied `train_recipe`. A top-level `epochs` is folded into the
recipe's hyperparameters (the server resolves recipe epochs ahead of the
body value). Returns `{"trainingId", "status", "jobId"}` for sweep-style
polling.
- `Version.list_trainings()` and `Version.get_training(training_id=None)` —
list/inspect v2 trainings for a version.
- `roboflow train recipe -p <project> -v <N> -m <model_type>` — print the
recipe schema and template as JSON.
- `roboflow train start --hyperparameters '<json>'` and
`--train-recipe '<json>'` — create the training through the v2 API and
print the new `trainingId`.

## 1.3.11

Expand Down
23 changes: 23 additions & 0 deletions CLI-COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,29 @@ roboflow train results my-project/3
NAS sweeps require the version's validation split to have at least 15 images;
the server returns `code: "insufficient_validation_images_for_nas"` otherwise.

### Train recipes — custom hyperparameters & augmentation (v2)

```bash
# Inspect a model type's tunable hyperparameter schema, allowed online
# augmentation/preprocessing steps, and a ready-to-edit recipe template:
roboflow train recipe -p my-project -v 3 -m rfdetr-medium

# Start a training with hyperparameter overrides. The CLI fetches the model
# type's recipe template, merges your overrides in, and submits it via the
# v2 trainings API (the server dense-fills the remaining defaults):
roboflow train start -p my-project -v 3 -t rfdetr-medium --hyperparameters '{"lr": 0.0002}'

# Or submit a fully edited recipe as-is (start from the `template` field of
# `roboflow train recipe`; not combinable with --hyperparameters):
roboflow --json train recipe -p my-project -v 3 -m rfdetr-medium | jq .template > recipe.json
# ... edit recipe.json ...
roboflow train start -p my-project -v 3 -t rfdetr-medium --train-recipe "$(cat recipe.json)"
```

Both flags create the training through the v2 trainings API and print the new
`trainingId` instead of blocking — handy for launching sweeps and polling
status separately.

### NAS models — list, star, deploy

```bash
Expand Down
100 changes: 100 additions & 0 deletions roboflow/adapters/rfapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,106 @@ def get_training_results(api_key: str, workspace_url: str, project_url: str, ver
return response.json()


def get_train_recipe(
api_key: str,
workspace_url: str,
project_url: str,
version: str,
*,
model_type: str,
):
"""GET /{ws}/{proj}/{version}/v2/trainings/recipe — training schema for a model type.

Returns the tunable-hyperparameter schema, the allowed online
augmentation/preprocessing steps, and a ready-to-submit ``template``
that can be edited and passed to ``create_training`` as ``train_recipe``.
"""
encoded_model_type = urllib.parse.quote(model_type, safe="")
url = (
f"{API_URL}/{workspace_url}/{project_url}/{version}/v2/trainings/recipe"
f"?api_key={api_key}&modelType={encoded_model_type}"
)
response = requests.get(url)
if not response.ok:
raise RoboflowError(response.text)
return response.json()


def create_training(
api_key: str,
workspace_url: str,
project_url: str,
version: str,
*,
model_type: Optional[str] = None,
speed: Optional[str] = None,
checkpoint: Optional[str] = None,
epochs: Optional[int] = None,
train_recipe: Optional[Dict] = None,
business_context: Optional[str] = None,
):
"""POST /{ws}/{proj}/{version}/v2/trainings — create a training (non-blocking).

All body keys are optional; only non-None arguments are sent. Returns the
server response, e.g. ``{"trainingId": ..., "status": ..., "jobId": ...}``.
"""
url = f"{API_URL}/{workspace_url}/{project_url}/{version}/v2/trainings?api_key={api_key}"

data: Dict[str, Union[str, int, Dict]] = {}
if model_type is not None:
# API expects camelCase
data["modelType"] = model_type
if speed is not None:
data["speed"] = speed
if checkpoint is not None:
data["checkpoint"] = checkpoint
if epochs is not None:
data["epochs"] = epochs
if train_recipe is not None:
data["trainRecipe"] = train_recipe
if business_context is not None:
data["business_context"] = business_context

response = requests.post(url, json=data)
if not response.ok:
raise RoboflowError(response.text)
return response.json()


def list_version_trainings(api_key: str, workspace_url: str, project_url: str, version: str):
"""GET /{ws}/{proj}/{version}/v2/trainings — list trainings for a version.

Returns the server response, e.g. ``{"trainings": [...]}``.
"""
url = f"{API_URL}/{workspace_url}/{project_url}/{version}/v2/trainings?api_key={api_key}"
response = requests.get(url)
if not response.ok:
raise RoboflowError(response.text)
return response.json()


def get_version_training(
api_key: str,
workspace_url: str,
project_url: str,
version: str,
*,
training_id: Optional[str] = None,
):
"""GET /{ws}/{proj}/{version}/v2/trainings/get — fetch a single training.

Pass ``training_id`` to fetch a specific training; omit it to let the
server pick its default (the version's current training).
"""
url = f"{API_URL}/{workspace_url}/{project_url}/{version}/v2/trainings/get?api_key={api_key}"
if training_id is not None:
url += f"&trainingId={urllib.parse.quote(str(training_id), safe='')}"
response = requests.get(url)
if not response.ok:
raise RoboflowError(response.text)
return response.json()


def list_project_models(
api_key: str,
workspace_url: str,
Expand Down
Loading
Loading