Spaces:
Sleeping
Sleeping
# blog_app.py | |
import streamlit as st | |
from typing import TypedDict, List, Optional | |
from langgraph.graph import StateGraph, END | |
import groq | |
import re | |
import os | |
from dotenv import load_dotenv | |
import streamlit as st | |
load_dotenv() | |
# Configure Groq client | |
GROQ_API_KEY = os.getenv("GROQ_API_KEY") | |
# Configure Groq client | |
client = groq.Groq(api_key=GROQ_API_KEY) | |
# Define state structure | |
class BlogState(TypedDict): | |
keyword: str | |
titles: List[str] | |
selected_title: Optional[str] | |
blog_content: Optional[str] | |
# Initialize LangGraph workflow | |
def create_workflow(): | |
workflow = StateGraph(BlogState) | |
# Define nodes | |
def generate_titles(state: BlogState): | |
prompt = f"""Generate 5 compelling blog titles on {state['keyword']}. | |
Ensure below standards | |
1. The titles should be attention-grabbing, SEO-friendly, and formatted in a way that encourages clicks. | |
2. Use a mix of listicles, how-to guides, thought leadership, and trending topics. | |
3. Ensure the titles are unique and cater to a wide audience. | |
4. Each Title should be Maximum 50 characters | |
5. Generate 5 Titles """ | |
try: | |
response = client.chat.completions.create( | |
messages=[{"role": "user", "content": prompt}], | |
model="llama3-70b-8192", | |
#model="qwen-2.5-32b", | |
temperature=0.7, | |
max_tokens=200 | |
) | |
raw_output = re.sub(r'<\/?[a-zA-Z]+>', '', response.choices[0].message.content, flags=re.DOTALL).strip() | |
titles = [line.split(". ", 1)[1].strip() for line in raw_output.split("\n") if ". " in line[:4]][:5] | |
return {"titles": titles} | |
except Exception as e: | |
st.error(f"Title generation failed: {str(e)}") | |
return {"titles": []} | |
def generate_content(state: BlogState): | |
prompt = f"""Write a comprehensive 1500-word blog post titled "{state['selected_title']}". | |
Write a detailed and engaging blog post on the topic '[topic]'. | |
The blog should have a captivating introduction, well-structured sections with informative subheadings, and a strong conclusion. | |
Use a conversational yet professional tone, include real-world examples, and provide actionable insights. | |
Ensure the content is SEO-optimized with relevant keywords, bullet points, and headings to improve readability. | |
Keep the word count between 1000 and 1500 words. | |
""" | |
try: | |
response = client.chat.completions.create( | |
messages=[{"role": "user", "content": prompt}], | |
model="llama3-70b-8192", | |
temperature=0.8, | |
max_tokens=3000 | |
) | |
content = re.sub(r'<\/?[a-zA-Z]+>', '', response.choices[0].message.content, flags=re.DOTALL).strip() | |
return {"blog_content": content} | |
except Exception as e: | |
st.error(f"Content generation failed: {str(e)}") | |
return {"blog_content": ""} | |
# Add nodes to workflow | |
workflow.add_node("generate_titles", generate_titles) | |
workflow.add_node("generate_content", generate_content) | |
# Define edges | |
workflow.set_entry_point("generate_titles") | |
def route_after_titles(state: BlogState): | |
return "generate_content" if state.get("selected_title") else END | |
workflow.add_conditional_edges( | |
"generate_titles", | |
route_after_titles | |
) | |
workflow.add_edge("generate_content", END) | |
return workflow.compile() | |
# Streamlit UI | |
st.title("AI Blog Generator") | |
app = create_workflow() | |
# Initialize session state | |
if 'blog_state' not in st.session_state: | |
st.session_state.blog_state = { | |
"keyword": "", | |
"titles": [], | |
"selected_title": None, | |
"blog_content": None | |
} | |
# Input Section | |
keyword = st.text_input("Enter blog topic keyword:", | |
value=st.session_state.blog_state["keyword"]) | |
# Generate Titles | |
if st.button("Generate Titles") and keyword.strip(): | |
with st.spinner("Generating title options..."): | |
new_state = app.invoke({ | |
"keyword": keyword.strip(), | |
"titles": [], | |
"selected_title": None, | |
"blog_content": None | |
}) | |
st.session_state.blog_state = new_state | |
# Display Titles | |
if st.session_state.blog_state["titles"]: | |
st.subheader("Generated Titles") | |
selected_idx = st.radio("Select a title:", | |
options=range(len(st.session_state.blog_state["titles"])), | |
format_func=lambda x: st.session_state.blog_state["titles"][x]) | |
# Store selection | |
st.session_state.blog_state["selected_title"] = st.session_state.blog_state["titles"][selected_idx] | |
# Generate Content | |
if st.session_state.blog_state["selected_title"] and not st.session_state.blog_state["blog_content"]: | |
if st.button("Generate Full Blog Post"): | |
with st.spinner("Writing blog post..."): | |
final_state = app.invoke(st.session_state.blog_state) | |
st.session_state.blog_state = final_state | |
# Display Content | |
if st.session_state.blog_state["blog_content"]: | |
st.subheader(st.session_state.blog_state["selected_title"]) | |
st.markdown(st.session_state.blog_state["blog_content"]) | |
# Download button | |
st.download_button( | |
"Download Text", | |
st.session_state.blog_state["blog_content"], | |
file_name=f"{st.session_state.blog_state['selected_title']}.txt" | |
) | |
# Reset functionality (corrected) | |
if st.button("Reset"): | |
st.session_state.blog_state = { | |
"keyword": "", | |
"titles": [], | |
"selected_title": None, | |
"blog_content": None | |
} | |
st.rerun() # Using current rerun method |