feat(provenance): ML stops surfacing the artist category
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,6 @@ from .embedder import MODEL_VERSION as SIGLIP_VERSION
|
|||||||
|
|
||||||
ELIGIBLE_KINDS = {
|
ELIGIBLE_KINDS = {
|
||||||
TagKind.character,
|
TagKind.character,
|
||||||
TagKind.artist,
|
|
||||||
TagKind.fandom,
|
TagKind.fandom,
|
||||||
TagKind.general,
|
TagKind.general,
|
||||||
TagKind.series,
|
TagKind.series,
|
||||||
|
|||||||
@@ -48,12 +48,13 @@ class SuggestionService:
|
|||||||
).scalar_one()
|
).scalar_one()
|
||||||
|
|
||||||
def _threshold_for(self, s: MLSettings, category: str) -> float:
|
def _threshold_for(self, s: MLSettings, category: str) -> float:
|
||||||
|
# 'artist' intentionally absent (FC-2d-vii-c) — falls through to
|
||||||
|
# the 1.01 "never surfaces" default like any unsurfaced category.
|
||||||
return {
|
return {
|
||||||
"artist": s.suggestion_threshold_artist,
|
|
||||||
"character": s.suggestion_threshold_character,
|
"character": s.suggestion_threshold_character,
|
||||||
"copyright": s.suggestion_threshold_copyright,
|
"copyright": s.suggestion_threshold_copyright,
|
||||||
"general": s.suggestion_threshold_general,
|
"general": s.suggestion_threshold_general,
|
||||||
}.get(category, 1.01) # 1.01 => never surfaces (unsurfaced category)
|
}.get(category, 1.01)
|
||||||
|
|
||||||
async def for_image(self, image_id: int) -> SuggestionList:
|
async def for_image(self, image_id: int) -> SuggestionList:
|
||||||
img = await self.session.get(ImageRecord, image_id)
|
img = await self.session.get(ImageRecord, image_id)
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ STORE_FLOOR = float(os.environ.get("TAGGER_STORE_FLOOR", "0.05"))
|
|||||||
|
|
||||||
# The categories FC-2b surfaces in the UI. Others (meta/rating/year) are
|
# The categories FC-2b surfaces in the UI. Others (meta/rating/year) are
|
||||||
# still stored but the suggestion service filters them out.
|
# still stored but the suggestion service filters them out.
|
||||||
SURFACED_CATEGORIES = {"artist", "character", "copyright", "general"}
|
# FC-2d-vii-c: 'artist' retired — artist identity is acquisition-derived
|
||||||
|
# (image_record.artist_id), never ML-inferred. Raw predictions are still
|
||||||
|
# stored at STORE_FLOOR but artist never surfaces.
|
||||||
|
SURFACED_CATEGORIES = {"character", "copyright", "general"}
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.integration
|
||||||
|
|
||||||
|
|
||||||
|
def test_artist_not_surfaced():
|
||||||
|
from backend.app.services.ml.tagger import SURFACED_CATEGORIES
|
||||||
|
assert "artist" not in SURFACED_CATEGORIES
|
||||||
|
|
||||||
|
|
||||||
|
def test_artist_not_centroid_eligible():
|
||||||
|
from backend.app.models import TagKind
|
||||||
|
from backend.app.services.ml.centroids import ELIGIBLE_KINDS
|
||||||
|
assert TagKind.artist not in ELIGIBLE_KINDS
|
||||||
|
|
||||||
|
|
||||||
|
def test_threshold_for_artist_is_unsurfaced():
|
||||||
|
from backend.app.services.ml.suggestions import SuggestionService
|
||||||
|
|
||||||
|
class _S:
|
||||||
|
suggestion_threshold_character = 0.5
|
||||||
|
suggestion_threshold_copyright = 0.5
|
||||||
|
suggestion_threshold_general = 0.5
|
||||||
|
|
||||||
|
svc = SuggestionService.__new__(SuggestionService)
|
||||||
|
# 'artist' must fall through to the 1.01 "never surfaces" default
|
||||||
|
assert svc._threshold_for(_S(), "artist") == 1.01
|
||||||
Reference in New Issue
Block a user