Skip to content

fix(inference): sam2_video is a point-prompt model, and an unknown type refuses - #458

Merged
JArmandoAnaya merged 1 commit into
mainfrom
fix/family-resolver
Aug 9, 2026
Merged

fix(inference): sam2_video is a point-prompt model, and an unknown type refuses#458
JArmandoAnaya merged 1 commit into
mainfrom
fix/family-resolver

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

A local connection for facebook/sam2-hiera-base-plus@main — the model id the
connection form pre-fills — refused point suggestions with a sentence about text
prompts. The adapter is chosen from the model_type the model's own config declares,
and SEGMENTER_FAMILIES held only sam2 while the published SAM 2 checkpoints declare
sam2_video. So the commonest point-prompt model in the product resolved to the
detector, and the detector refused the click in its own vocabulary.

Today's behaviour, verbatim

Against the real workspace connection, at 8084e4a, weights already in the cache:

family_of -> 'sam2_video'   in SEGMENTER_FAMILIES=False
RAISED UnsupportedPrompt: facebook/sam2-hiera-base-plus@main answers text prompts;
it was asked with 'points' points, which it has no way to interpret

Nothing about that sentence is true of that model.

The fix

sam2_video is named beside sam2. Not a door held open for 0.2.0, which is what
the old comment called it — it is what the checkpoints ship today, and transformers
loads one into the image model deliberately, saying so as it does:

You are using a model of type `sam2_video` to instantiate a model of type `sam2`.
This may be expected if you are loading a checkpoint that shares a subset of the
architecture (e.g., loading a `sam2_video` checkpoint into `Sam2Model`)

So no adapter changed. LocalSamProvider's Sam2Model was already the right class.

The fallback is gone. Resolution is now membership of two disjoint sets, and a
model_type in neither is refused with what the config declared and what this build
supports:

connection 'sam2-local' names 'facebook/sam2-hiera-base-plus', whose config declares
model type 'nonsense-net', and this build has no adapter for that model type; it
supports grounding-dino, mm-grounding-dino, sam2, sam2_video — point the connection
at a model of one of those types

The empty string an unreadable config yields reaches the same refusal by the same route,
with a different opening — "does not say what model type it is" — and a second remedy,
because damaged files are the usual cause and download_weights is what fixes them.
family_of itself is unchanged: reading the config and deciding what to do about it stay
separate jobs.

Two judgment calls inside the scope, both narrower than the obvious reading

The SAM 2 set is whole models only. The locked transformers registers four
model_types matching SAM 2: sam2, sam2_video, sam2_vision_model and
sam2_hiera_det_model. The last two are the encoder halves a full config nests, not
checkpoints anything can prompt — a connection naming one has no mask decoder to run.
The task said to enumerate what exists and cover them; covering those two would have been
the guess this PR removes, so they fall to the refusal instead.

The detector set is two names, not five. transformers_provider post-processes with
post_process_grounded_object_detection(outputs, input_ids, threshold=…, text_threshold=…).
Measured against the locked transformers, only the grounding-dino family takes that
signature:

grounding-dino     (outputs, input_ids=None, threshold=0.25, text_threshold=0.25, target_sizes=None, text_labels=None)
mm-grounding-dino  (same — GroundingDinoProcessor)
omdet-turbo        (outputs, text_labels=None, threshold=0.3, nms_threshold=0.5, target_sizes=None, max_num_det=None)
owlv2 / owlvit     (outputs, threshold=0.1, target_sizes=None, text_labels=None)

For the last three, input_ids would land in threshold and text_threshold is not a
parameter at all. They were reachable through the old fallback and would have failed
inside a post-processor; naming them here would have claimed a support that does not
exist. This is the task's "other reachable fall-through paths … refuse, never guess"
clause, applied.

Verification

Red before green. Both failing tests existed before the fix. With the new constants
in place and the mapping still frozenset({"sam2"}):

FAILED tests/inference/test_providers.py::test_the_video_variant_of_the_architecture_resolves_to_the_point_prompt_adapter
FAILED tests/inference/test_providers.py::test_an_unknown_model_type_is_refused_rather_than_handed_to_a_family
FAILED tests/inference/test_providers.py::test_a_config_that_declares_no_type_is_refused_too
FAILED tests/inference/test_providers.py::test_both_spellings_of_the_one_architecture_are_named
FAILED tests/inference/test_providers.py::test_an_unsupported_model_leaves_nothing_behind_for_the_next_request

