Initial commit — Movie Night media discovery app
AI-powered web app that recommends unwatched movies from a Jellyfin library based on natural language mood input. Jellyfin auth, modular LLM backend (Claude/OpenAI/Ollama), two-tier pre-filter + AI ranking, mobile-responsive dark theme UI with poster cards and deep links. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
import anthropic
|
||||
|
||||
from app.config import settings
|
||||
from app.models import Movie
|
||||
from app.services.llm.base import SYSTEM_PROMPT, LLMProvider, build_user_message
|
||||
|
||||
logger = logging.getLogger("movie-night.llm.anthropic")
|
||||
|
||||
|
||||
class AnthropicProvider(LLMProvider):
|
||||
def __init__(self):
|
||||
self.client = anthropic.AsyncAnthropic(api_key=settings.llm_api_key)
|
||||
self.model = settings.llm_model or "claude-sonnet-4-6"
|
||||
|
||||
async def get_recommendations(self, mood: str, candidates: list[Movie], max_results: int = 6) -> list[dict]:
|
||||
system = SYSTEM_PROMPT.format(max_results=max_results)
|
||||
user_msg = build_user_message(mood, candidates)
|
||||
|
||||
logger.info(f"Calling Anthropic ({self.model}) with {len(candidates)} candidates")
|
||||
|
||||
response = await self.client.messages.create(
|
||||
model=self.model,
|
||||
max_tokens=2048,
|
||||
system=system,
|
||||
messages=[{"role": "user", "content": user_msg}],
|
||||
)
|
||||
|
||||
text = response.content[0].text.strip()
|
||||
|
||||
# Parse JSON response
|
||||
try:
|
||||
data = json.loads(text)
|
||||
return data.get("recommendations", [])
|
||||
except json.JSONDecodeError:
|
||||
# Try to extract JSON from the response
|
||||
start = text.find("{")
|
||||
end = text.rfind("}") + 1
|
||||
if start >= 0 and end > start:
|
||||
try:
|
||||
data = json.loads(text[start:end])
|
||||
return data.get("recommendations", [])
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
logger.error(f"Failed to parse LLM response: {text[:200]}")
|
||||
return []
|
||||
Reference in New Issue
Block a user