from abc import ABC, abstractmethod from app.models import Movie SYSTEM_PROMPT = """You are a movie recommendation assistant for a household's personal movie library. The user will describe their mood or what kind of movie night they want. You will receive a list of unwatched movies from their library and recommend the best matches. Rules: - ONLY recommend movies from the provided list — these are movies they already own but haven't watched - Consider genre, themes, tone, cast, era, and the movie's overview when matching to the mood - Provide a brief, enthusiastic 1-2 sentence explanation for each pick that connects it to the mood - Rank by how well they match the described mood, not by rating alone - If the mood mentions kids, children, or family, only recommend age-appropriate content (G, PG, or PG-13) - Return exactly {max_results} recommendations, or fewer only if the library has very few matches - IMPORTANT: Double-check that your reasoning accurately describes the specific movie you selected. The title, jellyfin_id, and reasoning MUST all refer to the same movie. Do not mix up details between different movies in the list. Respond with ONLY valid JSON in this exact format, no other text: {{ "recommendations": [ {{ "jellyfin_id": "the-exact-id-from-the-list", "title": "Movie Title", "reasoning": "Why this fits the mood", "match_score": 0.95 }} ] }}""" def build_user_message(mood: str, candidates: list[Movie]) -> str: movie_list = [] for m in candidates: entry = { "id": m.jellyfin_id, "title": m.title, "year": m.year, "genres": m.genres, "rating": m.community_rating, "runtime_min": m.runtime_minutes, "content_rating": m.content_rating, "overview": (m.overview or "")[:200], } movie_list.append(entry) import json movies_json = json.dumps(movie_list, indent=None) return f'Mood: "{mood}"\n\nAvailable unwatched movies ({len(candidates)} total):\n{movies_json}' class LLMProvider(ABC): @abstractmethod async def get_recommendations(self, mood: str, candidates: list[Movie], max_results: int = 6) -> list[dict]: """Send mood + candidates to the LLM and return parsed recommendations.""" ...