Commit
ยท
b1ec794
1
Parent(s):
2e86bf2
add beautifulsoup4 dependency and update output formatting for research mcp
Browse files- mcp_servers/research/provider.py +91 -45
- requirements.txt +1 -0
mcp_servers/research/provider.py
CHANGED
|
@@ -460,31 +460,48 @@ class MessageGeneratorTool(BaseTool):
|
|
| 460 |
def _generate_template_message(
|
| 461 |
self, occasion: str, recipient: str, tone: str, details: str
|
| 462 |
) -> str:
|
| 463 |
-
"""Generate message using templates."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
templates = {
|
| 465 |
"birthday": [
|
| 466 |
-
f"๐ Happy Birthday
|
| 467 |
-
f"๐ Happy Birthday, {recipient}!\n\nAnother year older, another year wiser, and definitely another year more awesome! Hope your special day is as wonderful as you are.\n\nEnjoy your day! ๐",
|
| 468 |
-
],
|
| 469 |
-
"party invitation": [
|
| 470 |
-
f"๐ You're Invited!\n\nHey {recipient}!\n\nWe're throwing a celebration and it wouldn't be the same without you! Join us for an unforgettable time.\n\n{details if details else 'Details to follow!'}\n\nHope to see you there! ๐ฅณ",
|
| 471 |
],
|
| 472 |
"thank you": [
|
| 473 |
f"Dear {recipient},\n\nThank you so much for being part of our celebration! Your presence made the day even more special.\n\nWith gratitude,\nโค๏ธ",
|
| 474 |
],
|
| 475 |
}
|
| 476 |
|
| 477 |
-
occasion_lower = occasion.lower()
|
| 478 |
for key, msgs in templates.items():
|
| 479 |
if key in occasion_lower:
|
| 480 |
return msgs[0]
|
| 481 |
|
| 482 |
-
# Default template
|
| 483 |
return (
|
|
|
|
| 484 |
f"Dear {recipient},\n\n"
|
| 485 |
-
f"
|
| 486 |
-
f"{'
|
| 487 |
-
f"
|
|
|
|
| 488 |
)
|
| 489 |
|
| 490 |
def get_schema(self) -> Dict[str, Any]:
|
|
@@ -746,16 +763,9 @@ Maximum 3 tool calls per response."""
|
|
| 746 |
"params": {"query": f"how to plan {state.idea} party ideas activities guide"}
|
| 747 |
})
|
| 748 |
|
| 749 |
-
# Search for specific supplies based on highlights
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
# Extract shopping-related terms from highlights
|
| 753 |
-
for highlight in idea_highlights[:2]:
|
| 754 |
-
highlight_lower = highlight.lower()
|
| 755 |
-
if any(word in highlight_lower for word in ["station", "kit", "supplies", "decor", "activity"]):
|
| 756 |
-
amazon_terms.append(highlight[:30])
|
| 757 |
-
|
| 758 |
-
amazon_query = " ".join(amazon_terms[:3]) + " party supplies"
|
| 759 |
calls.append({
|
| 760 |
"tool": "amazon_search",
|
| 761 |
"params": {"query": amazon_query}
|
|
@@ -763,20 +773,20 @@ Maximum 3 tool calls per response."""
|
|
| 763 |
|
| 764 |
# Generate invitation message with more context
|
| 765 |
invite_details = []
|
| 766 |
-
if state.context.get("location"):
|
| 767 |
-
invite_details.append(f"Location: {state.context['location']}")
|
| 768 |
if state.context.get("event_date"):
|
| 769 |
invite_details.append(f"Date: {state.context['event_date']}")
|
| 770 |
-
if state.
|
| 771 |
-
invite_details.append(f"
|
|
|
|
|
|
|
| 772 |
|
| 773 |
calls.append({
|
| 774 |
"tool": "generate_message",
|
| 775 |
"params": {
|
| 776 |
-
"occasion": f"
|
| 777 |
-
"recipient": "
|
| 778 |
"tone": "fun and exciting",
|
| 779 |
-
"details": "
|
| 780 |
}
|
| 781 |
})
|
| 782 |
|
|
@@ -1136,12 +1146,8 @@ Be specific, practical, and enthusiastic!"""
|
|
| 1136 |
# Extract overview from synthesized text
|
| 1137 |
overview = final_text[:800] if len(final_text) > 800 else final_text
|
| 1138 |
|
| 1139 |
-
# Build
|
| 1140 |
-
steps =
|
| 1141 |
-
if state.idea_details and state.idea_details.get("next_steps"):
|
| 1142 |
-
steps = state.idea_details["next_steps"]
|
| 1143 |
-
else:
|
| 1144 |
-
steps = self._extract_steps_from_text(final_text)
|
| 1145 |
|
| 1146 |
# Build references (combined sources and links)
|
| 1147 |
references = [
|
|
@@ -1218,6 +1224,56 @@ Be specific, practical, and enthusiastic!"""
|
|
| 1218 |
|
| 1219 |
return "Completed"
|
| 1220 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1221 |
def _extract_steps_from_text(self, text: str) -> List[str]:
|
| 1222 |
"""Extract actionable steps from synthesized text."""
|
| 1223 |
steps = []
|
|
@@ -1232,17 +1288,7 @@ Be specific, practical, and enthusiastic!"""
|
|
| 1232 |
if clean_line and len(clean_line) > 10:
|
| 1233 |
steps.append(clean_line[:100])
|
| 1234 |
|
| 1235 |
-
|
| 1236 |
-
if not steps:
|
| 1237 |
-
steps = [
|
| 1238 |
-
"Review the plan and customize for your needs",
|
| 1239 |
-
"Purchase necessary supplies and decorations",
|
| 1240 |
-
"Send invitations to guests",
|
| 1241 |
-
"Prepare the venue and activities",
|
| 1242 |
-
"Enjoy the celebration!"
|
| 1243 |
-
]
|
| 1244 |
-
|
| 1245 |
-
return steps[:8] # Limit to 8 steps
|
| 1246 |
|
| 1247 |
def _get_web_sources(self, state: AgentState) -> List[Dict[str, str]]:
|
| 1248 |
"""Extract web sources from tool results."""
|
|
|
|
| 460 |
def _generate_template_message(
|
| 461 |
self, occasion: str, recipient: str, tone: str, details: str
|
| 462 |
) -> str:
|
| 463 |
+
"""Generate message using templates - always creates INVITATIONS for parties."""
|
| 464 |
+
occasion_lower = occasion.lower()
|
| 465 |
+
|
| 466 |
+
# For party invitations, always use invitation template
|
| 467 |
+
if "invitation" in occasion_lower or "party" in occasion_lower:
|
| 468 |
+
# Extract the party theme from occasion
|
| 469 |
+
theme = occasion.replace("invitation", "").replace("party", "").replace("for", "").strip()
|
| 470 |
+
if not theme:
|
| 471 |
+
theme = "our special celebration"
|
| 472 |
+
|
| 473 |
+
return (
|
| 474 |
+
f"๐ You're Invited to {theme}! ๐\n\n"
|
| 475 |
+
f"Dear {recipient},\n\n"
|
| 476 |
+
f"Get ready for an amazing celebration! We're hosting {theme} and it wouldn't be "
|
| 477 |
+
f"the same without you.\n\n"
|
| 478 |
+
f"๐
{details if details else 'Date & Time: TBD'}\n\n"
|
| 479 |
+
f"Please RSVP by [date] so we can plan accordingly.\n\n"
|
| 480 |
+
f"Can't wait to celebrate with you!\n"
|
| 481 |
+
f"๐ฅณ See you there!"
|
| 482 |
+
)
|
| 483 |
+
|
| 484 |
templates = {
|
| 485 |
"birthday": [
|
| 486 |
+
f"๐ Happy Birthday Celebration! ๐\n\nDear {recipient},\n\nYou're invited to a special birthday celebration!\n\n{details if details else 'Details coming soon!'}\n\nJoin us for fun, food, and festivities!\n\nRSVP: [contact]\n๐ Hope to see you there!",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
],
|
| 488 |
"thank you": [
|
| 489 |
f"Dear {recipient},\n\nThank you so much for being part of our celebration! Your presence made the day even more special.\n\nWith gratitude,\nโค๏ธ",
|
| 490 |
],
|
| 491 |
}
|
| 492 |
|
|
|
|
| 493 |
for key, msgs in templates.items():
|
| 494 |
if key in occasion_lower:
|
| 495 |
return msgs[0]
|
| 496 |
|
| 497 |
+
# Default invitation template
|
| 498 |
return (
|
| 499 |
+
f"๐ You're Invited!\n\n"
|
| 500 |
f"Dear {recipient},\n\n"
|
| 501 |
+
f"We're hosting a special celebration and would love for you to join us!\n\n"
|
| 502 |
+
f"{'๐
' + details if details else '๐
Details to follow!'}\n\n"
|
| 503 |
+
f"Please let us know if you can make it.\n\n"
|
| 504 |
+
f"Looking forward to celebrating with you!\n๐ฅณ"
|
| 505 |
)
|
| 506 |
|
| 507 |
def get_schema(self) -> Dict[str, Any]:
|
|
|
|
| 763 |
"params": {"query": f"how to plan {state.idea} party ideas activities guide"}
|
| 764 |
})
|
| 765 |
|
| 766 |
+
# Search for specific supplies based on idea title and highlights
|
| 767 |
+
# Use the full idea name for more specific results
|
| 768 |
+
amazon_query = f"{state.idea} party supplies decorations"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 769 |
calls.append({
|
| 770 |
"tool": "amazon_search",
|
| 771 |
"params": {"query": amazon_query}
|
|
|
|
| 773 |
|
| 774 |
# Generate invitation message with more context
|
| 775 |
invite_details = []
|
|
|
|
|
|
|
| 776 |
if state.context.get("event_date"):
|
| 777 |
invite_details.append(f"Date: {state.context['event_date']}")
|
| 778 |
+
if state.context.get("location"):
|
| 779 |
+
invite_details.append(f"Location: {state.context['location']}")
|
| 780 |
+
if state.honoree and state.honoree.get("guest_count"):
|
| 781 |
+
invite_details.append(f"Expected guests: {state.honoree['guest_count']}")
|
| 782 |
|
| 783 |
calls.append({
|
| 784 |
"tool": "generate_message",
|
| 785 |
"params": {
|
| 786 |
+
"occasion": f"party invitation for {state.idea}",
|
| 787 |
+
"recipient": "Friend",
|
| 788 |
"tone": "fun and exciting",
|
| 789 |
+
"details": "\n".join(invite_details) if invite_details else "Details to be announced"
|
| 790 |
}
|
| 791 |
})
|
| 792 |
|
|
|
|
| 1146 |
# Extract overview from synthesized text
|
| 1147 |
overview = final_text[:800] if len(final_text) > 800 else final_text
|
| 1148 |
|
| 1149 |
+
# Build detailed, actionable steps
|
| 1150 |
+
steps = self._build_actionable_steps(state, final_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1151 |
|
| 1152 |
# Build references (combined sources and links)
|
| 1153 |
references = [
|
|
|
|
| 1224 |
|
| 1225 |
return "Completed"
|
| 1226 |
|
| 1227 |
+
def _build_actionable_steps(self, state: AgentState, synthesis_text: str) -> List[str]:
|
| 1228 |
+
"""Build detailed, actionable steps with timing and specific guidance."""
|
| 1229 |
+
idea_name = state.idea
|
| 1230 |
+
|
| 1231 |
+
# Get idea-specific steps if available
|
| 1232 |
+
idea_steps = []
|
| 1233 |
+
if state.idea_details and state.idea_details.get("next_steps"):
|
| 1234 |
+
idea_steps = state.idea_details["next_steps"]
|
| 1235 |
+
|
| 1236 |
+
# Extract any steps from synthesis text
|
| 1237 |
+
extracted_steps = self._extract_steps_from_text(synthesis_text)
|
| 1238 |
+
|
| 1239 |
+
# Build comprehensive action plan - mix generic timeline with specific tasks
|
| 1240 |
+
planning_steps = []
|
| 1241 |
+
|
| 1242 |
+
# Phase 1: Planning (2 weeks before)
|
| 1243 |
+
planning_steps.append(f"๐ **2 weeks before**: Create your guest list and budget for {idea_name}")
|
| 1244 |
+
if idea_steps and len(idea_steps) > 0:
|
| 1245 |
+
planning_steps.append(f"โ๏ธ **2 weeks before**: {idea_steps[0]}")
|
| 1246 |
+
else:
|
| 1247 |
+
planning_steps.append(f"โ๏ธ **2 weeks before**: Design and send themed invitations")
|
| 1248 |
+
|
| 1249 |
+
# Phase 2: Preparation (1 week before)
|
| 1250 |
+
planning_steps.append(f"๐ **1 week before**: Order supplies from the shopping list below")
|
| 1251 |
+
if idea_steps and len(idea_steps) > 1:
|
| 1252 |
+
planning_steps.append(f"๐ฝ๏ธ **1 week before**: {idea_steps[1]}")
|
| 1253 |
+
else:
|
| 1254 |
+
planning_steps.append(f"๐ฝ๏ธ **1 week before**: Plan themed menu and drinks")
|
| 1255 |
+
|
| 1256 |
+
# Phase 3: Final prep (3-4 days before)
|
| 1257 |
+
if idea_steps and len(idea_steps) > 2:
|
| 1258 |
+
planning_steps.append(f"๐ฎ **3-4 days before**: {idea_steps[2]}")
|
| 1259 |
+
else:
|
| 1260 |
+
planning_steps.append(f"๐ฎ **3-4 days before**: Prepare games, activities, and entertainment")
|
| 1261 |
+
|
| 1262 |
+
# Phase 4: Setup and event
|
| 1263 |
+
planning_steps.append(f"๐ **1 day before**: Set up decorations and prepare the venue")
|
| 1264 |
+
planning_steps.append(f"โ
**Day of event**: Final walkthrough, welcome guests, and enjoy!")
|
| 1265 |
+
|
| 1266 |
+
# Add tips from web research
|
| 1267 |
+
if extracted_steps:
|
| 1268 |
+
for step in extracted_steps[:2]:
|
| 1269 |
+
if step and len(step) > 15:
|
| 1270 |
+
# Only add if it's genuinely new info
|
| 1271 |
+
step_lower = step.lower()
|
| 1272 |
+
if not any(kw in step_lower for kw in ["guest list", "invitation", "decoration", "menu"]):
|
| 1273 |
+
planning_steps.append(f"๐ก **Pro tip**: {step[:80]}")
|
| 1274 |
+
|
| 1275 |
+
return planning_steps[:9] # Limit to 9 steps
|
| 1276 |
+
|
| 1277 |
def _extract_steps_from_text(self, text: str) -> List[str]:
|
| 1278 |
"""Extract actionable steps from synthesized text."""
|
| 1279 |
steps = []
|
|
|
|
| 1288 |
if clean_line and len(clean_line) > 10:
|
| 1289 |
steps.append(clean_line[:100])
|
| 1290 |
|
| 1291 |
+
return steps[:5]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1292 |
|
| 1293 |
def _get_web_sources(self, state: AgentState) -> List[Dict[str, str]]:
|
| 1294 |
"""Extract web sources from tool results."""
|
requirements.txt
CHANGED
|
@@ -6,3 +6,4 @@ httpx
|
|
| 6 |
ddgs
|
| 7 |
langgraph>=1.0.0
|
| 8 |
langchain-core>=1.1.0
|
|
|
|
|
|
| 6 |
ddgs
|
| 7 |
langgraph>=1.0.0
|
| 8 |
langchain-core>=1.1.0
|
| 9 |
+
beautifulsoup4
|