Apply the picker task filter to local model sections

LM Studio, ./models and custom-folder rows ignored it, so the Images picker listed
chat GGUFs that 400 on a diffusion load. The backend already tags every local model
with a task for this purpose.
This commit is contained in:
Unsloth 2026-07-26 04:39:14 -07:00
commit 4c0a95c7ca
2 changed files with 21 additions and 2 deletions

View file

@ -2138,13 +2138,16 @@ export function HubModelPicker({
sortLocalModels(
lmStudioModels.filter(
(m) =>
// The backend tags every local model with its task for exactly this: on the
// Images/Video pages a chat GGUF must not be offered (it would 400 on load).
passesTaskGate(m.task, m.model_id ?? m.id, task) &&
localModelMatchesFormat(m, formatFilter) && matchesLocalQuery(m),
),
downloadedSort,
loadTimes,
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[lmStudioModels, downloadedSort, formatFilter, loadTimes, localQuery],
[lmStudioModels, downloadedSort, formatFilter, loadTimes, localQuery, task],
);
// Local ./models entries. Chat-only Unsloth runs GGUF (any host) and MLX (Mac
// only), so raw checkpoints there are hidden (mirrors the cached non-GGUF
@ -2156,6 +2159,7 @@ export function HubModelPicker({
sortLocalModels(
localDirModels.filter(
(m) =>
passesTaskGate(m.task, m.model_id ?? m.id, task) &&
(!chatOnly ||
Boolean(task) ||
localModelIsGguf(m) ||
@ -2183,13 +2187,14 @@ export function HubModelPicker({
sortLocalModels(
customFolderModels.filter(
(m) =>
passesTaskGate(m.task, m.model_id ?? m.id, task) &&
localModelMatchesFormat(m, formatFilter) && matchesLocalQuery(m),
),
customSort,
loadTimes,
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[customFolderModels, customSort, formatFilter, loadTimes, localQuery],
[customFolderModels, customSort, formatFilter, loadTimes, localQuery, task],
);
// Fine-tuned models for the On Device "Fine-tuned" section: flat, query-

View file

@ -637,3 +637,17 @@ def test_staged_downloads_always_scope_their_files():
assert "scopeId," in body and "files: current.files," in body
assert "? null" not in body and "? undefined" not in body
assert "const activeVariant = current ? scopedVariant(scopeId) : null;" in src
def test_local_model_sections_respect_the_task_filter():
"""LM Studio / ./models / custom-folder rows must honour the picker's task filter.
The backend tags every local model with a task for exactly this; without the gate the
Images picker listed chat GGUFs (which 400 on a diffusion load) and buried the
diffusion models the page can actually run."""
src = _read("features/model-picker/components/model-selector/pickers.tsx")
for memo in ("sortedLmStudio", "sortedLocalDir", "sortedCustomFolderModels"):
block = re.search(rf"const {memo} = useMemo\(.*?\n \);", src, re.S)
assert block, f"{memo} not found"
assert "passesTaskGate(m.task" in block.group(0), (
f"{memo} does not apply the task gate"
)