feat(wip): soft title tier — sketch/doodle vocab + ring-loud audit (#1474)
Extends WIP title-tagging to lower-precision cues (sketch/doodle/scribble) safely. - wip_title.py: soft matcher (word-anchored; sketchbook/kadoodle don't trip it); WIP_TITLE_SOFT_SOURCE + soft SQL prefilter; apply_wip_image_tags takes a source arg. - training_data._AUTO_SOURCES += 'wip_title_soft' → the soft tier is PROVISIONAL and never trains the wip head (a finished "sketch" can't pollute it). Only the hard tier (wip_title) + manual train. - ImportSettings.wip_soft_title_tagging_enabled (OFF by default, opt-in). Migration 0087. - importer: hard tier wins, soft is the fallback (source wip_title_soft). - backfill: refactored into a shared _backfill_wip_tier; hard always, soft when enabled. - heads.soft_wip_conflict_audit + daily beat: score soft-tagged images against content heads, flag ring-loud ones (PresentationReview mode=process) for the review strip — the operator's "measure if they got falsely tagged" safety. - api settings toggle; ImportFiltersForm soft toggle. - tests: soft matcher pos/neg; soft source not a training positive; audit flags ring-loud + spares quiet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,7 +47,14 @@ from .attachment_store import AttachmentStore
|
||||
from .audits import single_color
|
||||
from .link_extract import extract_external_links
|
||||
from .thumbnailer import Thumbnailer
|
||||
from .wip_title import apply_wip_image_tags, matches_wip_title, resolve_wip_tag_id
|
||||
from .wip_title import (
|
||||
WIP_TITLE_SOFT_SOURCE,
|
||||
WIP_TITLE_SOURCE,
|
||||
apply_wip_image_tags,
|
||||
matches_soft_wip_title,
|
||||
matches_wip_title,
|
||||
resolve_wip_tag_id,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -999,7 +1006,9 @@ class Importer:
|
||||
removal sticks. The existing catalogue is covered separately by the
|
||||
operator-triggered backfill sweep. Gated by the settings toggle, and
|
||||
best-effort: any failure is logged, never allowed to fail the import."""
|
||||
if not self.settings.wip_title_tagging_enabled:
|
||||
hard_on = self.settings.wip_title_tagging_enabled
|
||||
soft_on = self.settings.wip_soft_title_tagging_enabled
|
||||
if not (hard_on or soft_on):
|
||||
return
|
||||
if record.primary_post_id is None:
|
||||
return
|
||||
@@ -1007,13 +1016,22 @@ class Importer:
|
||||
title = self.session.execute(
|
||||
select(Post.post_title).where(Post.id == record.primary_post_id)
|
||||
).scalar_one_or_none()
|
||||
if not matches_wip_title(title):
|
||||
# HARD tier ("WIP"/"work in progress") wins — higher precision, and it
|
||||
# trains the head; SOFT (sketch/doodle, #1474) is the provisional fallback
|
||||
# that never trains (source wip_title_soft).
|
||||
if hard_on and matches_wip_title(title):
|
||||
source = WIP_TITLE_SOURCE
|
||||
elif soft_on and matches_soft_wip_title(title):
|
||||
source = WIP_TITLE_SOFT_SOURCE
|
||||
else:
|
||||
return
|
||||
if self._wip_tag_id is _UNSET:
|
||||
self._wip_tag_id = resolve_wip_tag_id(self.session)
|
||||
if self._wip_tag_id is None:
|
||||
return
|
||||
apply_wip_image_tags(self.session, [record.id], self._wip_tag_id)
|
||||
apply_wip_image_tags(
|
||||
self.session, [record.id], self._wip_tag_id, source=source
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001 — a tag must never fail an import
|
||||
log.warning(
|
||||
"wip-title auto-tag failed for image %s: %s", record.id, exc
|
||||
|
||||
Reference in New Issue
Block a user