fix: tolerate spotify version suffixes and surface rerank failures

This commit is contained in:
Justin Visser 2026-08-10 14:02:54 +02:00
parent ce652d3114
commit 0ccc9a5d4e
6 changed files with 92 additions and 29 deletions

View file

@ -177,27 +177,44 @@ class RecommendationPipeline:
history: tuple[ConversationTurn, ...],
selection: _TrackSelection,
) -> AsyncGenerator[PipelineTrackEvent | PipelineWarningEvent]:
"""Stream one rerank, retry once on invalid output, then fall back."""
correction: str | None = None
for _ in range(2):
if selection.is_full:
return
try:
async for event in self._rerank_once(
intent, taste_summary, history, selection, correction
):
yield event
return
except RecommenderOutputError as error:
correction = (
f"Validation failed: {error}."
f" Already emitted track ids: {selection.describe_selected_ids()}."
)
"""Stream the rerank with one corrected retry, then fall back."""
try:
async for event in self._rerank_with_one_retry(
intent, taste_summary, history, selection
):
yield event
return
except RecommenderOutputError as error:
_log_rerank_failure(attempt=2, error=error)
yield PipelineWarningEvent(code=RERANK_FALLBACK_CODE, message=RERANK_FALLBACK_MESSAGE)
for event in selection.fill_from_pool(RERANK_FALLBACK_JUSTIFICATION):
yield event
async def _rerank_with_one_retry(
self,
intent: Intent,
taste_summary: str,
history: tuple[ConversationTurn, ...],
selection: _TrackSelection,
) -> AsyncGenerator[PipelineTrackEvent]:
"""Rerank once; on invalid output, retry once with a correction."""
try:
async for event in self._rerank_once(intent, taste_summary, history, selection, None):
yield event
return
except RecommenderOutputError as error:
_log_rerank_failure(attempt=1, error=error)
if selection.is_full:
return
correction = (
f"Validation failed: {error}."
f" Already emitted track ids: {selection.describe_selected_ids()}."
)
async for event in self._rerank_once(intent, taste_summary, history, selection, correction):
yield event
async def _rerank_once(
self,
intent: Intent,
@ -219,6 +236,11 @@ class RecommendationPipeline:
yield selection.select(item.track_id, item.justification)
def _log_rerank_failure(attempt: int, error: RecommenderOutputError) -> None:
"""Log one invalid rerank attempt with its validation reason."""
structlog.get_logger().warning("rerank_attempt_failed", attempt=attempt, error=str(error))
def _log_completion(selection: _TrackSelection, taste: CompressedTasteProfile) -> None:
"""Log how many recommendations were served and how many are new."""
new_track_count = sum(track.id not in taste.known_track_ids for track in selection.selected)