|
1 | 1 | # AI service logic with LLM integration (OpenAI/Gemini) |
2 | 2 | # Implements 'generate_book_note' and 'get_ai_recommendations'. All recommendations MUST be AI-based. |
| 3 | + |
3 | 4 | # Enhanced with comprehensive caching for expensive operations |
4 | 5 |
|
5 | 6 |
|
@@ -219,6 +220,25 @@ def get_category_books_prompt(category: str, vibe_description: str, count: int = |
219 | 220 | - Output the JSON array only. |
220 | 221 | """ |
221 | 222 |
|
| 223 | + @staticmethod |
| 224 | + def get_vibe_check_prompt(vibe_prompt: str, count: int = 3) -> str: |
| 225 | + """ |
| 226 | + Prompt for generating hyper-personalized vibe-based book recommendations. |
| 227 | + """ |
| 228 | + return f"""You are Elara, an emotionally intuitive bookseller. |
| 229 | +A reader is looking for books matching this exact hyper-specific vibe or mood: "{vibe_prompt}" |
| 230 | +
|
| 231 | +Return exactly {count} real, verifiable books that perfectly capture this energy. |
| 232 | +Output ONLY a valid JSON array. No markdown fences. No text before or after. |
| 233 | +Schema: |
| 234 | +[ |
| 235 | + {{ |
| 236 | + "title": "Exact book title", |
| 237 | + "author": "Author full name", |
| 238 | + "reason": "One brilliant, atmospheric sentence explaining why it fits the requested vibe perfectly." |
| 239 | + }} |
| 240 | +] |
| 241 | +""" |
222 | 242 |
|
223 | 243 | class LLMService: |
224 | 244 | """ |
@@ -779,6 +799,46 @@ def get_category_books(category: str, vibe_description: str, count: int = 5) -> |
779 | 799 | logger.info("get_category_books: %d books returned for '%s'", len(valid_books), category) |
780 | 800 | return valid_books |
781 | 801 |
|
| 802 | +def get_vibe_recommendations(vibe_prompt: str, count: int = 3) -> list: |
| 803 | + """ |
| 804 | + Generate a list of real books based on a highly specific natural-language vibe. |
| 805 | + Returns a list of dicts: [{"title": ..., "author": ..., "reason": ...}, ...] |
| 806 | + """ |
| 807 | + if not llm_service.is_available(): |
| 808 | + logger.warning("get_vibe_recommendations: no LLM configured") |
| 809 | + return [] |
| 810 | + |
| 811 | + prompt = PromptTemplates.get_vibe_check_prompt(vibe_prompt, count) |
| 812 | + |
| 813 | + # Utilizing the existing LLMService pipeline |
| 814 | + raw = llm_service.generate_text( |
| 815 | + prompt, |
| 816 | + max_tokens=llm_service.config.get('category_books_max_tokens', 600), |
| 817 | + ) |
| 818 | + |
| 819 | + if not raw: |
| 820 | + logger.error("get_vibe_recommendations: LLM returned None") |
| 821 | + return [] |
| 822 | + |
| 823 | + # Use the file's existing robust JSON extractor |
| 824 | + parsed = _extract_json(raw) |
| 825 | + |
| 826 | + if not isinstance(parsed, list): |
| 827 | + logger.error("get_vibe_recommendations: expected JSON array") |
| 828 | + return [] |
| 829 | + |
| 830 | + # Validate and clean the output |
| 831 | + valid_books = [] |
| 832 | + for item in parsed: |
| 833 | + if isinstance(item, dict) and "title" in item and "author" in item: |
| 834 | + valid_books.append({ |
| 835 | + "title": item["title"], |
| 836 | + "author": item["author"], |
| 837 | + "reason": item.get("reason", "This perfectly matches your requested vibe."), |
| 838 | + }) |
| 839 | + |
| 840 | + logger.info("get_vibe_recommendations: %d books returned for vibe '%s'", len(valid_books), vibe_prompt) |
| 841 | + return valid_books |
782 | 842 |
|
783 | 843 | @cache_mood_tags |
784 | 844 | def get_book_mood_tags_safe(title: str, author: str = "") -> list: |
|
0 commit comments