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 |