recommender.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import google.generativeai as genai
  2. from typing import List, Dict, Any
  3. import json
  4. from app.config import get_settings
  5. class BookRecommender:
  6. """AI-powered book recommendation engine using Gemini."""
  7. def __init__(self):
  8. settings = get_settings()
  9. if not settings.gemini_api_key:
  10. raise ValueError("GEMINI_API_KEY not configured")
  11. genai.configure(api_key=settings.gemini_api_key)
  12. self.model = genai.GenerativeModel('gemini-1.5-flash')
  13. async def generate_recommendations(
  14. self,
  15. reading_history: List[Dict[str, Any]],
  16. num_recommendations: int = 5
  17. ) -> List[Dict[str, Any]]:
  18. """
  19. Generate book recommendations based on reading history.
  20. Args:
  21. reading_history: List of books the user has read/listened to
  22. num_recommendations: Number of recommendations to generate
  23. Returns:
  24. List of recommended books with title, author, description, and reason
  25. """
  26. # Build context from reading history
  27. history_text = self._format_reading_history(reading_history)
  28. # Create prompt for Gemini
  29. prompt = f"""Based on this reading history, recommend {num_recommendations} audiobooks that this person would enjoy.
  30. Reading History:
  31. {history_text}
  32. For each recommendation, provide:
  33. 1. Title
  34. 2. Author
  35. 3. Brief description (2-3 sentences)
  36. 4. Why you're recommending it based on their reading history (1-2 sentences)
  37. 5. Genres (as a list)
  38. Format your response as a JSON array with objects containing: title, author, description, reason, genres.
  39. Only respond with the JSON array, no additional text."""
  40. # Call Gemini API
  41. response = self.model.generate_content(prompt)
  42. # Parse response
  43. response_text = response.text
  44. try:
  45. recommendations = json.loads(response_text)
  46. return recommendations
  47. except json.JSONDecodeError:
  48. # If Claude didn't return valid JSON, try to extract it
  49. # Look for JSON array in the response
  50. start = response_text.find("[")
  51. end = response_text.rfind("]") + 1
  52. if start >= 0 and end > start:
  53. recommendations = json.loads(response_text[start:end])
  54. return recommendations
  55. else:
  56. raise ValueError("Failed to parse recommendations from AI response")
  57. def _format_reading_history(self, history: List[Dict[str, Any]]) -> str:
  58. """Format reading history for the AI prompt."""
  59. formatted = []
  60. for i, book in enumerate(history, 1):
  61. title = book.get("title", "Unknown")
  62. author = book.get("author", "Unknown")
  63. genres = book.get("genres", [])
  64. progress = book.get("progress", 0)
  65. is_finished = book.get("is_finished", False)
  66. status = "Finished" if is_finished else f"In Progress ({int(progress * 100)}%)"
  67. genre_str = ", ".join(genres) if genres else "Unknown"
  68. formatted.append(
  69. f"{i}. {title} by {author}\n"
  70. f" Status: {status}\n"
  71. f" Genres: {genre_str}"
  72. )
  73. return "\n\n".join(formatted)
  74. async def explain_recommendation(
  75. self,
  76. book_title: str,
  77. book_author: str,
  78. reading_history: List[Dict[str, Any]]
  79. ) -> str:
  80. """
  81. Get a detailed explanation for why a specific book is recommended.
  82. Args:
  83. book_title: Title of the recommended book
  84. book_author: Author of the recommended book
  85. reading_history: User's reading history
  86. Returns:
  87. Detailed explanation text
  88. """
  89. history_text = self._format_reading_history(reading_history)
  90. prompt = f"""Explain in detail why "{book_title}" by {book_author} would be a good recommendation for someone with this reading history:
  91. {history_text}
  92. Provide a thoughtful 2-3 paragraph explanation connecting specific aspects of the recommended book to their reading preferences."""
  93. response = self.model.generate_content(prompt)
  94. return response.text