fix: persist all found candidates and guard duplicate adapter names
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
|
from dataclasses import replace
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import psycopg
|
import psycopg
|
||||||
|
|
||||||
from lyra_worker.adapters.base import SourceAdapter
|
from lyra_worker.adapters.base import SourceAdapter
|
||||||
|
from lyra_worker.confidence import score_confidence
|
||||||
from lyra_worker.quality import quality_class
|
from lyra_worker.quality import quality_class
|
||||||
from lyra_worker.ranker import rank_candidates
|
from lyra_worker.ranker import rank_candidates
|
||||||
from lyra_worker.types import Candidate, MBTarget
|
from lyra_worker.types import Candidate, MBTarget
|
||||||
@@ -103,6 +105,10 @@ def run_pipeline(
|
|||||||
dest_root: str = "/music",
|
dest_root: str = "/music",
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Real staged acquisition using source-agnostic adapters. Fakes in this plan."""
|
"""Real staged acquisition using source-agnostic adapters. Fakes in this plan."""
|
||||||
|
names = [a.name for a in adapters]
|
||||||
|
if len(names) != len(set(names)):
|
||||||
|
raise ValueError(f"adapter names must be unique, got {names}")
|
||||||
|
|
||||||
# 1. intake
|
# 1. intake
|
||||||
_set_state(conn, job_id, "matching", "intake")
|
_set_state(conn, job_id, "matching", "intake")
|
||||||
target = _load_target(conn, job_id)
|
target = _load_target(conn, job_id)
|
||||||
@@ -116,16 +122,17 @@ def run_pipeline(
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
return
|
return
|
||||||
|
|
||||||
# 2. match
|
# 2. match — search all adapters; score + persist EVERY candidate found
|
||||||
_set_state(conn, job_id, "matching", "match")
|
_set_state(conn, job_id, "matching", "match")
|
||||||
candidates: list[Candidate] = []
|
found: list[Candidate] = []
|
||||||
for adapter in adapters:
|
for adapter in adapters:
|
||||||
candidates.extend(adapter.search(target))
|
for c in adapter.search(target):
|
||||||
|
found.append(replace(c, confidence=score_confidence(target, c)))
|
||||||
|
_persist_candidates(conn, job_id, found)
|
||||||
|
|
||||||
# 3. rank
|
# 3. rank
|
||||||
_set_state(conn, job_id, "matched", "rank")
|
_set_state(conn, job_id, "matched", "rank")
|
||||||
ranked = rank_candidates(target, candidates, min_confidence)
|
ranked = rank_candidates(target, found, min_confidence)
|
||||||
_persist_candidates(conn, job_id, ranked)
|
|
||||||
if not ranked:
|
if not ranked:
|
||||||
_fail(conn, job_id, "no candidate above confidence threshold")
|
_fail(conn, job_id, "no candidate above confidence threshold")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -99,3 +99,32 @@ def test_dedupe_skips_already_in_library(conn):
|
|||||||
cur.execute('SELECT count(*) FROM "LibraryItem" WHERE artist = %s AND album = %s',
|
cur.execute('SELECT count(*) FROM "LibraryItem" WHERE artist = %s AND album = %s',
|
||||||
("Radiohead", "In Rainbows"))
|
("Radiohead", "In Rainbows"))
|
||||||
assert cur.fetchone()[0] == 1 # not duplicated
|
assert cur.fetchone()[0] == 1 # not duplicated
|
||||||
|
|
||||||
|
|
||||||
|
def test_below_threshold_candidates_are_persisted_for_diagnosis(conn):
|
||||||
|
from lyra_worker.adapters.fakes import FakeQobuz
|
||||||
|
from lyra_worker.types import MBTarget
|
||||||
|
|
||||||
|
class WrongAlbumQobuz(FakeQobuz):
|
||||||
|
def search(self, target):
|
||||||
|
# returns a candidate for a different album -> scores below the gate
|
||||||
|
return super().search(MBTarget(artist=target.artist, album="Some Completely Unrelated Title"))
|
||||||
|
|
||||||
|
job_id = insert_request(conn, artist="Radiohead", album="In Rainbows")
|
||||||
|
claim_next(conn)
|
||||||
|
run_pipeline(conn, job_id, [WrongAlbumQobuz()], dest_root="/tmp/lib")
|
||||||
|
|
||||||
|
assert _job_state(conn, job_id)[0] == "needs_attention"
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute('SELECT count(*) FROM "Candidate" WHERE "jobId" = %s', (job_id,))
|
||||||
|
assert cur.fetchone()[0] >= 1 # found-but-rejected candidate retained for diagnosis
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_adapter_names_raise(conn):
|
||||||
|
import pytest
|
||||||
|
from lyra_worker.adapters.fakes import FakeQobuz
|
||||||
|
|
||||||
|
job_id = insert_request(conn, artist="Radiohead", album="In Rainbows")
|
||||||
|
claim_next(conn)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
run_pipeline(conn, job_id, [FakeQobuz(), FakeQobuz()], dest_root="/tmp/lib")
|
||||||
|
|||||||
Reference in New Issue
Block a user