feat(recipes): add Unknown base-model filter bucket for undetermined recipes

Normalize undetermined recipe base_model to None in RecipeFormatParser
(previously ''). get_base_models now reports an "Unknown" bucket backed
by a dedicated __unknown__ marker, and the listing filter matches it
against recipes whose base model is falsy. Frontend renders the bucket
label as "Unknown" while filtering via the marker.

Tests: handler, scanner, parser, and frontend filtering.
This commit is contained in:
Will Miao
2026-08-31 09:09:53 +08:00
parent 8d46d26abe
commit 2a3c632dc5
8 changed files with 214 additions and 11 deletions
+54 -1
View File
@@ -12,7 +12,7 @@ from py.services import model_scanner as model_scanner_module
from py.services.model_cache import ModelCache
from py.services.model_hash_index import ModelHashIndex
from py.services.model_scanner import CacheBuildResult, ModelScanner
from py.services.recipe_scanner import RecipeScanner
from py.services.recipe_scanner import RecipeScanner, UNKNOWN_BASE_MODEL_FILTER
from py.services import settings_manager as settings_manager_module
from py.utils.models import BaseModelMetadata
from py.utils.utils import calculate_recipe_fingerprint
@@ -1954,6 +1954,59 @@ async def test_get_paginated_data_filters_by_favorite(recipe_scanner):
assert len(result_fav_false["items"]) == 2
@pytest.mark.asyncio
async def test_get_paginated_data_filters_by_base_model_unknown_bucket(recipe_scanner):
scanner, _ = recipe_scanner
await scanner.add_recipe(
{
"id": "known",
"file_path": "path/known.png",
"title": "Known Base Model",
"modified": 1.0,
"created_date": 1.0,
"base_model": "SDXL 1.0",
"loras": [],
}
)
await scanner.add_recipe(
{
"id": "unknown",
"file_path": "path/unknown.png",
"title": "Unknown Base Model",
"modified": 2.0,
"created_date": 2.0,
"base_model": None,
"loras": [],
}
)
await asyncio.sleep(0)
await _wait_for_resort(scanner)
# Exact-name filter matches only the recipe with that base model
result_known = await scanner.get_paginated_data(
page=1, page_size=10, filters={"base_model": ["SDXL 1.0"]}
)
assert [item["id"] for item in result_known["items"]] == ["known"]
# Unknown bucket matches recipes whose base model could not be determined
result_unknown = await scanner.get_paginated_data(
page=1,
page_size=10,
filters={"base_model": [UNKNOWN_BASE_MODEL_FILTER]},
)
assert [item["id"] for item in result_unknown["items"]] == ["unknown"]
# Mixing known values with the unknown bucket matches both groups
result_both = await scanner.get_paginated_data(
page=1,
page_size=10,
filters={"base_model": ["SDXL 1.0", UNKNOWN_BASE_MODEL_FILTER]},
)
assert {item["id"] for item in result_both["items"]} == {"known", "unknown"}
@pytest.mark.asyncio
async def test_get_paginated_data_filters_by_prompt(recipe_scanner):
scanner, _ = recipe_scanner