mubashirhussaindev commited on
Commit
7244d4f
·
verified ·
1 Parent(s): 7e2cf6b

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +75 -114
agent.py CHANGED
@@ -1,22 +1,23 @@
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",
@@ -26,18 +27,19 @@ hf_pipeline = pipeline(
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")
@@ -46,159 +48,118 @@ def scrape_startpage(query: str, max_results: int = 3) -> List[dict]:
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)
@@ -217,4 +178,4 @@ 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()
 
 
 
 
 
1
  import os
2
+ import requests
3
+ from time import sleep
4
  from dataclasses import dataclass
5
+ from typing import List, Optional, TypedDict
6
+
7
+ from transformers import pipeline
8
+ from langchain_huggingface import HuggingFacePipeline
9
+ from bs4 import BeautifulSoup
10
  from dotenv import load_dotenv
11
+ from langgraph.graph import StateGraph, START, END
12
 
13
  load_dotenv()
14
 
15
  @dataclass
16
  class Command:
17
+ update: Optional[dict] = None
18
+ goto: Optional[str] = None
19
 
20
+ # Initialize HF pipeline and wrap
21
  hf_pipeline = pipeline(
22
  "text2text-generation",
23
  model="google/flan-t5-small",
 
27
  model = HuggingFacePipeline(pipeline=hf_pipeline)
28
 
29
  def scrape_startpage(query: str, max_results: int = 3) -> List[dict]:
 
30
  url = f"https://www.startpage.com/sp/search?query={query.replace(' ', '+')}"
31
  headers = {
32
+ "User-Agent": (
33
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
34
+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
35
+ )
36
  }
37
  for attempt in range(3):
38
  try:
39
+ r = requests.get(url, headers=headers, timeout=10)
40
+ r.raise_for_status()
41
+ soup = BeautifulSoup(r.text, "html.parser")
42
  results = []
 
43
  for result in soup.find_all("div", class_="result")[:max_results]:
44
  title = result.find("h3") or result.find("a")
45
  snippet = result.find("p", class_="desc")
 
48
  results.append({"title": title_text, "snippet": snippet_text})
49
  return results
50
  except Exception as e:
51
+ print(f"Scrape error (try {attempt+1}): {e}")
52
  if attempt < 2:
53
+ sleep(2 ** attempt)
54
  continue
55
  return []
56
 
57
  def get_platform_tips(state) -> Command:
 
58
  query = f"tips on how to write an effective post on {state['platform']}"
59
+ results = scrape_startpage(query)
60
+ if results:
61
+ prompt = f"Summarize tips for {state['platform']} post: {results}. Output plain text."
 
 
 
62
  response = model.invoke(prompt)
63
  else:
64
+ response = f"Write a concise professional post with a call-to-action on {state['platform']}."
65
  return Command(update={"tips": response}, goto="web_search")
66
 
67
  def web_search(state) -> Command:
68
+ results = scrape_startpage(state["topic"])
69
+ return Command(update={"search_results": results}, goto="generate_post")
 
70
 
71
  def generate_social_media_post(state) -> Command:
 
72
  prompt = f"""
73
+ Create a {state['platform']} post on {state['topic']} for B2B bank clients.
74
+ Use info: {state.get('search_results', [])}
75
+ Make it professional and engaging. Output text only.
 
 
 
 
76
  """
77
  response = model.invoke(prompt)
78
  return Command(update={"post": response}, goto="evaluate_engagement")
79
 
80
  def evaluate_engagement(state) -> Command:
 
81
  prompt = f"""
82
+ Rate engagement 1-10 for post on {state['platform']}:
83
+ {state['post']}
84
+ Reply only a number.
 
 
 
 
85
  """
86
  score = model.invoke(prompt).strip()
87
  return Command(update={"engagement_score": score}, goto="evaluate_tone")
88
 
89
  def evaluate_tone(state) -> Command:
 
90
  prompt = f"""
91
+ Rate tone 1-10 (professional/trustworthy) for post:
92
+ {state['post']}
93
+ Reply only a number.
 
 
 
 
 
94
  """
95
  score = model.invoke(prompt).strip()
96
  return Command(update={"tone_score": score}, goto="evaluate_clarity")
97
 
98
  def evaluate_clarity(state) -> Command:
 
99
  prompt = f"""
100
+ Rate clarity 1-10 for post:
101
+ {state['post']}
102
+ Reply only a number.
 
 
 
 
 
103
  """
104
  score = model.invoke(prompt).strip()
105
  return Command(update={"clarity_score": score}, goto="revise_if_needed")
106
 
107
  def revise_if_needed(state) -> Command:
 
108
  try:
109
+ scores = [int(state.get("engagement_score", "0")),
110
+ int(state.get("tone_score", "0")),
111
+ int(state.get("clarity_score", "0"))]
112
  except ValueError:
113
+ return Command(goto="get_image")
114
+
115
+ avg = sum(scores) / 3
116
+ if avg < 7:
117
+ prompt = f"Improve post clarity, engagement, and tone:\n{state['post']}\nScores: {scores}"
118
+ revised = model.invoke(prompt)
119
+ return Command(update={"post": revised}, goto="get_image")
120
+ else:
121
+ return Command(goto="get_image")
 
 
 
 
 
 
 
 
 
122
 
123
  def fetch_image(state) -> Command:
124
+ api_key = os.getenv("PEXELS_API_KEY")
125
+ if not api_key:
126
+ print("Pexels API key missing.")
127
+ return Command(update={"image_url": []}, goto=END)
128
+
129
+ prompt = f"Generate a descriptive, professional image search query for: {state['topic']}"
130
+ search_query = model.invoke(prompt).strip()
131
+
 
 
132
  url = "https://api.pexels.com/v1/search"
133
+ params = {"query": search_query, "per_page": 5}
134
+ headers = {"Authorization": api_key}
135
+
 
 
 
 
 
136
  for attempt in range(3):
137
  try:
138
+ r = requests.get(url, headers=headers, params=params)
139
+ r.raise_for_status()
140
+ data = r.json()
141
+ urls = [p["url"] for p in data.get("photos", [])]
142
  return Command(update={"image_url": urls}, goto=END)
143
+ except Exception as e:
144
+ print(f"Pexels request failed (try {attempt+1}): {e}")
145
  if attempt < 2:
146
+ sleep(2 ** attempt)
147
  continue
148
  return Command(update={"image_url": []}, goto=END)
149
 
150
+ class State(TypedDict, total=False):
151
  topic: str
152
  platform: str
153
  tips: str
154
  search_results: List[dict]
155
  post: str
156
+ engagement_score: str
157
+ tone_score: str
158
+ clarity_score: str
159
+ image_url: List[str]
160
 
161
  workflow = StateGraph(State)
162
+
163
  workflow.add_node("get_tips", get_platform_tips)
164
  workflow.add_node("web_search", web_search)
165
  workflow.add_node("generate_post", generate_social_media_post)
 
178
  workflow.add_edge("evaluate_clarity", "revise_if_needed")
179
  workflow.add_edge("revise_if_needed", "get_image")
180
 
181
+ graph = workflow.compile()