Mutation verification. Committed first, each mutation's anchor asserted present
exactly once before and its replacement asserted in the file after, each reverted to
byte-identical committed text:

Revert Test that goes red
frozenset({"sam2", "sam2_video"})frozenset({"sam2"}) test_the_video_variant_of_the_architecture_resolves_to_the_point_prompt_adapter
raise InferenceConnectionNotRunnable(_no_adapter_for(…)) → the old return LocalTransformersProvider(…) test_an_unknown_model_type_is_refused_rather_than_handed_to_a_family

End to end on this machine, same connection, same asset, weights already in the
workspace cache — no download:

request:  suggest(project=cat-dog, asset=d20be582… (682x360 jpeg),
                  connection=5c3cbb07… facebook/sam2-hiera-base-plus@main,
                  prompt=PointPrompt(positive=((341.0, 180.0),)),
                  allowed=(GeometryType.POLYGON,))
response: model_ref='facebook/sam2-hiera-base-plus@main'  regions=1
          PolygonGeometry  label=''  confidence=0.9102  points=16
          first3=[(329.0, 167.0), (333.0, 167.0), (337.0, 171.0)]

An empty label is correct and deliberate: pointing says where, not what (D3/D4 on #424).

Two environment notes, so the run above is reproducible rather than magic. This host's
~/.local carries a scikit-learn whose scipy is missing, which makes transformers
fail to import GenerationMixin; and the extra does not carry torchvision — see below.
Both were worked around outside the repository, on PYTHONPATH, and nothing in this
diff depends on either.

Gate

Staged (~10-minute harness ceiling), pytest split by the directories ls tests/ reported
at run time — tests/fixtures and tests/scripts are both new to this list since the
last staged run, and tests/scripts is node rather than pytest.

Stage Exit
pytest tests/architecture tests/cli tests/examples tests/fixtures tests/formats tests/inference tests/jobs tests/mcp tests/packaging tests/test_versioning.py 0
pytest tests/kernel 0
pytest tests/server 0
ruff check . 0
ruff format --check . 0
mypy src/visionset (150 files) 0
lint-imports (4 kept, 0 broken) 0
check.sh frontend 0
check.sh generated 0
check.sh browser (233 passed, no flakes; cycle 1 passed) 0

Frontend totals: annotator 904 passed (34 files), ui-core 806 passed (45 files). No
frontend or wire surface is touched by the diff; openapi.json and the generated client
are unchanged.

Found, not fixed

The local-inference extra does not declare torchvision, so SAM 2 cannot be loaded
from a clean install — #457.
With the resolver fixed and the extra exactly as declared,
the same call reaches:

family_of -> 'sam2_video'   in SEGMENTER_FAMILIES=True
RAISED ImportError:
Sam2ImageProcessor requires the Torchvision library but it was not found in your
environment.

This PR is still the right fix and is a prerequisite: it turns a false sentence about the
user's model into an honest one from transformers about a missing library. But the
suggested default model cannot run until #457 lands, and #457 is a pyproject.toml and
lockfile change, outside this task's stated scope.

pyproject.toml's comment on the extra is false at HEAD — it says "uv.lock does not
carry extras", and the lock carries provides-extras = ["local-inference"] with all four
packages pinned. Carried over from #455's own "found, not fixed"; still nobody's diff.

Out of scope, and untouched

The suggest route, the editor, and the D1 default model id — the id is correct and the
resolver was wrong. No adapter changed. No new dependency.

Closes #456

…pe refuses

The adapter is chosen from the `model_type` the model's own config declares.
`SEGMENTER_FAMILIES` held only `sam2`, and the published SAM 2 checkpoints —
including `facebook/sam2-hiera-base-plus`, the id the connection form suggests —
declare `sam2_video`. That fell through to the detector adapter, which refused a
click by saying the model "answers text prompts": a confident sentence about
some other model.

Both spellings of the architecture are now named, and the fallback is gone.
Resolution is by membership of two disjoint sets, and a `model_type` in neither —
including the empty string an unreadable config yields — is refused with what was
declared and what this build supports. A guess is invisible until the wrong
adapter reports the loss in its own vocabulary.

The detector set is `grounding-dino` and `mm-grounding-dino` rather than every
zero-shot detector `transformers` registers: the adapter post-processes with a
signature only that family takes.

Closes #456
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.

Family resolver misclassifies sam2_video as text-prompt; unknown model types fall through instead of refusing

1 participant