import google.generativeai as genai from typing import List, Dict, Any import json from app.config import get_settings class BookRecommender: """AI-powered book recommendation engine using Gemini.""" def __init__(self): settings = get_settings() if not settings.gemini_api_key: raise ValueError("GEMINI_API_KEY not configured") genai.configure(api_key=settings.gemini_api_key) self.model = genai.GenerativeModel('gemini-pro') async def generate_recommendations( self, reading_history: List[Dict[str, Any]], num_recommendations: int = 5 ) -> List[Dict[str, Any]]: """ Generate book recommendations based on reading history. Args: reading_history: List of books the user has read/listened to num_recommendations: Number of recommendations to generate Returns: List of recommended books with title, author, description, and reason """ # Build context from reading history history_text = self._format_reading_history(reading_history) # Create prompt for Gemini prompt = f"""Based on this reading history, recommend {num_recommendations} audiobooks that this person would enjoy. Reading History: {history_text} For each recommendation, provide: 1. Title 2. Author 3. Brief description (2-3 sentences) 4. Why you're recommending it based on their reading history (1-2 sentences) 5. Genres (as a list) Format your response as a JSON array with objects containing: title, author, description, reason, genres. Only respond with the JSON array, no additional text.""" # Call Gemini API response = self.model.generate_content(prompt) # Parse response response_text = response.text try: recommendations = json.loads(response_text) return recommendations except json.JSONDecodeError: # If Claude didn't return valid JSON, try to extract it # Look for JSON array in the response start = response_text.find("[") end = response_text.rfind("]") + 1 if start >= 0 and end > start: recommendations = json.loads(response_text[start:end]) return recommendations else: raise ValueError("Failed to parse recommendations from AI response") def _format_reading_history(self, history: List[Dict[str, Any]]) -> str: """Format reading history for the AI prompt.""" formatted = [] for i, book in enumerate(history, 1): title = book.get("title", "Unknown") author = book.get("author", "Unknown") genres = book.get("genres", []) progress = book.get("progress", 0) is_finished = book.get("is_finished", False) status = "Finished" if is_finished else f"In Progress ({int(progress * 100)}%)" genre_str = ", ".join(genres) if genres else "Unknown" formatted.append( f"{i}. {title} by {author}\n" f" Status: {status}\n" f" Genres: {genre_str}" ) return "\n\n".join(formatted) async def explain_recommendation( self, book_title: str, book_author: str, reading_history: List[Dict[str, Any]] ) -> str: """ Get a detailed explanation for why a specific book is recommended. Args: book_title: Title of the recommended book book_author: Author of the recommended book reading_history: User's reading history Returns: Detailed explanation text """ history_text = self._format_reading_history(reading_history) prompt = f"""Explain in detail why "{book_title}" by {book_author} would be a good recommendation for someone with this reading history: {history_text} Provide a thoughtful 2-3 paragraph explanation connecting specific aspects of the recommended book to their reading preferences.""" response = self.model.generate_content(prompt) return response.text