msaifee's picture
Refined flows
1f64690
raw
history blame
6.65 kB
import streamlit as st
from dotenv import load_dotenv
import os
from langchain_groq import ChatGroq
from typing_extensions import TypedDict
from langgraph.graph import add_messages, StateGraph, END, START
from langchain_core.messages import AIMessage, HumanMessage
from typing import Annotated, List
# Load environment variables
load_dotenv()
# Set up Groq client
os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")
llm = ChatGroq(model="qwen-2.5-32b")
# Define BlogState TypedDict
class BlogState(TypedDict):
topic: str
title: str
blog_content: Annotated[List, add_messages]
reviewed_content: Annotated[List, add_messages]
is_blog_ready: str
# Initialize session state
if 'blog_state' not in st.session_state:
st.session_state.blog_state = None
if 'graph' not in st.session_state:
st.session_state.graph = None
if 'graph_image' not in st.session_state:
st.session_state.graph_image = None
def init_graph():
builder = StateGraph(BlogState)
builder.add_node("title_generator", generate_title)
builder.add_node("content_generator", generate_content)
builder.add_node("content_reviewer", review_content)
builder.add_node("quality_check", evaluate_content)
builder.add_edge(START, "title_generator")
builder.add_edge("title_generator", "content_generator")
builder.add_edge("content_generator", "content_reviewer")
builder.add_edge("content_reviewer", "quality_check")
builder.add_conditional_edges(
"quality_check",
route_based_on_verdict,
{"Pass": END, "Fail": "content_generator"}
)
return builder.compile()
# Node functions with state management
def generate_title(state: BlogState):
prompt = f"""Generate compelling blog title options about {state["topic"]} that are:
- SEO-friendly
- Attention-grabbing
- Between 6-12 words"""
with st.status("πŸš€ Generating Titles..."):
response = llm.invoke(prompt)
state["title"] = response.content.split("\n")[0].strip('"')
st.write(f"Selected title: **{state['title']}**")
return state
def generate_content(state: BlogState):
prompt = f"""Write a comprehensive blog post titled "{state["title"]}" with:
1. Engaging introduction with hook
2. 3-5 subheadings with detailed content
3. Practical examples/statistics
4. Clear transitions between sections
5. Actionable conclusion
Style: Professional yet conversational (Flesch-Kincaid 60-70). Use markdown formatting"""
with st.status("πŸ“ Generating Content..."):
response = llm.invoke(prompt)
state["blog_content"].append(AIMessage(content=response.content))
st.markdown(response.content)
return state
def review_content(state: BlogState):
content = state["blog_content"][-1].content
prompt = f"""Critically review this blog content:
- Clarity & Structure
- Grammar & Style
- SEO optimization
- Reader engagement
Provide specific improvement suggestions. Content:\n{content}"""
with st.status("πŸ” Reviewing Content..."):
feedback = llm.invoke(prompt)
state["reviewed_content"].append(HumanMessage(content=feedback.content))
st.write(feedback.content)
return state
def evaluate_content(state: BlogState):
content = state["blog_content"][-1].content
feedback = state["reviewed_content"][-1].content
prompt = f"""Evaluate blog content against editorial feedback (Pass/Fail):
Content: {content}
Feedback: {feedback}
Answer only Pass or Fail:"""
with st.status("βœ… Evaluating Quality..."):
response = llm.invoke(prompt)
verdict = response.content.strip().upper()
state["is_blog_ready"] = "Pass" if "PASS" in verdict else "Fail"
state["reviewed_content"].append(AIMessage(
content=f"Verdict: {response.content}"
))
st.write(f"Final Verdict: **{state['is_blog_ready']}**")
return state
def route_based_on_verdict(state: BlogState):
return "Pass" if state["is_blog_ready"] == "Pass" else "Fail"
# Streamlit UI components
st.title("πŸš€ BlogForge Pro")
st.markdown("""
**Smart Blog Generation with Auto-Refinement**
*From first draft to final edit - AI-assisted writing meets professional standards*
βœ“ SEO-Optimized Structure βœ“ Grammar & Style Checks βœ“ Feedback-Driven Revisions
""")
# Sidebar components
def show_sidebar():
with st.sidebar:
st.subheader("Configuration")
# Groq API Key Input
api_key = st.session_state["GROQ_API_KEY"] = st.text_input("Groq API Key",type="password")
# Validate API key
if not api_key:
st.warning("⚠️ Please enter your GROQ API key to proceed. Don't have? refer : https://console.groq.com/keys ")
if st.button("Reset Session"):
st.session_state.clear()
st.rerun()
show_sidebar()
# Main content
topic = st.text_input("Enter your blog topic:", placeholder="Generative AI in Healthcare")
generate_btn = st.button("Generate Blog Post")
if generate_btn:
if not topic:
st.error("Please enter a blog topic!")
st.stop()
try:
st.session_state.llm = ChatGroq(model="qwen-2.5-32b")
# Initialize and run graph
st.session_state.graph = init_graph()
st.session_state.blog_state = BlogState(
topic=topic,
title="",
blog_content=[],
reviewed_content=[],
is_blog_ready=""
)
# Execute the graph
final_state = st.session_state.graph.invoke(st.session_state.blog_state)
st.session_state.blog_state = final_state
# Display results
st.success("Blog post generation complete!")
st.markdown("---")
st.subheader("Final Blog Post")
st.markdown(final_state["blog_content"][-1].content)
st.markdown("---")
st.subheader("Quality Assurance Report")
st.write(final_state["reviewed_content"][-1].content)
if st.session_state.blog_state:
st.markdown("---")
st.subheader("Generation Status")
st.write(f"**Topic:** {st.session_state.blog_state['topic']}")
st.write(f"**Status**: {'βœ… Approved' if st.session_state.blog_state['is_blog_ready'] == 'Pass' else '❌ Needs Revision'}")
st.write(f"**Review Cycles**: {len(st.session_state.blog_state['reviewed_content']) - 1}")
except Exception as e:
st.error(f"Error in blog generation: {str(e)}")