Skip to content

Feature: Multiple Models Per Version#497

Open
mkaic wants to merge 14 commits into
mainfrom
feature/mmpv
Open

Feature: Multiple Models Per Version#497
mkaic wants to merge 14 commits into
mainfrom
feature/mmpv

Conversation

@mkaic

@mkaic mkaic commented Jun 25, 2026

Copy link
Copy Markdown

Code and PR description are LLM-generated.

MMPV support: first-class Training / TrainedModel + backwards-compat

Adds DNA-style multi-model-per-version support to the SDK, consuming the new v2
external trainings endpoints.

Companion PRs:

What this adds

  • roboflow/core/training.py — new Training and TrainedModel objects.
    A version owns many trainings; a training owns one or more models (NAS owns
    many). TrainedModel.predict() infers by <ws>/<model-slug>;
    .download() pulls weights by model id.
  • adapters/rfapi.py — v2 trainings adapter
    (list_trainings_for_version, get_training, create/cancel/stop_training_v2,
    get_model_weights_url) mirroring the MCP's rf_api.py 1:1. The
    legacy-vs-MMPV branch lives on the backend, never in the client.
  • Version gains .trainings(), .models(), and a back-compat .model
    property that resolves the sole model and refuses to guess when a version
    owns several — raising with a CTA to version.models().

Backwards compatibility

Old installs keep hitting the frozen v1 routes, which the backend now makes
MMPV-aware. Single-model MMPV versions still work for metadata, train()
poll-reads, and weights download; only predict() on MMPV is the deliberate
casualty (requires the new <ws>/<model-slug> id).

Tests

tested e2e with both new and old versions of package and verified expected behavior/graceful-degradation on both legacy and MMPV versions on staging.

Link to Gist with the e2e smoketest run on the kaiplace staging workspace: https://gist.github.com/mkaic/2d19b05f4f4c253bbca2ef9aca7746f4

Results of running that script against the currently deployed staging API:

(roboflow-python) mkaic@Kais-MacBook-Pro-C521K6G9RP roboflow-python % uv run --with-editable . tests_e2e/test_new.py
roboflow(local)==1.4.0  ws=kaiplace proj=the-paid-gallery

== SMPV v141 (MMPV API works on legacy single-model versions) ==
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.trainings() == 1 — len=1 (want 1)
  [PASS] (package) version.models() == 1 — len=1 (want 1)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.models()[0] is the sole model
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.model is deprecated (warns)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) SMPV: model.predict() by slug
  [PASS] (package) SMPV: model.predict_video() by slug
  [PASS] (package) SMPV: model.poll_for_video_results() (cached job)
  [PASS] (package) SMPV: model.poll_for_video_results(job_id) (explicit)
  [SKIP] (package) SMPV: model.poll_until_video_results() — set POLL_VIDEO_TO_COMPLETION=1 to block until the job finishes
  [PASS] (package) SMPV: model.download() weights

== single-model MMPV v147 ==
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.trainings() == 1 — len=1 (want 1)
  [PASS] (package) training.models == 1 — len=1 (want 1)
  [PASS] (package) version.models() == 1 — len=1 (want 1)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.models()[0] is the sole model
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.model is deprecated (warns)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) single-MMPV: model.predict() by slug
  [FAIL] (package) single-MMPV: model.predict_video() by slug — raised Exception: Error starting video inference: {
    "error": "Bad Request"
}
  [SKIP] (package) single-MMPV: model.poll_for_video_results() — predict_video did not return a job id
  [PASS] (package) single-MMPV: model.download() weights

== NAS MMPV v151 (1 training, many models — needs the run finished) ==
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.trainings() == 1 — len=1 (want 1)
  [PASS] (package) training.models > 1 (NAS) — len=43 (want >1)
  [PASS] (package) version.models() > 1 — len=43 (want >1)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) every model selectable by id
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.model is deprecated (warns)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) NAS: model.predict() by slug
  [PASS] (package) NAS: model.download() unavailable (NAS) — raised RoboflowError

== multi-training MMPV v148 ==
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.trainings() > 1 — len=2 (want >1)
  [PASS] (package) each training selectable by id
  [PASS] (package) version.models() > 1 — len=2 (want >1)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) version.model is deprecated (warns)
loading Roboflow workspace...
loading Roboflow project...
  [PASS] (package) multi-training: model.predict() by slug
  [FAIL] (package) multi-training: model.predict_video() by slug — raised Exception: Error starting video inference: {
    "error": "Bad Request"
}
  [SKIP] (package) multi-training: model.poll_for_video_results() — predict_video did not return a job id
  [PASS] (package) multi-training: model.download() weights

SUMMARY: 29/31 pass · 2 fail

Comment thread roboflow/core/training.py Dismissed
@jarbas-roboflow

Copy link
Copy Markdown

Status: ❌ Request Changes — Gist: https://gist.github.com/jarbasrf/3c6059e57956669c2be838307254f445. Resumo: The PR changes Version.model into a raising network-backed resolver and exposes MMPV models through object-detection-only inference behavior, which risks public SDK regressions and incorrect predictions.

@jarbas-roboflow

Copy link
Copy Markdown

Status: ❌ Request Changes — Gist: https://gist.github.com/jarbasrf/fbfdc78d9c9c21ed3ef62cf90bd30955. Resumo: The artifact was written to the requested path. The main remaining issue is that Training.models can drop training-level modelType, causing non-detection MMPV models to fall back to object-detection routing under the documented response shape.

@jarbas-roboflow

Copy link
Copy Markdown

Status: ✅ Approve — Gist: https://gist.github.com/jarbasrf/f53f7a4dedd198da2b3b86edf18fabea. Resumo: The previously reported Training.models training-level modelType propagation issue is resolved at current head, with focused regression coverage. No new merge-blocking issue was found.

@jarbas-roboflow

Copy link
Copy Markdown

Status: ❌ Request Changes — Gist: https://gist.github.com/jarbasrf/e547be780a0c8fbda40ed65805fc6ad0. Resumo: The prior MMPV issues remain resolved, but current head adds a core Version construction regression via an extra uncaught network request, plus a missing MkDocs Trainings page.

probicheaux
probicheaux previously approved these changes Jun 26, 2026
@jarbas-roboflow

Copy link
Copy Markdown

Status: ✅ Approve — Gist: https://gist.github.com/jarbasrf/25192f4b64448ea98d5b32b989275840. Resumo: The prior Version constructor network regression and missing MkDocs Trainings page are fixed at current PR head. No remaining concrete correctness issue found in the reviewed diff.

@mkaic mkaic requested a review from probicheaux June 26, 2026 00:36
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ mkaic
❌ pre-commit-ci[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

probicheaux
probicheaux previously approved these changes Jul 7, 2026
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.

4 participants