neuralgeekroot commited on
Commit
2257102
·
1 Parent(s): 100ed5d

Blog Generation application

Browse files
Files changed (4) hide show
  1. .gitignore +2 -0
  2. app.py +173 -0
  3. images/blog-generation.png +0 -0
  4. requirements.txt +8 -0
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ blogGeneration/
2
+ .env
app.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ from langchain_core.prompts import PromptTemplate
4
+ from langchain_groq import ChatGroq
5
+ from dotenv import load_dotenv
6
+ from langgraph.graph import StateGraph, START, END
7
+ from pydantic import BaseModel, Field
8
+ from typing import List, TypedDict, Annotated
9
+ from langgraph.constants import Send
10
+ import operator
11
+ from langchain_core.messages import SystemMessage, HumanMessage
12
+
13
+ # Load environment variables
14
+ load_dotenv()
15
+ os.environ['GROQ_API_KEY'] = os.getenv('GROQ_API_KEY')
16
+ os.environ['LANGCHAIN_API_KEY'] = os.getenv('LANGCHAIN_API_KEY')
17
+ os.environ['LANGSMITH_TRACING_V2'] = 'true'
18
+
19
+ # Initialize LLM
20
+ llm = ChatGroq(model='llama3-70b-8192')
21
+
22
+ # Define section structure
23
+ class Section(BaseModel):
24
+ section_name: str = Field(description="Section name")
25
+ description: str = Field(description="Description of the section")
26
+
27
+ class Sections(BaseModel):
28
+ sections: List[Section] = Field(description="List of section details")
29
+
30
+ structured_sections = llm.with_structured_output(Sections)
31
+
32
+ # Define blog state
33
+ class BlogState(TypedDict):
34
+ topic: str
35
+ outline: str
36
+ sections: list[Section]
37
+ completed_section: Annotated[list, operator.add]
38
+ review_content: str
39
+ send_seo_optimization: str
40
+ revise_section_content: list[str]
41
+ finalize_blog: str
42
+ step: str
43
+ final_blog: str
44
+
45
+ class BlogStateSection(TypedDict):
46
+ section: Section
47
+ completed_sections: Annotated[list, operator.add]
48
+
49
+ # Orchestrator node to generate an outline
50
+ def generate_outline(state: BlogState):
51
+ st.write("Generating an outline for the blog...")
52
+ result = structured_sections.invoke([
53
+ SystemMessage(content="Provide an interesting and informative content outline for the given {topic}."),
54
+ HumanMessage(content=f"Here is the blog topic: {state['topic']}")
55
+ ])
56
+ return {'topic': state['topic'], 'outline': result.sections}
57
+
58
+ # Worker node to write sections
59
+ def write_section(state: BlogStateSection):
60
+ st.write("Generating content for the section...")
61
+ section_content = llm.invoke([
62
+ SystemMessage(content="Write a detailed blog section based on the provided name and description."),
63
+ HumanMessage(content=f"Section Name: {state['section'].section_name}, Description: {state['section'].description}")
64
+ ])
65
+ return {"completed_section": [section_content.content]}
66
+
67
+ # Review node to check the quality of sections
68
+ def review_section(state: BlogState):
69
+ st.write("Reviewing the section...")
70
+ prompt = PromptTemplate.from_template(
71
+ "Check if the section can be improved: {completed_section}. "
72
+ "If no, return 'send_seo_optimization'. "
73
+ "If yes, return 'revise_section_content'."
74
+ )
75
+ chain = prompt | llm
76
+ result = chain.invoke({'completed_section': state['completed_section']})
77
+
78
+ decision = result.content.strip().lower()
79
+ if decision not in ["send_seo_optimization", "revise_section_content"]:
80
+ decision = "send_seo_optimization"
81
+
82
+ return {"step": decision}
83
+
84
+ # Revision node to improve content
85
+ def revise_section(state: BlogState):
86
+ st.write("Revising the section content...")
87
+ if state['step'] == "revise_section_content":
88
+ revised_content = llm.invoke([
89
+ SystemMessage(content="Based on the review feedback, improve the content further."),
90
+ HumanMessage(content=f"Section Name: {state['sections'][0].section_name}, Description: {state['sections'][0].description}")
91
+ ])
92
+ return {"completed_section": [revised_content.content]}
93
+
94
+ # Assign writers dynamically to sections
95
+ def assign_writers(state: BlogState):
96
+ st.write("Assigning writers to sections...")
97
+ return [Send('write_section', {'section': s}) for s in state['outline']]
98
+
99
+ # Decision function for routing after review
100
+ def should_revise(state: BlogState):
101
+ return state["step"]
102
+
103
+ # SEO Optimization step
104
+ def seo_optimization(state: BlogState):
105
+ st.write("Performing SEO optimization...")
106
+ result = llm.invoke(f"Optimize the blog for search ranking: {state['topic']}")
107
+ return {'finalize_blog': result.content}
108
+
109
+ # Final publishing step
110
+ def publish_blog(state: BlogState):
111
+ st.write("Finalizing and publishing the blog...")
112
+ return {"final_blog": state['finalize_blog']}
113
+
114
+ # Build LangGraph workflow
115
+ builder = StateGraph(BlogState)
116
+
117
+ # Add orchestrator nodes
118
+ builder.add_node('generate_outline', generate_outline)
119
+
120
+ # Add worker and review nodes
121
+ builder.add_node('write_section', write_section)
122
+ builder.add_node('review_section', review_section)
123
+ builder.add_node('revise_section', revise_section)
124
+
125
+ # Add finalization nodes
126
+ builder.add_node('seo_optimization', seo_optimization)
127
+ builder.add_node('publish_blog', publish_blog)
128
+
129
+ # Define workflow edges
130
+ builder.add_edge(START, 'generate_outline')
131
+ builder.add_conditional_edges('generate_outline', assign_writers, ['write_section'])
132
+ builder.add_edge('write_section', 'review_section')
133
+ builder.add_conditional_edges('review_section', should_revise, {'revise_section_content': 'revise_section', 'send_seo_optimization': 'seo_optimization'})
134
+ builder.add_edge('revise_section', 'review_section') # Loop back after revision
135
+ builder.add_edge('seo_optimization', 'publish_blog')
136
+ builder.add_edge('publish_blog', END)
137
+
138
+ # Compile workflow
139
+ workflow = builder.compile()
140
+
141
+ # Streamlit app
142
+ def main():
143
+ st.title("Blog Writing Assistant")
144
+
145
+ # Input for blog topic
146
+ topic = st.text_input("Enter the blog topic:")
147
+
148
+ if st.button("Generate Blog"):
149
+ if topic:
150
+ # Define initial state
151
+ initial_state = {
152
+ 'topic': topic,
153
+ 'outline': "",
154
+ 'sections': [],
155
+ 'completed_section': [],
156
+ 'review_content': "",
157
+ 'send_seo_optimization': "",
158
+ 'revise_section_content': [],
159
+ 'finalize_blog': "",
160
+ 'step': ""
161
+ }
162
+
163
+ # Invoke workflow
164
+ result = workflow.invoke(initial_state)
165
+
166
+ # Display final result
167
+ st.subheader("Final Blog Content")
168
+ st.write(result['final_blog'])
169
+ else:
170
+ st.error("Please enter a blog topic.")
171
+
172
+ if __name__ == "__main__":
173
+ main()
images/blog-generation.png ADDED
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ langgraph
3
+ langchain-openai
4
+ langsmith
5
+ streamlit
6
+ langgraph
7
+ langchain_groq
8
+ dotenv