Skip to content

feat: delete (soft) and restore training runs via SDK and CLI - #512

Merged
leeclemnet merged 7 commits into
mainfrom
feat/trainings-delete-restore
Jul 28, 2026
Merged

feat: delete (soft) and restore training runs via SDK and CLI#512
leeclemnet merged 7 commits into
mainfrom
feat/trainings-delete-restore

Conversation

@leeclemnet

@leeclemnet leeclemnet commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds SDK and CLI support for the platform's new training deletion feature (roboflow/roboflow#13869), which is soft-delete-first: deleting a training moves it (and every model it produced) to the workspace Trash for 30 days, hidden from listings but restorable, before permanent cleanup.

  • rfapi.delete_version_training(...) — thin wrapper for DELETE /{ws}/{project}/{version}/v2/trainings/{training_id} (the platform's entity trash pattern; training_id required). Restore goes through the existing rfapi.restore_trash_item(..., "training", id) — the same shared POST /{ws}/trash/restore route project/version/workflow restores use; there is no bespoke trainings restore route. rfapi.resolve_version_training_id(...) resolves an omitted id to the version's sole run client-side (several runs raise MULTIPLE_TRAININGS with the ids listed).
  • Version.delete_training(training_id=None) / Version.restore_training(training_id) — mirror the existing Version.delete()/Version.restore() trash semantics; Training.delete()/Training.restore() do the same on the run object returned by the trainings APIs.
  • CLIroboflow train delete <project>/<version> [--training-id ...] and roboflow train restore <project>/<version> --training-id ..., with actionable hints for the refusal cases (in-flight run → stop/cancel first; multi-run version needs --training-id; item not in trash → roboflow trash list). The version's hosted endpoint always serves the oldest remaining run's model: when the deleted run was serving, the API reports versionAliasAction: "repointed" (+ versionAliasTarget) or "deleted" when no run survives, and train delete prints the switch or the stop. roboflow train list <project>/<version> enumerates a version's runs with their ids (reading the API's id field) so MMPV training ids are discoverable. Blank/whitespace --training-id values fail as structured usage errors (exit code 2) instead of raw tracebacks, and restore_trash_item validates its id for every trash type.

Deletion is soft-only on the public API by design — permanent deletion stays in the web UI's Trash view, consistent with the existing note in rfapi that permanent trash actions are never exposed to API keys.

Related Issue(s): Depends on roboflow/roboflow#13869 (platform endpoints) — merge/deploy that first; until then these calls return 404.

Type of Change

  • New feature (non-breaking change that adds functionality)

Testing

  • I have tested this change locally
  • I have added/updated tests for this change

Test details:

> uv run roboflow train --help

 Usage: roboflow train [OPTIONS] [COMMAND] [ARGS]...

 Train a model

╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --checkpoint            TEXT     Checkpoint to resume training from                                                  │
│ --epochs                INTEGER  Number of training epochs                                                           │
│ --project       -p      TEXT     Project ID to train                                                                 │
│ --speed                 TEXT     Training speed preset                                                               │
│ --type          -t      TEXT     Model type (e.g. rfdetr-nano, yolov8n)                                              │
│ --train-recipe          TEXT     Full trainRecipe as inline JSON or @path/to/file.json (see 'roboflow train          │
│                                  recipe'); --epochs is folded into its hyperparameters unless the recipe already     │
│                                  sets epochs                                                                         │
│ --version       -v      INTEGER  Version number to train                                                             │
│ --help          -h               Show this message and exit.                                                         │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ cancel   Cancel an in-flight training run.                                                                           │
│ delete   Move a terminal training run to the workspace Trash (soft delete).                                          │
│ list     List a version's training runs with their ids.                                                              │
│ recipe   Show the training recipe schema and template for a model type.                                              │
│ restore  Restore a trashed training run (and its models) back into listings.                                         │
│ results  Run-level training results bundle.                                                                          │
│ start    Start training for a dataset version.                                                                       │
│ stop     Request a graceful early-stop on an in-flight training run.                                                 │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
 > uv run roboflow train delete --help

 Usage: roboflow train delete [OPTIONS] TARGET

 Move a terminal training run to the workspace Trash (soft delete).

 The run and every model it produced disappear from listings but stay
 restorable for 30 days ('roboflow train restore' or the web Trash view),
 after which they are permanently deleted. In-flight runs are refused —
 stop or cancel first — as is the run backing the version's registered
 model. Permanent deletion is only available in the web UI's Trash view.

╭─ Arguments ──────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *    target      TEXT  Training to delete as 'project/version' [required]                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --training-id          TEXT  Training id of the run to delete (versions can own several). Omit to target the         │
│                              version's sole run.                                                                     │
│ --help         -h            Show this message and exit.                                                             │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
> uv run roboflow train delete chess-pieces-fmhpz/112 --training-id b704c8e6ee031f3ef563

Training moved to Trash for chess-pieces-fmhpz version 112. Restorable for 30 days via 'roboflow train restore'.
image image
> uv run roboflow train restore chess-pieces-fmhpz/112 --training-id b704c8e6ee031f3ef563

Training restored for chess-pieces-fmhpz version 112.
image image

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code where necessary, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors
  • I have updated the documentation accordingly (if applicable)

Additional Context

Mirrors the existing cancel_version_training/stop_version_training adapter pattern and the train cancel/train stop CLI command pattern. The companion MCP PR (roboflow/roboflow-mcp#124) was closed by design: MCP has no trash-lifecycle tools for any entity yet, so trainings won't lead there.

🤖 Generated with Claude Code

@leeclemnet
leeclemnet force-pushed the feat/trainings-delete-restore branch from ae5c422 to 958e8ad Compare July 24, 2026 18:36
@leeclemnet
leeclemnet marked this pull request as ready for review July 27, 2026 17:12
@leeclemnet
leeclemnet requested a review from mkaic July 27, 2026 17:14
mkaic
mkaic previously approved these changes Jul 28, 2026

@mkaic mkaic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

leeclemnet and others added 6 commits July 28, 2026 11:01
Wires the platform's external v2 trainings delete/restore routes:

- rfapi: delete_version_training (POST .../v2/trainings/delete, optional
  trainingId with sole-run fallback) and restore_version_training
  (trainingId required — trashed runs are invisible to the sole-run
  resolver).
- SDK: Version.delete_training(training_id=None) and
  Version.restore_training(training_id), mirroring Version.delete()/
  restore() trash semantics: the run and its models hide from listings
  but stay restorable for 30 days before permanent cleanup.
- CLI: `roboflow train delete <project>/<version> [--training-id]` and
  `roboflow train restore <project>/<version> --training-id`, with
  actionable hints for in-progress runs, alias-backed models, and
  multi-run versions.

Deletion is soft-only on the public API by design; permanent deletion
stays in the web UI's Trash view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y path

- delete_version_training omits trainingId only when None and raises
  ValueError on blank/whitespace ids, so an empty selector can never
  fall back to soft-deleting the version's sole run;
  restore_version_training rejects blank ids likewise.
- New `roboflow train list <project>/<version>` enumerates a version's
  runs (TRAINING_ID / STATUS / MODEL_TYPE / MODELS), and the
  MULTIPLE_TRAININGS hint on delete now points at it instead of
  `train results`, which cannot enumerate runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The platform refactored training deletion to match project/version/workflow:
HTTP DELETE on the resource and restore via the shared workspace trash route.

- delete_version_training now issues
  DELETE /{ws}/{proj}/{ver}/v2/trainings/{training_id} (id required) and
  returns the shared { deleted, type, ..., trash: true } shape.
- restore_version_training is gone; Training.restore, Version.restore_training,
  and 'roboflow train restore' go through restore_trash_item(..., "training", id) —
  the same route project/version/workflow restores use.
- Sole-run resolution moves client-side (resolve_version_training_id):
  Version.delete_training() and 'roboflow train delete' without --training-id
  list the version's runs and only proceed when there is exactly one, keeping
  the MULTIPLE_TRAININGS error + 'roboflow train list' hint.
- CLI restore hints now match the trash route's "not found in trash" message.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v2 trainings list serializes each run's id as `id` (the pre-existing
serializeExternalTraining shape, shared with MCP), not `trainingId` — the CLI
table rendered an empty TRAINING_ID column and the sole-run resolver's
MULTIPLE_TRAININGS message listed "None". Read `id`, correct the docstring,
and point the test fixtures at the real payload shape (the old fixtures used
the wrong key, which is exactly what masked this).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up: a blank --training-id raised a bare ValueError past the
CLI's error path, and restore forwarded whitespace ids to the API.

- restore_trash_item validates item_id (shared by every trash restore).
- train delete/restore map ValueError through output_error with exit code 2
  and a --training-id hint.
- Tests: structured error + exit code for blank delete/restore, blank
  Training.restore(), and blank restore_trash_item ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
External SDK users have no context for "DNA" or "MMPV" — say what the
commands and methods do in product terms instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@leeclemnet
leeclemnet force-pushed the feat/trainings-delete-restore branch from 3957620 to 6d1e1d6 Compare July 28, 2026 13:31
The platform replaced the delete refusal for the serving run with a single
rule: {project}/{version} serves the oldest surviving run's model. Deleting
the serving run switches serving to the next-oldest run (versionAliasAction
"repointed" + target) or stops it when none survives ("deleted"); restoring
the oldest run hands serving back. Update docstrings and CLI help, drop the
now-impossible "register another model first" hint, and print the switch or
stop in train delete output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@leeclemnet
leeclemnet force-pushed the feat/trainings-delete-restore branch from 6d1e1d6 to 2cae987 Compare July 28, 2026 14:03

@mkaic mkaic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@leeclemnet
leeclemnet merged commit 0b29edd into main Jul 28, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants