copywriter and blog metadata updates

This commit is contained in:
ajaysi
2025-04-30 16:06:33 +05:30
parent ad9f401b60
commit e19f933a19
6 changed files with 2061 additions and 312 deletions

View File

@@ -6,6 +6,7 @@ import streamlit as st
from loguru import logger
import random
import asyncio
import re
logger.remove()
logger.add(sys.stdout,
@@ -17,110 +18,367 @@ from ..gpt_providers.text_generation.main_text_generation import llm_text_gen
async def blog_metadata(blog_article):
""" Common function to get blog metadata """
logger.info(f"Generating Content MetaData\n")
"""
Generate comprehensive SEO metadata for a blog article.
Args:
blog_article (str): The content of the blog article
Returns:
tuple: (blog_title, blog_meta_desc, blog_tags, blog_categories, blog_hashtags, blog_slug)
"""
logger.info("Generating comprehensive blog metadata")
progress_bar = st.progress(0)
total_steps = 4
total_steps = 6 # Increased steps for new metadata types
status_container = st.empty()
# Step 1: Generate blog title
await asyncio.sleep(random.uniform(1, 3))
blog_title = generate_blog_title(blog_article)
progress_bar.progress(1 / total_steps)
try:
# Step 1: Generate blog title
status_container.info("Generating SEO-optimized blog title...")
await asyncio.sleep(random.uniform(0.5, 1.5))
blog_title = generate_blog_title(blog_article)
progress_bar.progress(1 / total_steps)
# Step 2: Generate blog meta description
await asyncio.sleep(random.uniform(1, 3))
blog_meta_desc = generate_blog_description(blog_article)
progress_bar.progress(2 / total_steps)
# Step 2: Generate blog meta description
status_container.info("Creating compelling meta description...")
await asyncio.sleep(random.uniform(0.5, 1.5))
blog_meta_desc = generate_blog_description(blog_article)
progress_bar.progress(2 / total_steps)
# Step 3: Generate blog tags
await asyncio.sleep(random.uniform(1, 3))
blog_tags = get_blog_tags(blog_article)
progress_bar.progress(3 / total_steps)
# Step 3: Generate blog tags
status_container.info("Extracting relevant blog tags...")
await asyncio.sleep(random.uniform(0.5, 1.5))
blog_tags = get_blog_tags(blog_article)
progress_bar.progress(3 / total_steps)
# Step 4: Generate blog categories
await asyncio.sleep(random.uniform(1, 3))
blog_categories = get_blog_categories(blog_article)
progress_bar.progress(4 / total_steps)
# Step 4: Generate blog categories
status_container.info("Identifying primary blog categories...")
await asyncio.sleep(random.uniform(0.5, 1.5))
blog_categories = get_blog_categories(blog_article)
progress_bar.progress(4 / total_steps)
# Step 5: Generate social media hashtags
status_container.info("Creating social media hashtags...")
await asyncio.sleep(random.uniform(0.5, 1.5))
blog_hashtags = generate_blog_hashtags(blog_article)
progress_bar.progress(5 / total_steps)
# Step 6: Generate SEO URL slug
status_container.info("Generating SEO-friendly URL slug...")
await asyncio.sleep(random.uniform(0.5, 1.5))
blog_slug = generate_blog_slug(blog_title)
progress_bar.progress(6 / total_steps)
# Present the result in a table format
st.table({
"Metadata": ["Blog Title", "Meta Description", "Tags", "Categories"],
"Value": [blog_title, blog_meta_desc, blog_tags, blog_categories]
})
# Present the result in a table format
status_container.success("✅ Metadata generation complete")
st.table({
"Metadata": ["Blog Title", "Meta Description", "Tags", "Categories", "Social Hashtags", "URL Slug"],
"Value": [blog_title, blog_meta_desc, blog_tags, blog_categories, blog_hashtags, blog_slug]
})
return blog_title, blog_meta_desc, blog_tags, blog_categories
return blog_title, blog_meta_desc, blog_tags, blog_categories, blog_hashtags, blog_slug
except Exception as e:
status_container.error(f"Error generating metadata: {str(e)}")
logger.error(f"Failed to generate metadata: {str(e)}")
# Return default values to ensure the blog generation process can continue
return f"Blog Article", "An informative blog post", "content, blog", "General, Information", "#content #blog", "blog-article"
def generate_blog_title(blog_article):
"""
Given a blog title generate an outline for it
Generate an SEO-optimized and engaging title for a blog article.
Args:
blog_article (str): The content of the blog article
Returns:
str: An SEO-optimized title
"""
logger.info("Generating blog title.")
prompt = f"""As a SEO expert, I will provide you with a blog content.
Your task is write a SEO optimized and call to action, blog title for given blog content.
Follow SEO best practises to suggest the blog title.
Please keep the titles concise, not exceeding 60 words.
Respond with only the title and no explanations.
Negative Keywords: Unvieling, unleash, power of. Dont use such words in your title.
\nGenerate blog title for this given blog content:\n '{blog_article}' """
logger.info("Generating SEO-optimized blog title")
# Extract the first 3000 characters for title generation
snippet = blog_article[:3000] if len(blog_article) > 3000 else blog_article
prompt = f"""As an expert SEO copywriter, create the perfect blog title based on this content.
REQUIREMENTS:
1. Make it compelling, specific, and actionable
2. Include primary keywords naturally near the beginning
3. Keep it between 50-60 characters (10-12 words maximum)
4. Make it promise clear value to the reader
5. Use power words that evoke emotion where appropriate
AVOID:
- Clickbait tactics or false promises
- Generic titles that could apply to any article
- Using words like "unveiling", "unleash", "power of", "ultimate guide", or "complete"
- ALL CAPS or excessive punctuation!!!!
EXAMPLES OF GREAT TITLES:
- "7 Proven Strategies to Improve Your Email Marketing ROI"
- "Why Remote Work Improves Productivity: New Research Findings"
- "How to Build a Personal Budget That Actually Works"
CONTENT TO ANALYZE:
"{snippet}"
Reply with ONLY the title and no other text or explanation.
"""
try:
response = llm_text_gen(prompt)
return response
title = llm_text_gen(prompt)
# Clean up any quotes or extra spaces
title = title.strip('"\'').strip()
logger.info(f"Generated title: {title}")
return title
except Exception as err:
logger.error(f"Failed to get response from LLM: {err}")
raise err
logger.error(f"Failed to generate blog title: {err}")
return "Blog Article" # Fallback title
def generate_blog_description(blog_content):
"""
Prompt designed to give SEO optimized blog descripton
Generate an SEO-optimized meta description for the blog.
Args:
blog_content (str): The content of the blog article
Returns:
str: An SEO-optimized meta description
"""
logger.info("Generating Blog Meta Description for the given blog.")
prompt = f"""As an expert SEO and blog writer, Compose a compelling meta description for the given blog content,
adhering to SEO best practices. Keep it between 150-160 characters.
Provide a glimpse of the content's value to entice readers.
Respond with only one of your best effort and do not include your explanations.
Blog Content: '{blog_content}'"""
logger.info("Generating SEO-optimized meta description")
# Extract the first 2000 characters for description generation
snippet = blog_content[:2000] if len(blog_content) > 2000 else blog_content
prompt = f"""As an SEO expert, write the perfect meta description for this blog content.
try:
response = llm_text_gen(prompt)
return response
except Exception as err:
logger.error(f"Failed to get response from LLM:{err}")
raise err
REQUIREMENTS:
1. Exactly 150-160 characters (this is critical for SEO)
2. Include primary keywords naturally
3. Compelling value proposition that makes readers want to click
4. Clear indication of what the reader will learn/gain
5. End with an implicit call-to-action when possible
def get_blog_categories(blog_article):
"""
Function to generate blog categories for given blog content.
"""
prompt = f"""As an expert SEO and content writer, I will provide you with blog content.
Suggest only 2 blog categories which are most relevant to provided blog content,
by identifying the main topic. Also consider the target audience and the
blog's category taxonomy. Only reply with comma separated values.
The blog content is: '{blog_article}'"
"""
logger.info("Generating blog categories for the given blog.")
EXAMPLES OF EXCELLENT META DESCRIPTIONS:
- "Learn how to increase email open rates by 43% with these 5 proven strategies from industry experts. Implement today for immediate results."
- "Discover why 67% of professionals struggle with work-life balance and explore research-backed techniques to reclaim your time and energy."
CONTENT TO SUMMARIZE:
"{snippet}"
Reply with ONLY the meta description and no other text. Keep it between 150-160 characters exactly.
"""
try:
response = llm_text_gen(prompt)
return response
description = llm_text_gen(prompt)
# Clean up any quotes or extra spaces
description = description.strip('"\'').strip()
logger.info(f"Generated meta description: {description}")
return description
except Exception as err:
logger.error(f"get_blog_categories:Failed to get response from LLM: {err}")
logger.error(f"Failed to generate blog description: {err}")
return "An informative blog post about this topic." # Fallback description
def get_blog_tags(blog_article):
"""
Function to suggest tags for the given blog content
Generate relevant SEO tags for a blog article.
Args:
blog_article (str): The content of the blog article
Returns:
str: Comma-separated list of relevant tags
"""
prompt = f"""As an expert SEO and blog writer, suggest only 2 relevant and specific blog tags
for the given blog content. Only reply with comma separated values.
Blog content: {blog_article}."""
logger.info("Generating Blog tags for the given blog post.")
logger.info("Generating SEO-optimized blog tags")
# Extract the first 3000 characters for tag generation
snippet = blog_article[:3000] if len(blog_article) > 3000 else blog_article
prompt = f"""As an SEO specialist, extract the 4-6 most relevant tags for this blog post.
REQUIREMENTS:
1. Choose specific, targeted keywords that accurately represent the content
2. Include a mix of broad and specific tags
3. Focus on terms users would actually search for
4. Include at least one long-tail keyword phrase
5. Ensure all tags are directly addressed in the content
CONTENT TO ANALYZE:
"{snippet}"
Reply with ONLY the tags as a comma-separated list (e.g., "keyword1, keyword2, keyword3, keyword phrase"). Provide 4-6 tags total.
"""
try:
response = llm_text_gen(prompt)
return response
tags = llm_text_gen(prompt)
# Clean up any quotes or extra commas
tags = tags.strip('"\'').strip()
if tags.endswith(','):
tags = tags[:-1]
logger.info(f"Generated tags: {tags}")
return tags
except Exception as err:
logger.error(f"Failed to get response from LLM: {err}")
raise err
logger.error(f"Failed to generate blog tags: {err}")
return "content, blog" # Fallback tags
def get_blog_categories(blog_article):
"""
Identify the most appropriate blog categories for the article.
Args:
blog_article (str): The content of the blog article
Returns:
str: Comma-separated list of relevant categories
"""
logger.info("Generating blog categories")
# Extract the first 2000 characters for category generation
snippet = blog_article[:2000] if len(blog_article) > 2000 else blog_article
prompt = f"""As a content strategist, identify the 2-3 most appropriate high-level categories for this blog.
REQUIREMENTS:
1. Choose broad, established categories used in content organization
2. Select categories that best represent the main themes of the article
3. Consider the target audience and their interests
4. Focus on categories that would help with site navigation
5. Aim for a primary category and 1-2 supporting categories
EXAMPLES OF GOOD CATEGORIES:
- Marketing, Social Media, Strategy
- Finance, Personal Budgeting, Money Management
- Productivity, Remote Work, Business
CONTENT TO ANALYZE:
"{snippet}"
Reply with ONLY the categories as a comma-separated list (e.g., "Category1, Category2, Category3"). Provide 2-3 categories total.
"""
try:
categories = llm_text_gen(prompt)
# Clean up any quotes or extra commas
categories = categories.strip('"\'').strip()
if categories.endswith(','):
categories = categories[:-1]
logger.info(f"Generated categories: {categories}")
return categories
except Exception as err:
logger.error(f"Failed to generate blog categories: {err}")
return "General, Information" # Fallback categories
def generate_blog_hashtags(blog_article):
"""
Generate social media hashtags for promoting the blog article.
Args:
blog_article (str): The content of the blog article
Returns:
str: Space-separated list of hashtags starting with #
"""
logger.info("Generating social media hashtags")
# Extract the first 2000 characters for hashtag generation
snippet = blog_article[:2000] if len(blog_article) > 2000 else blog_article
prompt = f"""As a social media strategist, create 5-7 effective hashtags for this blog content.
REQUIREMENTS:
1. Mix of popular and niche hashtags for better visibility
2. Include industry-specific and trending hashtags where relevant
3. Avoid overly generic hashtags (like #content or #blog)
4. Format each hashtag with # symbol and camelCase or separate words
5. Include at least one branded or campaign-style hashtag
EXAMPLES OF EFFECTIVE HASHTAG SETS:
- #EmailMarketing #ROITips #DigitalStrategy #MarketingTips #GrowthHacking #EmailROI
- #RemoteWork #ProductivityTips #FutureOfWork #WorkFromHome #RemoteProductivity #HRInsights
CONTENT TO ANALYZE:
"{snippet}"
Reply with ONLY the hashtags, each starting with # and separated by spaces. Provide 5-7 hashtags total.
"""
try:
hashtags = llm_text_gen(prompt)
# Clean up any quotes or extra spaces
hashtags = hashtags.strip('"\'').strip()
# Ensure all hashtags start with #
if not hashtags.startswith('#'):
hashtags = ' '.join([f"#{tag.strip('#')}" for tag in hashtags.split()])
logger.info(f"Generated hashtags: {hashtags}")
return hashtags
except Exception as err:
logger.error(f"Failed to generate blog hashtags: {err}")
return "#content #blog" # Fallback hashtags
def generate_blog_slug(blog_title):
"""
Generate an SEO-friendly URL slug from the blog title.
Args:
blog_title (str): The title of the blog article
Returns:
str: An SEO-friendly URL slug
"""
logger.info("Generating SEO-friendly URL slug")
try:
# Use a prompt to generate a customized slug
prompt = f"""As an SEO specialist, create an SEO-friendly URL slug for this blog title: "{blog_title}"
REQUIREMENTS:
1. Keep it under 60 characters
2. Use only lowercase letters, numbers, and hyphens
3. Include primary keywords near the beginning
4. Remove all unnecessary words (a, the, and, or, but, etc.)
5. Ensure it's human-readable and descriptive
EXAMPLES:
- Title: "10 Effective Ways to Improve Your Email Marketing ROI This Quarter"
Slug: "improve-email-marketing-roi"
- Title: "Why Most Remote Workers Are More Productive According to New Research"
Slug: "remote-workers-productivity-research"
Reply with ONLY the slug and no other text or explanation.
"""
slug = llm_text_gen(prompt)
# Clean up and normalize the slug
slug = slug.strip('"\'').strip()
# If the LLM didn't create a proper slug, do it programmatically
if not re.match(r'^[a-z0-9-]+$', slug):
# Fallback to simple programmatic slug creation
slug = blog_title.lower()
# Remove special characters
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
# Replace spaces with hyphens
slug = re.sub(r'\s+', '-', slug)
# Remove redundant hyphens
slug = re.sub(r'-+', '-', slug)
# Limit length to 60 characters
slug = slug[:60].strip('-')
logger.info(f"Generated slug: {slug}")
return slug
except Exception as err:
logger.error(f"Failed to generate blog slug: {err}")
# Create a simple slug programmatically as fallback
slug = blog_title.lower()
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
slug = re.sub(r'\s+', '-', slug)
slug = re.sub(r'-+', '-', slug)
slug = slug[:60].strip('-')
return slug
# Helper function to run the asyncio event loop within Streamlit
def run_async(coro):