Spaces:
Sleeping
Sleeping
Commit Β·
cda8a27
1
Parent(s): 43e3fb7
Updated README.md file and app.py file
Browse files
README.md
CHANGED
|
@@ -1,6 +1,4 @@
|
|
| 1 |
-
|
| 2 |
-
This project automates blog generation using an LLM-powered workflow.
|
| 3 |
-
|
| 4 |
title: Blog Generation
|
| 5 |
emoji: π
|
| 6 |
colorFrom: Green
|
|
@@ -11,6 +9,10 @@ app_file: app.py
|
|
| 11 |
license: apache-2.0
|
| 12 |
pinned: false
|
| 13 |
short_description: Refined AiBlogGenerator
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
## π Setup
|
| 16 |
1. Clone repo
|
|
|
|
| 1 |
+
---
|
|
|
|
|
|
|
| 2 |
title: Blog Generation
|
| 3 |
emoji: π
|
| 4 |
colorFrom: Green
|
|
|
|
| 9 |
license: apache-2.0
|
| 10 |
pinned: false
|
| 11 |
short_description: Refined AiBlogGenerator
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# Blog Generation Automation with LangGraph
|
| 15 |
+
This project automates blog generation using an LLM-powered workflow.
|
| 16 |
|
| 17 |
## π Setup
|
| 18 |
1. Clone repo
|
app.py
CHANGED
|
@@ -9,6 +9,7 @@ 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()
|
|
@@ -21,6 +22,7 @@ os.environ['LANGCHAIN_PROJECT_NAME'] = os.getenv('LANGCHAIN_PROJECT_NAME')
|
|
| 21 |
llm = ChatGroq(model='llama3-70b-8192')
|
| 22 |
|
| 23 |
# Define section structure
|
|
|
|
| 24 |
class Section(BaseModel):
|
| 25 |
section_name: str = Field(description="Section name")
|
| 26 |
description: str = Field(description="Description of the section")
|
|
@@ -31,6 +33,7 @@ class Sections(BaseModel):
|
|
| 31 |
structured_sections = llm.with_structured_output(Sections)
|
| 32 |
|
| 33 |
# Define blog state
|
|
|
|
| 34 |
class BlogState(TypedDict):
|
| 35 |
topic: str
|
| 36 |
outline: str
|
|
@@ -43,11 +46,13 @@ class BlogState(TypedDict):
|
|
| 43 |
step: str
|
| 44 |
final_blog: str
|
| 45 |
|
|
|
|
| 46 |
class BlogStateSection(TypedDict):
|
| 47 |
section: Section
|
| 48 |
completed_sections: Annotated[list, operator.add]
|
| 49 |
|
| 50 |
# Orchestrator node to generate an outline
|
|
|
|
| 51 |
def generate_outline(state: BlogState):
|
| 52 |
st.write("Generating an outline for the blog...")
|
| 53 |
result = structured_sections.invoke([
|
|
@@ -57,6 +62,7 @@ def generate_outline(state: BlogState):
|
|
| 57 |
return {'topic': state['topic'], 'outline': result.sections}
|
| 58 |
|
| 59 |
# Worker node to write sections
|
|
|
|
| 60 |
def write_section(state: BlogStateSection):
|
| 61 |
st.write("Generating content for the section...")
|
| 62 |
section_content = llm.invoke([
|
|
@@ -66,6 +72,7 @@ def write_section(state: BlogStateSection):
|
|
| 66 |
return {"completed_section": [section_content.content]}
|
| 67 |
|
| 68 |
# Review node to check the quality of sections
|
|
|
|
| 69 |
def review_section(state: BlogState):
|
| 70 |
st.write("Reviewing the section...")
|
| 71 |
prompt = PromptTemplate.from_template(
|
|
@@ -83,6 +90,7 @@ def review_section(state: BlogState):
|
|
| 83 |
return {"step": decision}
|
| 84 |
|
| 85 |
# Revision node to improve content
|
|
|
|
| 86 |
def revise_section(state: BlogState):
|
| 87 |
st.write("Revising the section content...")
|
| 88 |
if state['step'] == "revise_section_content":
|
|
@@ -93,6 +101,7 @@ def revise_section(state: BlogState):
|
|
| 93 |
return {"completed_section": [revised_content.content]}
|
| 94 |
|
| 95 |
# Assign writers dynamically to sections
|
|
|
|
| 96 |
def assign_writers(state: BlogState):
|
| 97 |
st.write("Assigning writers to sections...")
|
| 98 |
return [Send('write_section', {'section': s}) for s in state['outline']]
|
|
@@ -102,12 +111,14 @@ def should_revise(state: BlogState):
|
|
| 102 |
return state["step"]
|
| 103 |
|
| 104 |
# SEO Optimization step
|
|
|
|
| 105 |
def seo_optimization(state: BlogState):
|
| 106 |
st.write("Performing SEO optimization...")
|
| 107 |
result = llm.invoke(f"Optimize the blog for search ranking: {state['topic']}")
|
| 108 |
return {'finalize_blog': result.content}
|
| 109 |
|
| 110 |
# Final publishing step
|
|
|
|
| 111 |
def publish_blog(state: BlogState):
|
| 112 |
st.write("Finalizing and publishing the blog...")
|
| 113 |
return {"final_blog": state['finalize_blog']}
|
|
|
|
| 9 |
from langgraph.constants import Send
|
| 10 |
import operator
|
| 11 |
from langchain_core.messages import SystemMessage, HumanMessage
|
| 12 |
+
from langsmith import
|
| 13 |
|
| 14 |
# Load environment variables
|
| 15 |
load_dotenv()
|
|
|
|
| 22 |
llm = ChatGroq(model='llama3-70b-8192')
|
| 23 |
|
| 24 |
# Define section structure
|
| 25 |
+
@traceable
|
| 26 |
class Section(BaseModel):
|
| 27 |
section_name: str = Field(description="Section name")
|
| 28 |
description: str = Field(description="Description of the section")
|
|
|
|
| 33 |
structured_sections = llm.with_structured_output(Sections)
|
| 34 |
|
| 35 |
# Define blog state
|
| 36 |
+
@traceable
|
| 37 |
class BlogState(TypedDict):
|
| 38 |
topic: str
|
| 39 |
outline: str
|
|
|
|
| 46 |
step: str
|
| 47 |
final_blog: str
|
| 48 |
|
| 49 |
+
@traceable
|
| 50 |
class BlogStateSection(TypedDict):
|
| 51 |
section: Section
|
| 52 |
completed_sections: Annotated[list, operator.add]
|
| 53 |
|
| 54 |
# Orchestrator node to generate an outline
|
| 55 |
+
@traceable
|
| 56 |
def generate_outline(state: BlogState):
|
| 57 |
st.write("Generating an outline for the blog...")
|
| 58 |
result = structured_sections.invoke([
|
|
|
|
| 62 |
return {'topic': state['topic'], 'outline': result.sections}
|
| 63 |
|
| 64 |
# Worker node to write sections
|
| 65 |
+
@traceable
|
| 66 |
def write_section(state: BlogStateSection):
|
| 67 |
st.write("Generating content for the section...")
|
| 68 |
section_content = llm.invoke([
|
|
|
|
| 72 |
return {"completed_section": [section_content.content]}
|
| 73 |
|
| 74 |
# Review node to check the quality of sections
|
| 75 |
+
@traceable
|
| 76 |
def review_section(state: BlogState):
|
| 77 |
st.write("Reviewing the section...")
|
| 78 |
prompt = PromptTemplate.from_template(
|
|
|
|
| 90 |
return {"step": decision}
|
| 91 |
|
| 92 |
# Revision node to improve content
|
| 93 |
+
@traceable
|
| 94 |
def revise_section(state: BlogState):
|
| 95 |
st.write("Revising the section content...")
|
| 96 |
if state['step'] == "revise_section_content":
|
|
|
|
| 101 |
return {"completed_section": [revised_content.content]}
|
| 102 |
|
| 103 |
# Assign writers dynamically to sections
|
| 104 |
+
@traceable
|
| 105 |
def assign_writers(state: BlogState):
|
| 106 |
st.write("Assigning writers to sections...")
|
| 107 |
return [Send('write_section', {'section': s}) for s in state['outline']]
|
|
|
|
| 111 |
return state["step"]
|
| 112 |
|
| 113 |
# SEO Optimization step
|
| 114 |
+
@traceable
|
| 115 |
def seo_optimization(state: BlogState):
|
| 116 |
st.write("Performing SEO optimization...")
|
| 117 |
result = llm.invoke(f"Optimize the blog for search ranking: {state['topic']}")
|
| 118 |
return {'finalize_blog': result.content}
|
| 119 |
|
| 120 |
# Final publishing step
|
| 121 |
+
@traceable
|
| 122 |
def publish_blog(state: BlogState):
|
| 123 |
st.write("Finalizing and publishing the blog...")
|
| 124 |
return {"final_blog": state['finalize_blog']}
|