Repository navigation
Expand file tree
/
Copy pathbot.py
More file actions
257 lines (210 loc) · 10.8 KB
/
Copy pathbot.py
File metadata and controls
257 lines (210 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
import os
from datetime import datetime
from collections import defaultdict
from agents import Agent, Runner
from arcadepy import AsyncArcade
from agents_arcade import get_arcade_tools
from constants import BOT_CONFIG, ERROR_MESSAGES
class FlyMeBot:
def __init__(self, slack_client=None):
self.arcade_client = AsyncArcade(api_key=os.getenv("ARCADE_API_KEY"))
self.agent = None
self.user_id = "flyme_slack_user"
self.slack_client = slack_client
# Add conversation memory
self.conversation_history = defaultdict(list)
async def get_user_location(self, user_id):
"""Get user's timezone from Slack profile"""
if not self.slack_client:
return None
try:
user_info = await self.slack_client.users_info(user=user_id)
user_data = user_info.get("user", {})
tz = user_data.get("tz", "")
tz_label = user_data.get("tz_label", "")
# Returns the timezone for agent interpretation
if tz_label:
return f"{tz_label} timezone"
elif tz:
return f"{tz} timezone"
return None
except Exception as e:
return None
async def initialize(self):
"""Initialize the flight search agent"""
try:
print("\n" + "="*50)
print("Initializing service...")
print(f"ARCADE_API_KEY present: {bool(os.getenv('ARCADE_API_KEY'))}")
print(f"OPENAI_API_KEY present: {bool(os.getenv('OPENAI_API_KEY'))}")
print(f"SLACK_BOT_TOKEN present: {bool(os.getenv('SLACK_BOT_TOKEN'))}")
print(f"SLACK_APP_TOKEN present: {bool(os.getenv('SLACK_APP_TOKEN'))}")
tools = await get_arcade_tools(
self.arcade_client,
toolkits=["search"],
user_id=self.user_id
)
print(f"Loaded {len(tools)} tools")
for tool in tools:
print(f"Tool available: {tool.name if hasattr(tool, 'name') else str(tool)}")
# Load agent instructions
with open('instructions.md', 'r') as f:
instructions_template = f.read()
instructions = instructions_template.replace(
'{current_date}',
datetime.now().strftime("%Y-%m-%d")
)
print(f"Instructions loaded, length: {len(instructions)}")
self.agent = Agent(
name="FlyMe Assistant",
model=BOT_CONFIG["model"],
instructions=instructions,
tools=tools
)
print("Agent initialized successfully")
print("="*50)
return True
except Exception as e:
print(f"[ERROR] Detected in initialize: {str(e)}")
print("\n" + "="*50)
import traceback
traceback.print_exc()
return False
async def search_flights(self, query, user_id, user_location=None):
"""Search for flights based on user query with conversation memory"""
try:
print(f"[Agent] Starting search_flights for user {user_id}")
print(f"[Agent] Query: {query}")
print(f"[Agent] User location: {user_location}")
# Check if agent is initialized
if not self.agent:
print("[ERROR] Agent not initialized!")
return "Sorry, the bot is not properly initialized. Please try again later."
# Update conversation history
self.conversation_history[user_id].append({
"role": "user",
"content": query,
"timestamp": datetime.now()
})
# Build conversation context for the agent
conversation_summary = ""
if len(self.conversation_history[user_id]) > 1:
recent_messages = self.conversation_history[user_id][-5:] # Last 5 messages
conversation_summary = "Previous conversation:\n"
for msg in recent_messages[:-1]: # Exclude current message
role = msg['role'].capitalize()
conversation_summary += f"{role}: {msg['content']}\n"
# Add user location if available
location_context = ""
if user_location:
location_context = f"User's timezone: {user_location}. Use this to infer their likely departure location if they don't specify one. "
# Combine all context
full_context = f"""{location_context}{conversation_summary}
Current request: {query}
IMPORTANT REMINDERS:
1. When responding to gather information, use natural conversational language, NOT bullet points or structured lists.
2. Extract all relevant information from the conversation history to understand what the user needs.
3. If you have enough information to search for flights, use the Search tools immediately.
4. If you need more information, ask for it conversationally.
5. If the user says they're flexible with dates, DO NOT ask for specific dates. Instead, search across multiple dates in their timeframe.
6. If you see timezone information, use it to intelligently guess the user's location but ask for confirmation if needed."""
print(f"[AGENT] Sending to agent:\n{full_context}")
try:
result = await Runner.run(
starting_agent=self.agent,
input=full_context,
context={"user_id": self.user_id},
max_turns=BOT_CONFIG["max_turns"]
)
print(f"[AGENT] Response: {result.final_output[:200]}...")
# Store the response
self.conversation_history[user_id].append({
"role": "assistant",
"content": result.final_output,
"timestamp": datetime.now()
})
return result.final_output
except Exception as agent_error:
print(f"[ERROR] Detected in Runner.run: {str(agent_error)}")
import traceback
traceback.print_exc()
raise
except Exception as e:
print(f"[ERROR] Detected in search_flights: {str(e)}")
import traceback
traceback.print_exc()
error_str = str(e)
if "rate_limit_exceeded" in error_str or "429" in error_str:
return ERROR_MESSAGES["rate_limit"]
else:
return ERROR_MESSAGES["generic"]
async def search_hotels(self, query, user_id, user_location=None):
"""Search for hotels based on user query"""
try:
print(f"[Agent] Starting search_hotels for user {user_id}")
print(f"[Agent] Query: {query}")
print(f"[Agent] User location: {user_location}")
# Check if agent is initialized
if not self.agent:
print("[ERROR] Agent not initialized!")
return "Sorry, the bot is not properly initialized. Please try again later."
# Update conversation history
self.conversation_history[user_id].append({
"role": "user",
"content": query,
"timestamp": datetime.now()
})
# Build conversation context for the agent
conversation_summary = ""
if len(self.conversation_history[user_id]) > 1:
recent_messages = self.conversation_history[user_id][-5:] # Last 5 messages
conversation_summary = "Previous conversation:\n"
for msg in recent_messages[:-1]: # Exclude current message
role = msg['role'].capitalize()
conversation_summary += f"{role}: {msg['content']}\n"
# Add user location if available
location_context = ""
if user_location:
location_context = f"User's timezone: {user_location}. Use this to infer their likely search location if they don't specify one. "
# Combine all context for hotel search
full_context = f"""{location_context}{conversation_summary}
Current hotel search request: {query}
IMPORTANT REMINDERS FOR HOTEL SEARCH:
1. When responding to gather information, use natural conversational language, NOT bullet points or structured lists.
2. Extract all relevant information from the conversation history to understand what the user needs.
3. If you have enough information to search for hotels, use the Search_GoogleHotels tool immediately.
4. If you need more information, ask for it conversationally.
5. Essential information needed: destination/city, check-in date, check-out date, number of guests
6. Helpful additional info: budget range, hotel preferences (luxury, budget, business, etc.)
7. If the user says they're flexible with dates, search across multiple date ranges in their timeframe.
8. CRITICAL: Use Slack link format <URL|Hotel Name> NOT markdown [Hotel Name](URL) - markdown breaks in Slack!"""
print(f"[AGENT] Sending hotel search to agent:\n{full_context}")
try:
result = await Runner.run(
starting_agent=self.agent,
input=full_context,
context={"user_id": self.user_id},
max_turns=BOT_CONFIG["max_turns"]
)
print(f"[AGENT] Hotel Response: {result.final_output[:200]}...")
# Store the response
self.conversation_history[user_id].append({
"role": "assistant",
"content": result.final_output,
"timestamp": datetime.now()
})
return result.final_output
except Exception as agent_error:
print(f"[ERROR] Detected in hotel Runner.run: {str(agent_error)}")
import traceback
traceback.print_exc()
raise
except Exception as e:
print(f"[ERROR] Detected in search_hotels: {str(e)}")
import traceback
traceback.print_exc()
error_str = str(e)
if "rate_limit_exceeded" in error_str or "429" in error_str:
return ERROR_MESSAGES["rate_limit"]
else:
return ERROR_MESSAGES.get("hotel_generic", "I encountered an error while searching for hotels. Please try again.")