From 348839be36e6c8924893843e6eb595fda37748e1 Mon Sep 17 00:00:00 2001 From: ajaysi Date: Mon, 6 Apr 2026 15:19:23 +0530 Subject: [PATCH] Fix: Improve podcast analysis LLM prompt and skip bible generation in podcast mode - Add pandas to requirements-podcast.txt for usage tracking - Fix LLM prompt to return plain strings instead of objects for enhanced_ideas - Add object-to-string normalization for LLM responses that return objects - Skip bible generation in podcast-only mode (onboarding disabled) - Skip alerts polling in AlertsBadge when in podcast-only demo mode --- backend/api/podcast/handlers/analysis.py | 71 +++++++++++++++---- backend/requirements-podcast.txt | 3 +- .../src/components/shared/AlertsBadge.tsx | 5 ++ 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/backend/api/podcast/handlers/analysis.py b/backend/api/podcast/handlers/analysis.py index a1e7dd42..c1a33ef4 100644 --- a/backend/api/podcast/handlers/analysis.py +++ b/backend/api/podcast/handlers/analysis.py @@ -19,6 +19,7 @@ from services.llm_providers.main_image_generation import generate_image from services.podcast_bible_service import PodcastBibleService from utils.asset_tracker import save_asset_to_library from loguru import logger +import os from ..constants import PODCAST_IMAGES_DIR from ..models import ( PodcastAnalyzeRequest, @@ -27,6 +28,11 @@ from ..models import ( PodcastEnhanceIdeaResponse ) +# Check if running in podcast-only demo mode +def _is_podcast_only_mode() -> bool: + """Check if podcast-only demo mode is enabled.""" + return os.getenv("ALWRITY_ENABLED_FEATURES", "").strip().lower() == "podcast" + router = APIRouter() @@ -42,19 +48,31 @@ async def enhance_podcast_idea( user_id = require_authenticated_user(current_user) # Serialize Bible context if provided or generate from onboarding + # In podcast-only mode, skip bible generation since onboarding is disabled bible_context = "" - try: - bible_service = PodcastBibleService() + if not _is_podcast_only_mode(): + try: + bible_service = PodcastBibleService() + if request.bible: + from models.podcast_bible_models import PodcastBible + bible_data = PodcastBible(**request.bible) + bible_context = bible_service.serialize_bible(bible_data) + else: + # Generate from onboarding data directly + bible_obj = bible_service.generate_bible(user_id, "temp_enhance") + bible_context = bible_service.serialize_bible(bible_obj) + except Exception as exc: + logger.warning(f"[Podcast Enhance] Failed to parse or generate bible context: {exc}") + else: + # In podcast mode, use the provided bible directly if available if request.bible: - from models.podcast_bible_models import PodcastBible - bible_data = PodcastBible(**request.bible) - bible_context = bible_service.serialize_bible(bible_data) - else: - # Generate from onboarding data directly - bible_obj = bible_service.generate_bible(user_id, "temp_enhance") - bible_context = bible_service.serialize_bible(bible_obj) - except Exception as exc: - logger.warning(f"[Podcast Enhance] Failed to parse or generate bible context: {exc}") + try: + from models.podcast_bible_models import PodcastBible + bible_data = PodcastBible(**request.bible) + bible_service = PodcastBibleService() + bible_context = bible_service.serialize_bible(bible_data) + except Exception as exc: + logger.debug(f"[Podcast Enhance] Bible parsing skipped in podcast mode: {exc}") prompt = f""" You are a creative podcast producer. Generate 3 distinct, compelling podcast episode concepts from the raw idea. @@ -72,8 +90,22 @@ Generate 3 different enhanced versions, each with a unique angle: Each version should be 2-3 sentences, audience-focused, and align with host persona if provided. Return JSON with: -- enhanced_ideas: array of 3 enhanced episode pitches (in order: Professional, Storytelling, Trendy) -- rationales: array of 3 rationales explaining the approach for each version +- enhanced_ideas: array of 3 strings, each string being a complete episode pitch (NOT objects, just plain strings) +- rationales: array of 3 strings explaining the approach for each version + +IMPORTANT: enhanced_ideas must be an array of plain strings, NOT objects. Example: +{{ + "enhanced_ideas": [ + "Your expert guide to AI advancement: A practical look at how AI is transforming industries...", + "The human stories behind AI innovation: From Silicon Valley to your daily life...", + "AI in 2026: What's trending and what's next in artificial intelligence..." + ], + "rationales": [ + "Professional approach focusing on expertise and authority", + "Storytelling approach emphasizing human connection", + "Contemporary approach highlighting current relevance" + ] +}} """ try: @@ -95,6 +127,19 @@ Return JSON with: enhanced_ideas = data.get("enhanced_ideas", []) rationales = data.get("rationales", []) + # Handle case where LLM returns objects instead of strings + normalized_ideas = [] + for idea in enhanced_ideas: + if isinstance(idea, dict): + # Extract title and description from object + title = idea.get("title", "") + description = idea.get("description", "") or idea.get("content", "") + normalized_ideas.append(f"{title}: {description}" if description else title) + elif isinstance(idea, str): + normalized_ideas.append(idea) + + enhanced_ideas = normalized_ideas + # Ensure we have exactly 3 ideas, fallback to original if needed if not isinstance(enhanced_ideas, list) or len(enhanced_ideas) != 3: # Fallback: create 3 variations of the original idea diff --git a/backend/requirements-podcast.txt b/backend/requirements-podcast.txt index c5facbf9..013c0adb 100644 --- a/backend/requirements-podcast.txt +++ b/backend/requirements-podcast.txt @@ -41,8 +41,9 @@ exa-py==1.9.1 markdown>=3.5.0 beautifulsoup4>=4.12.0 -# Data processing (numpy needed for moviepy) +# Data processing (numpy needed for moviepy, pandas for usage tracking) numpy>=1.24.0 +pandas>=2.0.0 # Image/media for podcast Pillow>=10.0.0 diff --git a/frontend/src/components/shared/AlertsBadge.tsx b/frontend/src/components/shared/AlertsBadge.tsx index af0bd518..42299f1d 100644 --- a/frontend/src/components/shared/AlertsBadge.tsx +++ b/frontend/src/components/shared/AlertsBadge.tsx @@ -222,6 +222,11 @@ const AlertsBadge: React.FC = ({ colorMode = 'light' }) => { // Poll for alerts useEffect(() => { + // Skip alerts polling entirely in podcast-only mode + if (isPodcastOnlyDemoMode()) { + return; + } + if (!userId) return; // Delay initial fetch slightly to ensure auth token getter is installed