124 lines
4.6 KiB
Python
124 lines
4.6 KiB
Python
"""
|
|
Title Generator - Handles title generation and formatting for blog outlines.
|
|
|
|
Extracts content angles from research data and combines them with AI-generated titles.
|
|
"""
|
|
|
|
from typing import List
|
|
from loguru import logger
|
|
|
|
|
|
class TitleGenerator:
|
|
"""Handles title generation, formatting, and combination logic."""
|
|
|
|
def __init__(self):
|
|
"""Initialize the title generator."""
|
|
pass
|
|
|
|
def extract_content_angle_titles(self, research) -> List[str]:
|
|
"""
|
|
Extract content angles from research data and convert them to blog titles.
|
|
|
|
Args:
|
|
research: BlogResearchResponse object containing suggested_angles
|
|
|
|
Returns:
|
|
List of title-formatted content angles
|
|
"""
|
|
if not research or not hasattr(research, 'suggested_angles'):
|
|
return []
|
|
|
|
content_angles = research.suggested_angles or []
|
|
if not content_angles:
|
|
return []
|
|
|
|
# Convert content angles to title format
|
|
title_formatted_angles = []
|
|
for angle in content_angles:
|
|
if isinstance(angle, str) and angle.strip():
|
|
# Clean and format the angle as a title
|
|
formatted_angle = self._format_angle_as_title(angle.strip())
|
|
if formatted_angle and formatted_angle not in title_formatted_angles:
|
|
title_formatted_angles.append(formatted_angle)
|
|
|
|
logger.info(f"Extracted {len(title_formatted_angles)} content angle titles from research data")
|
|
return title_formatted_angles
|
|
|
|
def _format_angle_as_title(self, angle: str) -> str:
|
|
"""
|
|
Format a content angle as a proper blog title.
|
|
|
|
Args:
|
|
angle: Raw content angle string
|
|
|
|
Returns:
|
|
Formatted title string
|
|
"""
|
|
if not angle or len(angle.strip()) < 10: # Too short to be a good title
|
|
return ""
|
|
|
|
# Clean up the angle
|
|
cleaned_angle = angle.strip()
|
|
|
|
# Capitalize first letter of each sentence and proper nouns
|
|
sentences = cleaned_angle.split('. ')
|
|
formatted_sentences = []
|
|
for sentence in sentences:
|
|
if sentence.strip():
|
|
# Use title case for better formatting
|
|
formatted_sentence = sentence.strip().title()
|
|
formatted_sentences.append(formatted_sentence)
|
|
|
|
formatted_title = '. '.join(formatted_sentences)
|
|
|
|
# Ensure it ends with proper punctuation
|
|
if not formatted_title.endswith(('.', '!', '?')):
|
|
formatted_title += '.'
|
|
|
|
# Limit length to reasonable blog title size
|
|
if len(formatted_title) > 100:
|
|
formatted_title = formatted_title[:97] + "..."
|
|
|
|
return formatted_title
|
|
|
|
def combine_title_options(self, ai_titles: List[str], content_angle_titles: List[str], primary_keywords: List[str]) -> List[str]:
|
|
"""
|
|
Combine AI-generated titles with content angle titles, ensuring variety and quality.
|
|
|
|
Args:
|
|
ai_titles: AI-generated title options
|
|
content_angle_titles: Titles derived from content angles
|
|
primary_keywords: Primary keywords for fallback generation
|
|
|
|
Returns:
|
|
Combined list of title options (max 6 total)
|
|
"""
|
|
all_titles = []
|
|
|
|
# Add content angle titles first (these are research-based and valuable)
|
|
for title in content_angle_titles[:3]: # Limit to top 3 content angles
|
|
if title and title not in all_titles:
|
|
all_titles.append(title)
|
|
|
|
# Add AI-generated titles
|
|
for title in ai_titles:
|
|
if title and title not in all_titles:
|
|
all_titles.append(title)
|
|
|
|
# Note: Removed fallback titles as requested - only use research and AI-generated titles
|
|
|
|
# Limit to 6 titles maximum for UI usability
|
|
final_titles = all_titles[:6]
|
|
|
|
logger.info(f"Combined title options: {len(final_titles)} total (AI: {len(ai_titles)}, Content angles: {len(content_angle_titles)})")
|
|
return final_titles
|
|
|
|
def generate_fallback_titles(self, primary_keywords: List[str]) -> List[str]:
|
|
"""Generate fallback titles when AI generation fails."""
|
|
primary_keyword = primary_keywords[0] if primary_keywords else "Topic"
|
|
return [
|
|
f"The Complete Guide to {primary_keyword}",
|
|
f"{primary_keyword}: Everything You Need to Know",
|
|
f"How to Master {primary_keyword} in 2024"
|
|
]
|