mubashirhussaindev commited on
Commit
3b57b61
·
verified ·
1 Parent(s): 929f597

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +216 -64
agent.py CHANGED
@@ -1,68 +1,220 @@
1
- import streamlit as st
2
- import agent as ag
 
 
 
 
 
 
 
 
3
 
4
- # Page styling
5
- st.markdown(
6
- """
7
- <style>
8
- body {font-family: Arial, sans-serif;}
9
- .stButton>button {background-color: #4CAF50; color: white;}
10
- </style>
11
- """,
12
- unsafe_allow_html=True
 
 
 
 
13
  )
 
14
 
15
- # Page title
16
- st.markdown("<h1 style='text-align: center;'>Instagram and LinkedIn Content Generator</h1>", unsafe_allow_html=True)
17
-
18
- # Form for user input
19
- with st.form("platform"):
20
- topic = st.text_input(label="Topic", placeholder="e.g., Small Business Financing")
21
- platform = st.radio(label="Select a platform", options=["LinkedIn", "Instagram"])
22
- submit_button = st.form_submit_button("Generate Content", use_container_width=True)
23
-
24
- # Process form submission
25
- if submit_button:
26
- if topic:
27
- with st.spinner("Generating content..."):
28
- try:
29
- # Initialize state for agent.py
30
- input_data = {
31
- "topic": topic,
32
- "platform": platform,
33
- "tips": "",
34
- "search_results": [],
35
- "post": "",
36
- "engagement_score": 0,
37
- "tone_score": 0,
38
- "clarity_score": 0,
39
- "image_url": ""
40
- }
41
- # Run the workflow
42
- output = ag.graph.invoke(input_data)
43
-
44
- # Display results
45
- st.markdown("**Generated Post:**")
46
- st.markdown(output['post'])
47
-
48
- # Display image URLs and images
49
- if output['image_url']:
50
- st.markdown("**Image URLs:**")
51
- for i, url in enumerate(output['image_url']):
52
- st.markdown(f"[Image {i+1}]({url})")
53
- try:
54
- st.image(url, caption=f"Image {i+1} from Pexels", use_column_width=True)
55
- except:
56
- st.warning(f"Could not display Image {i+1}. Use the URL above.")
57
- else:
58
- st.warning("No images were fetched from Pexels.")
59
-
60
- # Display evaluation scores
61
- st.markdown("**Evaluation Scores:**")
62
- st.write(f"Engagement: {output['engagement_score']}")
63
- st.write(f"Tone: {output['tone_score']}")
64
- st.write(f"Clarity: {output['clarity_score']}")
65
- except Exception as e:
66
- st.error(f"Error generating content: {str(e)}")
67
  else:
