2026-03-14 19:20:56 -07:00
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
2026-03-15 10:34:43 -07:00
- 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.
2026-03-14 19:20:56 -07:00
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\n Available 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."""
...