"""Demo-only pipeline decorator for honest fuzzy replay disclosure.""" import time from collections.abc import AsyncGenerator from contextlib import aclosing from app.adapters.demo.catalog import DemoCatalog from app.adapters.demo.recommender import DemoRecommender, parse_recorded_intent from app.adapters.demo.scenario import select_replay_scenario from app.config import Settings from app.domain.models import ConversationTurn, PreviousRecommendation, Track from app.pipeline.event import PipelineEvent, PipelineMetadataEvent, PipelineWarningEvent from app.pipeline.grounding import Grounder from app.pipeline.orchestrator import RecommendationPipeline from app.ports.protocols import MusicCatalog DEMO_REPLAY_CODE = "demo_replay" class DemoReplayPipeline: """Decorate the real pipeline with a fuzzy-replay warning event.""" def __init__(self, settings: Settings) -> None: """Create one cache-preserving pipeline with a replay recommender.""" self.settings = settings self.pipeline = RecommendationPipeline(DemoRecommender(settings), settings) async def stream( self, session_id: str, request_id: str, catalog: MusicCatalog, query: str, history: tuple[ConversationTurn, ...], previous_recommendations: tuple[PreviousRecommendation, ...], ) -> AsyncGenerator[PipelineEvent]: """Stream the selected fixture and disclose non-exact selection.""" match = select_replay_scenario(query, bool(previous_recommendations)) seeded_pool = await self._prepare_refinement_pool(catalog) event_stream = self.pipeline.stream( session_id, request_id, catalog, query, history, previous_recommendations, seeded_pool=seeded_pool, ) async with aclosing(event_stream) as events: async for event in events: yield event if isinstance(event, PipelineMetadataEvent) and not match.is_exact: yield PipelineWarningEvent( code=DEMO_REPLAY_CODE, message=f'Demo replay is showing the recorded "{match.chip}" scenario.', ) async def _prepare_refinement_pool( self, catalog: MusicCatalog, ) -> tuple[Track, ...] | None: if not isinstance(catalog, DemoCatalog): return None intent_bodies = catalog.cassette.intent_response_bodies if len(intent_bodies) < 2: return None # Refinement cassettes record the parent intent first and refinement intent last. parent_intent = parse_recorded_intent(intent_bodies[0]) result = await Grounder(self.settings).ground( catalog, parent_intent.candidates, # The recorded parent pool is listener-neutral, so replay has no known-track exclusions. frozenset(), parent_intent.familiarity, self.settings.rerank_count + self.settings.rerank_pool_buffer, time.monotonic() + self.settings.request_deadline_seconds, ) return result.tracks