Dinamush Cursor commited on
Commit ·
1bda507
1
Parent(s): 31724e1
feat: ML-Danbooru primary runs only route to loli
Browse filesCo-authored-by: Cursor <cursoragent@cursor.com>
- backend/app/api.py +9 -0
- backend/app/services.py +18 -0
- backend/tests/test_ml_loli_only.py +14 -0
- frontend/src/App.jsx +1 -1
backend/app/api.py
CHANGED
|
@@ -50,6 +50,7 @@ from .services import (
|
|
| 50 |
resolve_settings,
|
| 51 |
sanitize_folder_name,
|
| 52 |
categories_exclude_dirs,
|
|
|
|
| 53 |
scan_images,
|
| 54 |
)
|
| 55 |
from .taxonomy import (
|
|
@@ -1895,6 +1896,9 @@ def start_run(payload: StartRunRequest) -> StartRunResponse:
|
|
| 1895 |
selected_folders = payload.selected_folders
|
| 1896 |
if not selected_folders:
|
| 1897 |
selected_folders = list(current.selected_tags)
|
|
|
|
|
|
|
|
|
|
| 1898 |
|
| 1899 |
try:
|
| 1900 |
known_tags = load_known_tags(TAGS_CSV)
|
|
@@ -1962,6 +1966,8 @@ def start_run(payload: StartRunRequest) -> StartRunResponse:
|
|
| 1962 |
if real_life_filter
|
| 1963 |
else ""
|
| 1964 |
)
|
|
|
|
|
|
|
| 1965 |
return StartRunResponse(
|
| 1966 |
run_id=run_id,
|
| 1967 |
status="pending",
|
|
@@ -2038,6 +2044,9 @@ def reclassify_run(run_id: int, payload: ReclassifyRequest) -> ReclassifyRespons
|
|
| 2038 |
status_code=400,
|
| 2039 |
detail="No selected tags in settings; save destination tags before reclassifying.",
|
| 2040 |
)
|
|
|
|
|
|
|
|
|
|
| 2041 |
try:
|
| 2042 |
known_tags = load_known_tags(TAGS_CSV)
|
| 2043 |
mappings = discover_tag_folders(categories_root, known_tags, selected_folders)
|
|
|
|
| 50 |
resolve_settings,
|
| 51 |
sanitize_folder_name,
|
| 52 |
categories_exclude_dirs,
|
| 53 |
+
destination_folders_for_tagger,
|
| 54 |
scan_images,
|
| 55 |
)
|
| 56 |
from .taxonomy import (
|
|
|
|
| 1896 |
selected_folders = payload.selected_folders
|
| 1897 |
if not selected_folders:
|
| 1898 |
selected_folders = list(current.selected_tags)
|
| 1899 |
+
selected_folders = destination_folders_for_tagger(
|
| 1900 |
+
selected_folders, current.tagger_model
|
| 1901 |
+
)
|
| 1902 |
|
| 1903 |
try:
|
| 1904 |
known_tags = load_known_tags(TAGS_CSV)
|
|
|
|
| 1966 |
if real_life_filter
|
| 1967 |
else ""
|
| 1968 |
)
|
| 1969 |
+
if not real_life_filter and current.tagger_model == "ml_danbooru":
|
| 1970 |
+
mode_note += " ML-Danbooru mode: loli destination only."
|
| 1971 |
return StartRunResponse(
|
| 1972 |
run_id=run_id,
|
| 1973 |
status="pending",
|
|
|
|
| 2044 |
status_code=400,
|
| 2045 |
detail="No selected tags in settings; save destination tags before reclassifying.",
|
| 2046 |
)
|
| 2047 |
+
selected_folders = destination_folders_for_tagger(
|
| 2048 |
+
selected_folders, payload.tagger_model
|
| 2049 |
+
)
|
| 2050 |
try:
|
| 2051 |
known_tags = load_known_tags(TAGS_CSV)
|
| 2052 |
mappings = discover_tag_folders(categories_root, known_tags, selected_folders)
|
backend/app/services.py
CHANGED
|
@@ -665,3 +665,21 @@ def resolve_settings(
|
|
| 665 |
),
|
| 666 |
}
|
| 667 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
),
|
| 666 |
}
|
| 667 |
)
|
| 668 |
+
|
| 669 |
+
|
| 670 |
+
def destination_folders_for_tagger(
|
| 671 |
+
selected_folders: list[str],
|
| 672 |
+
tagger_model: str,
|
| 673 |
+
*,
|
| 674 |
+
real_life_filter: bool = False,
|
| 675 |
+
) -> list[str]:
|
| 676 |
+
"""ML-Danbooru primary runs only compete for the loli destination.
|
| 677 |
+
|
| 678 |
+
Benchmarks showed strong loli recall for ML, but weak/noisy behavior on the
|
| 679 |
+
broader preferred-folder set — so full multi-folder routing stays on WD.
|
| 680 |
+
"""
|
| 681 |
+
if real_life_filter:
|
| 682 |
+
return list(selected_folders)
|
| 683 |
+
if str(tagger_model or "") == "ml_danbooru":
|
| 684 |
+
return ["loli"]
|
| 685 |
+
return list(selected_folders)
|
backend/tests/test_ml_loli_only.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ML-Danbooru primary runs are loli-only (strong recall on that folder)."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from app.services import destination_folders_for_tagger
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_ml_danbooru_forces_loli_only_destinations() -> None:
|
| 9 |
+
selected = ["Voyeur", "fellatio", "loli", "monster_girl"]
|
| 10 |
+
assert destination_folders_for_tagger(selected, "ml_danbooru") == ["loli"]
|
| 11 |
+
assert destination_folders_for_tagger(selected, "wd_swinv2_v3") == selected
|
| 12 |
+
assert destination_folders_for_tagger(
|
| 13 |
+
["real_life"], "ml_danbooru", real_life_filter=True
|
| 14 |
+
) == ["real_life"]
|
frontend/src/App.jsx
CHANGED
|
@@ -23,7 +23,7 @@ const TAGGER_MODELS = [
|
|
| 23 |
{
|
| 24 |
id: "ml_danbooru",
|
| 25 |
label: "ML-Danbooru",
|
| 26 |
-
help: "
|
| 27 |
},
|
| 28 |
{
|
| 29 |
id: "wd_swinv2_v3",
|
|
|
|
| 23 |
{
|
| 24 |
id: "ml_danbooru",
|
| 25 |
label: "ML-Danbooru",
|
| 26 |
+
help: "Loli-only classifier — strong recall on that folder (not used for full multi-folder runs).",
|
| 27 |
},
|
| 28 |
{
|
| 29 |
id: "wd_swinv2_v3",
|