BlogAgent / app.py
jsakshi's picture
Update app.py
fac7dd6 verified
raw
history blame
30.6 kB
'''
import gradio as gr
import os
import time
import tempfile
import requests
from PIL import Image
from io import BytesIO
import re
from datetime import datetime
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Hugging Face configuration
HF_TOKEN = os.getenv("HF_TOKEN")
TEXT_API_URL = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2"
IMAGE_API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
def generate_blog_content(topic, tone="professional", length="medium"):
try:
current_date = datetime.now().strftime("%B %d, %Y")
reading_time = {"short": "5-8", "medium": "8-12", "long": "15-20"}[length]
prompt = f"""<s>[INST] Write a {tone} blog post about {topic} with:
- Title and subtitle
- Introduction with statistics
- 2 main sections with subsections
- Conclusion
- Markdown formatting
- Published date: {current_date}
- Reading time: {reading_time} minutes [/INST]</s>"""
payload = {
"inputs": prompt,
"parameters": {
"max_new_tokens": 1024,
"temperature": 0.7,
"return_full_text": False
}
}
response = requests.post(TEXT_API_URL, headers=HEADERS, json=payload)
if response.status_code == 503:
estimate = response.json().get('estimated_time', 30)
time.sleep(estimate)
response = requests.post(TEXT_API_URL, headers=HEADERS, json=payload)
response.raise_for_status()
return response.json()[0]['generated_text']
except Exception as e:
return f"Error generating content: {str(e)}"
def generate_featured_image(topic):
try:
prompt = f"Professional digital illustration for blog about {topic}, high quality"
payload = {
"inputs": prompt,
"parameters": {
"height": 512,
"width": 768,
"num_inference_steps": 25
}
}
response = requests.post(IMAGE_API_URL, headers=HEADERS, json=payload)
response.raise_for_status()
image = Image.open(BytesIO(response.content))
temp_img = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
image.save(temp_img.name)
return temp_img.name, None # Return path and no error
except Exception as e:
return None, f"Image error: {str(e)}"
def create_download_file(content, title, author):
try:
temp_file = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".md", encoding="utf-8")
full_content = content + f"\n\n**About the Author** \n{author}"
temp_file.write(full_content)
temp_file.close()
sanitized_title = re.sub(r'[^\w\-_ ]', '_', title)[:50]
return temp_file.name # Return only the file path
except Exception as e:
return None
def generate_blog(topic, tone, length, author_name, publish_option, linkedin_user=None, linkedin_pass=None, hf_token=None):
status_updates = []
blog_content = ""
title = ""
file_path = None
try:
# Generate content
status_updates.append("πŸš€ Starting blog generation...")
blog_content = generate_blog_content(topic, tone, length)
if "Error" in blog_content:
return blog_content, "", "\n".join(status_updates), None
# Extract title
title_match = re.search(r'^#\s+(.+)$', blog_content, re.MULTILINE)
title = title_match.group(1).strip() if title_match else topic
# Generate image
status_updates.append("πŸ–ΌοΈ Generating featured image...")
image_path, image_error = generate_featured_image(topic)
if image_error:
status_updates.append(image_error)
else:
status_updates.append("βœ… Image generated!")
# Create downloadable file
status_updates.append("πŸ“₯ Preparing download...")
file_path = create_download_file(blog_content, title, author_name)
status_updates.append("βœ… Download ready!")
return blog_content, title, "\n".join(status_updates), file_path
except Exception as e:
status_updates.append(f"❌ Critical error: {str(e)}")
return blog_content, title, "\n".join(status_updates), None
# Gradio interface
with gr.Blocks(title="AI Blog Generator", theme=gr.themes.Soft()) as app:
gr.Markdown("# πŸ“ AI Blog Generator")
with gr.Row():
with gr.Column(scale=1):
topic_input = gr.Textbox(label="Blog Topic")
tone_input = gr.Dropdown(
["professional", "casual", "technical", "storytelling"],
label="Writing Style",
value="professional"
)
length_input = gr.Dropdown(
["short", "medium", "long"],
label="Article Length",
value="medium"
)
author_input = gr.Textbox(label="Author Name")
generate_btn = gr.Button("Generate", variant="primary")
with gr.Column(scale=2):
title_output = gr.Textbox(label="Generated Title")
blog_output = gr.Markdown()
status_output = gr.Textbox(label="Status")
download_output = gr.File(label="Download")
generate_btn.click(
generate_blog,
inputs=[topic_input, tone_input, length_input, author_input, gr.Radio(["none"], visible=False)],
outputs=[blog_output, title_output, status_output, download_output]
)
if __name__ == "__main__":
app.launch(share=False)'''
'''import gradio as gr
import os
import time
import requests
import re
import uuid
import markdown
from datetime import datetime
from dotenv import load_dotenv
from huggingface_hub import HfApi, upload_file
load_dotenv()
# Configuration
HF_TOKEN = os.getenv("HF_TOKEN")
HF_USERNAME = "jsakshi"
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
def generate_blog_content(topic):
try:
prompt = f"""Create a professional blog post about {topic} including:
- Title and subtitle
- 3 sections with subsections
- 2 image placeholders [IMAGE1][IMAGE2]
- Data points and examples
- Conclusion
Use markdown formatting"""
response = requests.post(
"https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
headers=HEADERS,
json={"inputs": prompt, "parameters": {"max_length": 2000}}
)
if response.status_code != 200:
return None, f"API Error: {response.text}"
return response.json()[0]['generated_text'], None
except Exception as e:
return None, f"Error: {str(e)}"
def create_hosted_blog(topic):
try:
# Generate blog content first
content, error = generate_blog_content(topic)
if error or not content:
return f"Error generating content: {error}", ""
# Create unique space
space_id = f"blog-{uuid.uuid4().hex[:8]}"
space_name = f"{HF_USERNAME}/{space_id}"
# Initialize Hub API
api = HfApi(token=HF_TOKEN)
api.create_repo(
repo_id=space_name,
repo_type="space",
space_sdk="static",
private=False
)
# Generate and upload images
image_paths = []
for idx in range(2):
response = requests.post(
"https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0",
headers=HEADERS,
json={"inputs": f"Professional illustration about {topic}, clean design"}
)
if response.status_code == 200:
upload_file(
path_or_fileobj=response.content,
path_in_repo=f"image_{idx}.png",
repo_id=space_name,
repo_type="space"
)
image_paths.append(f"image_{idx}.png")
time.sleep(1)
# Replace image placeholders
if content:
updated_content = content.replace("[IMAGE1]", f'![Image 1]({image_paths[0]})').replace("[IMAGE2]", f'![Image 2]({image_paths[1]})')
else:
return "Error: No content generated", ""
# Create HTML template
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>{topic}</title>
<style>
body {{
max-width: 800px;
margin: 0 auto;
padding: 40px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
}}
.header {{
text-align: center;
padding: 40px 0;
border-bottom: 1px solid #eee;
margin-bottom: 40px;
}}
h1 {{
color: #2c3e50;
font-size: 2.5em;
margin-bottom: 10px;
}}
img {{
width: 100%;
height: auto;
margin: 25px 0;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}}
.content {{
background: white;
padding: 30px;
border-radius: 8px;
}}
</style>
</head>
<body>
<div class="header">
<h1>{topic}</h1>
<p>Published on {datetime.now().strftime("%B %d, %Y")}</p>
</div>
<div class="content">
{markdown.markdown(updated_content)}
</div>
</body>
</html>
"""
# Upload HTML file
upload_file(
path_or_fileobj=html_content.encode(),
path_in_repo="index.html",
repo_id=space_name,
repo_type="space"
)
return f"https://huggingface.co/spaces/{space_name}", content
except Exception as e:
return f"Error: {str(e)}", ""
# Gradio interface
with gr.Blocks(theme=gr.themes.Soft()) as app:
gr.Markdown("# πŸ“„ Professional Blog Generator")
with gr.Row():
with gr.Column():
topic_input = gr.Textbox(label="Enter Blog Topic",
placeholder="e.g., Future of AI in Healthcare")
generate_btn = gr.Button("Generate Blog", variant="primary")
with gr.Column():
gr.Markdown("### Generated Content")
blog_output = gr.Markdown()
gr.Markdown("### Blog URL")
blog_link = gr.Markdown("Your blog link will appear here...")
status = gr.Textbox(label="Status", interactive=False)
generate_btn.click(
fn=create_hosted_blog,
inputs=topic_input,
outputs=[blog_link, blog_output]
)
if __name__ == "__main__":
app.launch(share=True)'''
'''import gradio as gr
import os
import time
import requests
import re
import uuid
import markdown
from datetime import datetime
from dotenv import load_dotenv
from huggingface_hub import HfApi, upload_file
load_dotenv()
# Configuration
HF_TOKEN = os.getenv("HF_TOKEN")
HF_USERNAME = "jsakshi"
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
def generate_blog_content(topic):
try:
prompt = f"""Create a detailed, professional blog post about {topic} including:
- A compelling title and subtitle
- An introduction
- 3 main sections with descriptive headings
- Key points and data examples in each section
- A conclusion
Use an informative, professional tone."""
response = requests.post(
"https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
headers=HEADERS,
json={"inputs": prompt, "parameters": {"max_length": 2000}}
)
if response.status_code != 200:
return None, f"API Error: {response.text}"
blog_text = response.json()[0]['generated_text']
# Extract title (assuming first line contains the title)
lines = blog_text.split('\n')
title = topic
subtitle = "A comprehensive analysis and exploration"
for line in lines[:5]:
if line.strip() and not line.startswith('#'):
title = line.strip()
break
# Look for a possible subtitle
for line in lines[1:10]:
if line.strip() and line != title and not line.startswith('#'):
subtitle = line.strip()
break
return {
"title": title,
"subtitle": subtitle,
"content": blog_text
}, None
except Exception as e:
return None, f"Error: {str(e)}"
def create_hosted_blog(topic):
try:
# Generate blog content first
content_data, error = generate_blog_content(topic)
if error or not content_data:
return f"Error generating content: {error}", ""
# Create unique space
space_id = f"blog-{uuid.uuid4().hex[:8]}"
space_name = f"{HF_USERNAME}/{space_id}"
# Initialize Hub API
api = HfApi(token=HF_TOKEN)
api.create_repo(
repo_id=space_name,
repo_type="space",
space_sdk="static",
private=False
)
# Generate and upload images
image_paths = []
image_prompts = [
f"Professional illustration about {topic}, clean design, minimalist style",
f"Data visualization or concept diagram related to {topic}, professional look"
]
for idx, img_prompt in enumerate(image_prompts):
response = requests.post(
"https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0",
headers=HEADERS,
json={"inputs": img_prompt}
)
if response.status_code == 200:
img_filename = f"image_{idx}.png"
upload_file(
path_or_fileobj=response.content,
path_in_repo=img_filename,
repo_id=space_name,
repo_type="space"
)
image_paths.append(img_filename)
time.sleep(2) # Add delay to prevent rate limiting
# Format the current date
current_date = datetime.now().strftime("%B %d, %Y")
# Create HTML using the modern template from the example
title = content_data.get("title", topic)
subtitle = content_data.get("subtitle", "A comprehensive analysis")
content = content_data.get("content", "")
# Process the content to get sections for TOC
sections = []
section_pattern = re.compile(r'^##?\s+(.+)$', re.MULTILINE)
section_matches = section_pattern.findall(content)
for i, section in enumerate(section_matches[:6]):
section_id = section.lower().replace(' ', '-').replace(':', '')
sections.append({
"title": section,
"id": section_id
})
# Convert markdown content to HTML with proper section IDs
html_content = content
for section in sections:
pattern = f"## {section['title']}"
replacement = f"<h2 id=\"{section['id']}\">{section['title']}</h2>"
html_content = html_content.replace(pattern, replacement)
html_content = markdown.markdown(html_content)
# Create complete HTML
complete_html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<style>
:root {{
--primary-color: #2D68C4;
--secondary-color: #f8f9fa;
--text-color: #333;
--light-gray: #e9ecef;
--dark-gray: #495057;
}}
* {{
margin: 0;
padding: 0;
box-sizing: border-box;
}}
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: #fff;
}}
header {{
background: linear-gradient(135deg, var(--primary-color), #1d4ed8);
color: white;
padding: 2rem 0;
text-align: center;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
}}
.blog-header {{
display: flex;
flex-direction: column;
align-items: center;
}}
.blog-title {{
font-size: 2.5rem;
margin-bottom: 1rem;
}}
.blog-subtitle {{
font-size: 1.2rem;
opacity: 0.9;
}}
.blog-meta {{
display: flex;
margin-top: 1rem;
font-size: 0.9rem;
}}
.blog-meta div {{
margin-right: 1.5rem;
display: flex;
align-items: center;
}}
.blog-content {{
display: grid;
grid-template-columns: 1fr;
gap: 2rem;
margin: 3rem 0;
}}
@media (min-width: 768px) {{
.blog-content {{
grid-template-columns: 7fr 3fr;
}}
}}
.main-content {{
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
padding: 2rem;
}}
.sidebar {{
position: sticky;
top: 2rem;
height: fit-content;
}}
.sidebar-section {{
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
padding: 1.5rem;
margin-bottom: 2rem;
}}
.sidebar-title {{
font-size: 1.2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--light-gray);
}}
.toc-list {{
list-style: none;
}}
.toc-list li {{
margin-bottom: 0.5rem;
}}
.toc-list a {{
color: var(--primary-color);
text-decoration: none;
}}
.toc-list a:hover {{
text-decoration: underline;
}}
h1, h2, h3, h4 {{
margin: 1.5rem 0 1rem 0;
line-height: 1.3;
}}
h1 {{
font-size: 2rem;
}}
h2 {{
font-size: 1.75rem;
border-bottom: 2px solid var(--light-gray);
padding-bottom: 0.5rem;
}}
h3 {{
font-size: 1.4rem;
}}
p {{
margin-bottom: 1.5rem;
}}
.blog-image {{
width: 100%;
height: auto;
border-radius: 8px;
margin: 1.5rem 0;
}}
.highlight-box {{
background-color: var(--secondary-color);
border-left: 4px solid var(--primary-color);
padding: 1.5rem;
margin: 1.5rem 0;
border-radius: 0 8px 8px 0;
}}
.highlight-box h4 {{
margin-top: 0;
color: var(--primary-color);
}}
footer {{
background-color: var(--dark-gray);
color: white;
padding: 2rem 0;
margin-top: 3rem;
}}
.footer-content {{
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}}
@media (min-width: 768px) {{
.footer-content {{
flex-direction: row;
justify-content: space-between;
text-align: left;
}}
}}
.footer-links {{
list-style: none;
display: flex;
margin-top: 1rem;
}}
.footer-links li {{
margin-right: 1rem;
}}
.footer-links a {{
color: white;
text-decoration: none;
}}
.footer-links a:hover {{
text-decoration: underline;
}}
</style>
</head>
<body>
<header>
<div class="container">
<div class="blog-header">
<h1 class="blog-title">{title}</h1>
<p class="blog-subtitle">{subtitle}</p>
<div class="blog-meta">
<div>Published: {current_date}</div>
<div>Reading time: 8 minutes</div>
</div>
</div>
</div>
</header>
<div class="container">
<div class="blog-content">
<article class="main-content">
{f'<img src="{image_paths[0]}" alt="{topic} illustration" class="blog-image" />' if image_paths else ''}
{html_content}
{f'<img src="{image_paths[1]}" alt="{topic} visualization" class="blog-image" />' if len(image_paths) > 1 else ''}
<div class="highlight-box">
<h4>Key Takeaways</h4>
<p>This article explores the essential aspects of {topic}, providing insights into current trends, challenges, and future opportunities in this field.</p>
</div>
</article>
<aside class="sidebar">
<div class="sidebar-section">
<h3 class="sidebar-title">Table of Contents</h3>
<ul class="toc-list">
{''.join([f'<li><a href="#{section["id"]}">{section["title"]}</a></li>' for section in sections])}
</ul>
</div>
<div class="sidebar-section">
<h3 class="sidebar-title">About the Author</h3>
<p>This article was created by Sakshi Jadhav that combines research, writing, and design capabilities to produce comprehensive, informative content on cutting-edge topics.</p>
</div>
<div class="sidebar-section">
<h3 class="sidebar-title">Related Topics</h3>
<ul class="toc-list">
<li><a href="#">Latest Developments in {topic}</a></li>
<li><a href="#">Industry Perspectives on {topic}</a></li>
<li><a href="#">Research Advancements in {topic}</a></li>
<li><a href="#">Case Studies: {topic} in Action</a></li>
</ul>
</div>
</aside>
</div>
</div>
<footer>
<div class="container">
<div class="footer-content">
<div>
<p>&copy; {datetime.now().year} Professional Blog Hub</p>
<p>Created with AI Blog Generator</p>
</div>
<ul class="footer-links">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Topics</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
</div>
</footer>
</body>
</html>
"""
# Upload HTML file
upload_file(
path_or_fileobj=complete_html.encode(),
path_in_repo="index.html",
repo_id=space_name,
repo_type="space"
)
return f"https://huggingface.co/spaces/{space_name}", content_data.get("content", "")
except Exception as e:
return f"Error: {str(e)}", ""
# Gradio interface
with gr.Blocks(theme=gr.themes.Soft()) as app:
gr.Markdown("# πŸ“„ Professional Blog Generator")
gr.Markdown("Create well-structured, professional blog posts with just a topic")
with gr.Row():
with gr.Column():
topic_input = gr.Textbox(label="Enter Blog Topic",
placeholder="e.g., Future of AI in Healthcare")
generate_btn = gr.Button("Generate Blog", variant="primary")
status = gr.Textbox(label="Status", interactive=False)
with gr.Column():
gr.Markdown("### Blog URL")
blog_link = gr.Markdown("Your blog link will appear here...")
gr.Markdown("### Preview")
blog_output = gr.Markdown()
generate_btn.click(
fn=create_hosted_blog,
inputs=topic_input,
outputs=[blog_link, blog_output]
)
if __name__ == "__main__":
app.launch(share=True)'''
import gradio as gr
import os
import time
import requests
import re
import uuid
import markdown
from datetime import datetime
from dotenv import load_dotenv
from huggingface_hub import HfApi, upload_file
load_dotenv()
# Configuration
HF_TOKEN = os.getenv("HF_TOKEN")
HF_USERNAME = "jsakshi"
AUTHOR_NAME = "Sarah Johnson"
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
def generate_blog_content(topic, seo_keywords):
try:
prompt = f"""Write a high-quality blog post about {topic} that sounds human-written. Include:
- Engaging introduction with a hook
- 3-5 main sections with subheadings
- Bold key terms using **bold**
- SEO keywords: {', '.join(seo_keywords)}
- Conclusion with actionable advice
- Author attribution for {AUTHOR_NAME}
Avoid AI-sounding language. Use conversational tone."""
response = requests.post(
"https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
headers=HEADERS,
json={"inputs": prompt, "parameters": {"max_length": 2500}}
)
if response.status_code != 200:
return None, f"API Error: {response.text}"
blog_text = response.json()[0]['generated_text']
blog_text += f"\n\n*This article was written by {AUTHOR_NAME}, combining research and practical experience.*"
return {
"title": extract_title(blog_text),
"content": blog_text,
"bold_terms": re.findall(r'\*\*(.*?)\*\*', blog_text)
}, None
except Exception as e:
return None, f"Error: {str(e)}"
def extract_title(text):
lines = text.split('\n')
for line in lines:
if line.strip() and not line.startswith('#'):
return line.strip().replace('#', '')
return "Professional Analysis"
def create_hosted_blog(topic, seo_keywords):
try:
# Generate content
content_data, error = generate_blog_content(topic, seo_keywords)
if error:
return f"Error: {error}", ""
# Create space
space_id = f"blog-{uuid.uuid4().hex[:8]}"
space_name = f"{HF_USERNAME}/{space_id}"
api = HfApi(token=HF_TOKEN)
api.create_repo(
repo_id=space_name,
repo_type="space",
space_sdk="static",
private=False
)
# Create HTML with improved styling
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>{content_data['title']}</title>
<style>
body {{
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f4f4f4;
color: #333;
margin: 0;
padding: 0;
}}
.container {{
width: 80%;
margin: auto;
overflow: hidden;
background: white;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 10px;
}}
h1, h2, h3 {{
color: #222;
}}
header {{
background: linear-gradient(135deg, #ff7e5f, #feb47b);
color: white;
padding: 20px 0;
text-align: center;
border-radius: 10px 10px 0 0;
}}
footer {{
text-align: center;
padding: 10px;
background: #222;
color: white;
margin-top: 20px;
border-radius: 0 0 10px 10px;
}}
img {{
width: 100%;
border-radius: 10px;
margin-bottom: 10px;
}}
</style>
</head>
<body>
<header>
<h1>{content_data['title']}</h1>
</header>
<div class="container">
{markdown.markdown(content_data['content'])}
</div>
<footer>
<p>Β© 2025 {AUTHOR_NAME}. All Rights Reserved.</p>
</footer>
</body>
</html>
"""
# Upload HTML
upload_file(
path_or_fileobj=html_content.encode(),
path_in_repo="index.html",
repo_id=space_name,
repo_type="space"
)
return f"https://huggingface.co/spaces/{space_name}", content_data['content']
except Exception as e:
return f"Error: {str(e)}", ""
# Gradio interface
with gr.Blocks() as app:
gr.Markdown("# Professional Blog Generator")
with gr.Row():
with gr.Column():
topic_input = gr.Textbox(label="Topic")
seo_input = gr.Textbox(label="SEO Keywords (comma-separated)")
generate_btn = gr.Button("Generate", variant="primary")
with gr.Column():
blog_link = gr.Markdown()
blog_output = gr.Markdown()
generate_btn.click(
fn=create_hosted_blog,
inputs=[topic_input, seo_input],
outputs=[blog_link, blog_output]
)
if __name__ == "__main__":
app.launch(share=True)