68
- st.error("Please provide a topic.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_huggingface import HuggingFacePipeline
2
+ from transformers import pipeline
3
+ import requests
4
+ from bs4 import BeautifulSoup
5
+ import os
6
+ from typing import List, TypedDict
7
+ from dataclasses import dataclass
8
+ from langgraph.graph import StateGraph, START, END
9
+ from dotenv import load_dotenv
10
+ from time import sleep
11
 
12
+ load_dotenv()
13
+
14
+ @dataclass
15
+ class Command:
16
+ update: dict = None
17
+ goto: str = None
18
+
19
+ # Initialize local Hugging Face model
20
+ hf_pipeline = pipeline(
21
+ "text2text-generation",
22
+ model="google/flan-t5-small",
23
+ max_length=512,
24
+ temperature=0.7,
25
  )
26
+ model = HuggingFacePipeline(pipeline=hf_pipeline)
27
 
28
+ def scrape_startpage(query: str, max_results: int = 3) -> List[dict]:
29
+ """Scrape search results from Startpage."""
30
+ url = f"https://www.startpage.com/sp/search?query={query.replace(' ', '+')}"
31
+ headers = {
32
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
33
+ }
34
+ for attempt in range(3):
35
+ try:
36
+ response = requests.get(url, headers=headers, timeout=10)
37
+ response.raise_for_status()
38
+ soup = BeautifulSoup(response.text, "html.parser")
39
+ results = []
40
+ # Extract search result snippets
41
+ for result in soup.find_all("div", class_="result")[:max_results]:
42
+ title = result.find("h3") or result.find("a")
43
+ snippet = result.find("p", class_="desc")
44
+ title_text = title.get_text().strip() if title else "No title"
45
+ snippet_text = snippet.get_text().strip() if snippet else "No snippet"
46
+ results.append({"title": title_text, "snippet": snippet_text})
47
+ return results
48
+ except Exception as e:
49
+ print(f"Error scraping Startpage (attempt {attempt+1}/3): {str(e)}")
50
+ if attempt < 2:
51
+ sleep(2 ** attempt) # Exponential backoff
52
+ continue
53
+ return []
54
+
55
+ def get_platform_tips(state) -> Command:
56
+ """Scrape tips on writing effective posts for the provided platform from Startpage."""
57
+ query = f"tips on how to write an effective post on {state['platform']}"
58
+ search_results = scrape_startpage(query, max_results=3)
59
+ if search_results:
60
+ prompt = f"""
61
+ Summarize the tips provided in {search_results}. These tips will be used to generate a {state['platform']} post.
62
+ Output as plain text.
63
+ """
64
+ response = model.invoke(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  else:
66
+ response = f"Use a professional tone, include a clear call-to-action, and keep the post concise for {state['platform']}."
67
+ return Command(update={"tips": response}, goto="web_search")
68
+
69
+ def web_search(state) -> Command:
70
+ """Scrape up-to-date information about the provided topic from Startpage."""
71
+ search_results = scrape_startpage(state["topic"], max_results=3)
72
+ return Command(update={"search_results": search_results}, goto="generate_post")
73
+
74
+ def generate_social_media_post(state) -> Command:
75
+ """Generate a social media post for a B2B bank."""
76
+ prompt = f"""
77
+ You are a social media strategist for a B2B bank. Generate a {state["platform"]} post.
78
+ The post should:
79
+ - Be engaging but professional.
80
+ - Provide value to corporate clients.
81
+ - Focus on {state["topic"]}.
82
+ - Incorporate information from {state["search_results"] if state["search_results"] else "general knowledge about the topic"}
83
+ Output as plain text.
84
+ """
85
+ response = model.invoke(prompt)
86
+ return Command(update={"post": response}, goto="evaluate_engagement")
87
+
88
+ def evaluate_engagement(state) -> Command:
89
+ """Assess how engaging the post is."""
90
+ prompt = f"""
91
+ Score the following post on engagement (1-10) based on the provided social media platform.
92
+ Consider clarity, readability, and compelling call-to-action.
93
+
94
+ Platform: {state["platform"]}
95
+ Post: {state["post"]}
96
+
97
+ Respond with only a number between 1 and 10, no text.
98
+ """
99
+ score = model.invoke(prompt).strip()
100
+ return Command(update={"engagement_score": score}, goto="evaluate_tone")
101
+
102
+ def evaluate_tone(state) -> Command:
103
+ """Check if the post maintains a professional yet engaging tone."""
104
+ prompt = f"""
105
+ Score the post’s tone (1-10). Ensure it's:
106
+ - Professional but not too rigid.
107
+ - Trustworthy and aligned with B2B financial services.
108
+ - Aligns with the specified platform.
109
+ Platform: {state["platform"]}
110
+ Post: {state["post"]}
111
+
112
+ Respond with only a number between 1 and 10, no text.
113
+ """
114
+ score = model.invoke(prompt).strip()
115
+ return Command(update={"tone_score": score}, goto="evaluate_clarity")
116
+
117
+ def evaluate_clarity(state) -> Command:
118
+ """Ensure the post is clear and not overly technical."""
119
+ prompt = f"""
120
+ Score the post on clarity (1-10).
121
+ - Avoids jargon.
122
+ - Easy to read for busy corporate professionals.
123
+ - Appropriate for the social media platform.
124
+ Platform: {state["platform"]}
125
+ Post: {state["post"]}
126
+
127
+ Respond with only a number between 1 and 10, no text.
128
+ """
129
+ score = model.invoke(prompt).strip()
130
+ return Command(update={"clarity_score": score}, goto="revise_if_needed")
131
+
132
+ def revise_if_needed(state) -> Command:
133
+ """Revise post if average evaluation score is below a threshold."""
134
+ try:
135
+ scores = [int(state["engagement_score"]), int(state["tone_score"]), int(state["clarity_score"])]
136
+ except ValueError:
137
+ return Command(update={"post": "Error: Non-numeric scores received."}, goto="get_image")
138
+ avg_score = sum(scores) / len(scores)
139
+
140
+ if avg_score < 7:
141
+ prompt = f"""
142
+ Revise this post to improve clarity, engagement, and tone:
143
+
144
+ {state["post"]}
145
+
146
+ Improve based on the following scores:
147
+ Engagement: {state["engagement_score"]}
148
+ Tone: {state["tone_score"]}
149
+ Clarity: {state["clarity_score"]}
150
+ """
151
+ revised_post = model.invoke(prompt)
152
+ return Command(update={"post": revised_post}, goto="get_image")
153
+
154
+ return Command(goto="get_image")
155
+
156
+ def fetch_image(state) -> Command:
157
+ """Fetch an image from Pexels based on the provided text."""
158
+ prompt = f"""
159
+ You are a search optimization assistant. Your task is to take a topic and improve it to ensure the best image results from an image search API like Pexels. Follow these steps:
160
+ 1. Normalize the input: Convert all text to lowercase and remove special characters (except for spaces).
161
+ 2. Add more descriptive terms: If the query is broad (e.g., "nature"), add more specific keywords like "landscape" or "outdoor".
162
+ 3. Use synonyms and related terms: For terms with multiple meanings, add variations (e.g., "car" -> "vehicle automobile").
163
+ 4. Specify style and tone: Add words like "professional", "modern", or "corporate" for B2B contexts.
164
+ 5. Categorize the query: Add related terms for domains like "business", "finance", or "corporate".
165
+ Topic: {state['topic']}
166
+ """
167
+ url = "https://api.pexels.com/v1/search"
168
+ params = {
169
+ "query": model.invoke(prompt).strip(),
170
+ "per_page": 5,
171
+ "page": 1
172
+ }
173
+ headers = {
174
+ "Authorization": os.getenv("PEXELS_API_KEY")
175
+ }
176
+ for attempt in range(3):
177
+ try:
178
+ response = requests.get(url, headers=headers, params=params)
179
+ response.raise_for_status()
180
+ data = response.json()
181
+ urls = [photo['url'] for photo in data.get('photos', [])]
182
+ return Command(update={"image_url": urls}, goto=END)
183
+ except requests.RequestException as e:
184
+ print(f"Error fetching images from Pexels (attempt {attempt+1}/3): {e}")
185
+ if attempt < 2:
186
+ sleep(2 ** attempt) # Exponential backoff
187
+ continue
188
+ return Command(update={"image_url": []}, goto=END)
189
+
190
+ class State(TypedDict):
191
+ topic: str
192
+ platform: str
193
+ tips: str
194
+ search_results: List[dict]
195
+ post: str
196
+ engagement_score: int
197
+ tone_score: int
198
+ clarity_score: int
199
+ image_url: str
200
+
201
+ workflow = StateGraph(State)
202
+ workflow.add_node("get_tips", get_platform_tips)
203
+ workflow.add_node("web_search", web_search)
204
+ workflow.add_node("generate_post", generate_social_media_post)
205
+ workflow.add_node("evaluate_engagement", evaluate_engagement)
206
+ workflow.add_node("evaluate_tone", evaluate_tone)
207
+ workflow.add_node("evaluate_clarity", evaluate_clarity)
208
+ workflow.add_node("revise_if_needed", revise_if_needed)
209
+ workflow.add_node("get_image", fetch_image)
210
+
211
+ workflow.add_edge(START, "get_tips")
212
+ workflow.add_edge("get_tips", "web_search")
213
+ workflow.add_edge("web_search", "generate_post")
214
+ workflow.add_edge("generate_post", "evaluate_engagement")
215
+ workflow.add_edge("evaluate_engagement", "evaluate_tone")
216
+ workflow.add_edge("evaluate_tone", "evaluate_clarity")
217
+ workflow.add_edge("evaluate_clarity", "revise_if_needed")
218
+ workflow.add_edge("revise_if_needed", "get_image")
219
+
220
+ graph = workflow.compile()