msaifee commited on
Commit
5631744
Β·
1 Parent(s): d7f7886

Blog Generator Agent

Browse files
Files changed (3) hide show
  1. .github/workflows/main.yaml +24 -0
  2. app.py +162 -0
  3. requirements.txt +8 -0
.github/workflows/main.yaml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face Space
2
+ on:
3
+ push:
4
+ branches: [main]
5
+
6
+ # to run this workflow manually from the Actions tab
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ sync-to-hub:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+ with:
15
+ fetch-depth: 0
16
+ lfs: false
17
+
18
+ - name: Ignore large files
19
+ run : git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch' HEAD
20
+
21
+ - name: Push to hub
22
+ env:
23
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
24
+ run: git push --force https://msaifee:$HF_TOKEN@huggingface.co/spaces/msaifee/BlogGeneratorAgent main
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from dotenv import load_dotenv
3
+ import os
4
+ from langchain_groq import ChatGroq
5
+ from typing_extensions import TypedDict
6
+ from langgraph.graph import add_messages, StateGraph, END, START
7
+ from langchain_core.messages import AIMessage, HumanMessage
8
+ from typing import Annotated, List
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+ # Set up Groq client
14
+ os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")
15
+ llm = ChatGroq(model="qwen-2.5-32b")
16
+
17
+ # Define BlogState TypedDict
18
+ class BlogState(TypedDict):
19
+ topic: str
20
+ title: str
21
+ blog_content: Annotated[List, add_messages]
22
+ reviewed_content: Annotated[List, add_messages]
23
+ is_blog_ready: str
24
+
25
+ # Initialize session state
26
+ if 'blog_state' not in st.session_state:
27
+ st.session_state.blog_state = None
28
+ if 'graph' not in st.session_state:
29
+ st.session_state.graph = None
30
+
31
+ def init_graph():
32
+ builder = StateGraph(BlogState)
33
+
34
+ builder.add_node("title_generator", generate_title)
35
+ builder.add_node("content_generator", generate_content)
36
+ builder.add_node("content_reviewer", review_content)
37
+ builder.add_node("quality_check", evaluate_content)
38
+
39
+ builder.add_edge(START, "title_generator")
40
+ builder.add_edge("title_generator", "content_generator")
41
+ builder.add_edge("content_generator", "content_reviewer")
42
+ builder.add_edge("content_reviewer", "quality_check")
43
+
44
+ builder.add_conditional_edges(
45
+ "quality_check",
46
+ route_based_on_verdict,
47
+ {"Pass": END, "Fail": "content_generator"}
48
+ )
49
+ return builder.compile()
50
+
51
+ # Node functions with state management
52
+ def generate_title(state: BlogState):
53
+ prompt = f"""Generate compelling blog title options about {state["topic"]} that are:
54
+ - SEO-friendly
55
+ - Attention-grabbing
56
+ - Between 6-12 words"""
57
+
58
+ with st.status("πŸš€ Generating Titles..."):
59
+ response = llm.invoke(prompt)
60
+ state["title"] = response.content.split("\n")[0].strip('"')
61
+ st.write(f"Selected title: **{state['title']}**")
62
+ return state
63
+
64
+ def generate_content(state: BlogState):
65
+ prompt = f"""Write a comprehensive blog post titled "{state["title"]}" with:
66
+ 1. Engaging introduction with hook
67
+ 2. 3-5 subheadings with detailed content
68
+ 3. Practical examples/statistics
69
+ 4. Clear transitions between sections
70
+ 5. Actionable conclusion
71
+ Style: Professional yet conversational (Flesch-Kincaid 60-70). Use markdown formatting"""
72
+
73
+ with st.status("πŸ“ Generating Content..."):
74
+ response = llm.invoke(prompt)
75
+ state["blog_content"].append(AIMessage(content=response.content))
76
+ st.markdown(response.content)
77
+ return state
78
+
79
+ def review_content(state: BlogState):
80
+ content = state["blog_content"][-1].content
81
+ prompt = f"""Critically review this blog content:
82
+ - Clarity & Structure
83
+ - Grammar & Style
84
+ - SEO optimization
85
+ - Reader engagement
86
+ Provide specific improvement suggestions. Content:\n{content}"""
87
+
88
+ with st.status("πŸ” Reviewing Content..."):
89
+ feedback = llm.invoke(prompt)
90
+ state["reviewed_content"].append(HumanMessage(content=feedback.content))
91
+ st.write(feedback.content)
92
+ return state
93
+
94
+ def evaluate_content(state: BlogState):
95
+ content = state["blog_content"][-1].content
96
+ feedback = state["reviewed_content"][-1].content
97
+
98
+ prompt = f"""Evaluate blog content against editorial feedback (Pass/Fail):
99
+ Content: {content}
100
+ Feedback: {feedback}
101
+ Answer only Pass or Fail:"""
102
+
103
+ with st.status("βœ… Evaluating Quality..."):
104
+ response = llm.invoke(prompt)
105
+ verdict = response.content.strip().upper()
106
+ state["is_blog_ready"] = "Pass" if "PASS" in verdict else "Fail"
107
+ state["reviewed_content"].append(AIMessage(
108
+ content=f"Verdict: {response.content}"
109
+ ))
110
+ st.write(f"Final Verdict: **{state['is_blog_ready']}**")
111
+ return state
112
+
113
+ def route_based_on_verdict(state: BlogState):
114
+ return "Pass" if state["is_blog_ready"] == "Pass" else "Fail"
115
+
116
+ # Streamlit UI components
117
+ st.title("AI Blog Generation Assistant")
118
+ st.markdown("### Generate high-quality blog posts with AI-powered review process")
119
+
120
+ topic = st.text_input("Enter your blog topic:", placeholder="Generative AI in Healthcare")
121
+ generate_btn = st.button("Generate Blog Post")
122
+
123
+
124
+ if generate_btn and topic:
125
+ st.session_state.graph = init_graph()
126
+ st.session_state.blog_state = BlogState(
127
+ topic=topic,
128
+ title="",
129
+ blog_content=[],
130
+ reviewed_content=[],
131
+ is_blog_ready=""
132
+ )
133
+
134
+ # Execute the graph
135
+ final_state = st.session_state.graph.invoke(st.session_state.blog_state)
136
+ st.session_state.blog_state = final_state
137
+
138
+ # Display results
139
+ st.success("Blog post generation complete!")
140
+ st.markdown("---")
141
+ st.subheader("Final Blog Post")
142
+ st.markdown(final_state["blog_content"][-1].content)
143
+
144
+ st.markdown("---")
145
+ st.subheader("Quality Assurance Report")
146
+ st.write(final_state["reviewed_content"][-1].content)
147
+ # st.write(f"Final Verdict: {final_state['is_blog_ready']}")
148
+
149
+
150
+ elif generate_btn and not topic:
151
+ st.error("Please enter a blog topic to get started!")
152
+
153
+ if st.session_state.blog_state:
154
+ with st.sidebar:
155
+ st.subheader("Generation Details")
156
+ st.write(f"**Topic:** {st.session_state.blog_state['topic']}")
157
+ st.write(f"**Status**: {'βœ… Approved' if st.session_state.blog_state['is_blog_ready'] == 'Pass' else '❌ Needs Revision'}")
158
+ st.write(f"**Review Cycles**: {len(st.session_state.blog_state['reviewed_content']) - 1}")
159
+
160
+ if st.button("Reset Session"):
161
+ st.session_state.clear()
162
+ st.rerun()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ langgraph
3
+ langchain_community
4
+ langchain_core
5
+ langchain_groq
6
+ langchain_openai
7
+ faiss_cpu
8
+ streamlit