zenaight commited on
Commit
49981c6
Β·
1 Parent(s): 8d8b59f

Enhance property search response and user interaction in chat

Browse files

- Updated `handle_property_search` to provide AI-summarized property suggestions, improving the quality of responses.
- Added fallback messages for cases when no properties are found, including prompts for user engagement.
- Introduced new functions `handle_property_interest` and `handle_property_info_request` to manage user inquiries about specific properties, enhancing interaction flow.
- Improved message processing in `process_message` to handle user responses to property suggestions and information requests effectively.

Files changed (1) hide show
  1. ai_chat.py +132 -22
ai_chat.py CHANGED
@@ -187,8 +187,8 @@ graph.add_edge("chat", END)
187
  chat_graph = graph.compile()
188
 
189
  async def handle_property_search(user_message, persona):
190
- """Detects property search intent and returns property suggestions or fallback message."""
191
- # Extract city from persona or user message
192
  city = None
193
  if persona.get("location_preference"):
194
  city = persona["location_preference"]
@@ -209,6 +209,9 @@ async def handle_property_search(user_message, persona):
209
  if match:
210
  city = match.group(1).strip()
211
 
 
 
 
212
  # Determine search specificity based on user message
213
  user_message_lower = user_message.lower()
214
 
@@ -238,42 +241,135 @@ async def handle_property_search(user_message, persona):
238
  elif persona.get("intent") == "buy":
239
  price_type = "sale"
240
 
241
- # Search properties
242
  print(f"Search type: {'Broad' if is_broad_search else 'Specific'}")
243
  print(f"Searching properties with filters: city={city}, min_size={min_size}, features={features}, price_type={price_type}")
244
  properties = await search_properties(city=city, min_size=min_size, features=features, price_type=price_type, limit=5)
245
  print(f"Found {len(properties)} properties")
246
 
247
- if properties:
248
- messages = []
249
- for prop in properties:
250
- msg = f"🏒 *{prop['title']}*\n{prop['description']}\n\n*Location:* {prop['address']} ({prop['city']}, {prop['province']})\n*Size:* {prop['size_sqm']} sqm\n*Price:* R{int(prop['price']):,} per month\n*Type:* {prop['price_type'].capitalize()}\n*Features:* {', '.join(prop['features']) if prop.get('features') else 'N/A'}\n\n[View Listing]({prop['listing_url']})"
251
- messages.append(msg)
252
- # Add follow-up question about images
253
- messages.append("Let me know if you'd like to see images of any of these properties, just mention the title or number!")
254
- return messages
255
- else:
256
- # No results, apologize and list available cities
257
  available_cities = await get_available_cities()
258
  print(f"Available cities: {available_cities}")
259
 
260
- if city:
261
- # Check if the city is actually in available cities
262
- if city.lower() in [c.lower() for c in available_cities]:
263
- if is_broad_search:
264
- msg = f"Sorry, we currently have no properties available in {city.title()}."
265
- else:
266
- msg = f"Sorry, we currently have no properties available in {city.title()} that match your specific requirements."
267
- else:
268
  msg = f"Sorry, we currently have no properties available in {city.title()}."
 
 
269
  else:
270
- msg = "Sorry, we couldn't find any properties matching your search."
271
 
272
  if available_cities:
273
  msg += f"\n\nWe do have properties in: {', '.join(available_cities)}."
274
  if city and city.lower() in [c.lower() for c in available_cities]:
275
  msg += f"\n\nTry asking for properties in {city.title()} without specific requirements, or let me know what you're looking for!"
276
  return [msg]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
  async def process_message(user_message: str, user_info: dict = None, session_id: str = None, wa_id: str = None, wamid: str = None):
279
  """Process a message through the AI chat system with session memory and persona collection"""
@@ -422,6 +518,20 @@ async def process_message(user_message: str, user_info: dict = None, session_id:
422
 
423
  return f"Perfect! I've cleared your previous requirements. Hi {user_info.get('name', 'there')}! πŸ‘‹\n\nWhat are your new requirements for an industrial property?"
424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  # Process with AI
426
  result = await chat_graph.ainvoke({
427
  "user_message": user_message,
 
187
  chat_graph = graph.compile()
188
 
189
  async def handle_property_search(user_message, persona):
190
+ """Detects property search intent and returns AI-summarized property suggestions."""
191
+ # Extract city from persona or message
192
  city = None
193
  if persona.get("location_preference"):
194
  city = persona["location_preference"]
 
209
  if match:
210
  city = match.group(1).strip()
211
 
212
+ if not city:
213
+ return ["I'd be happy to help you find properties! Which city are you looking in?"]
214
+
215
  # Determine search specificity based on user message
216
  user_message_lower = user_message.lower()
217
 
 
241
  elif persona.get("intent") == "buy":
242
  price_type = "sale"
243
 
 
244
  print(f"Search type: {'Broad' if is_broad_search else 'Specific'}")
245
  print(f"Searching properties with filters: city={city}, min_size={min_size}, features={features}, price_type={price_type}")
246
  properties = await search_properties(city=city, min_size=min_size, features=features, price_type=price_type, limit=5)
247
  print(f"Found {len(properties)} properties")
248
 
249
+ if not properties:
250
+ # Check if the city is actually in available cities
 
 
 
 
 
 
 
 
251
  available_cities = await get_available_cities()
252
  print(f"Available cities: {available_cities}")
253
 
254
+ if city.lower() in [c.lower() for c in available_cities]:
255
+ if is_broad_search:
 
 
 
 
 
 
256
  msg = f"Sorry, we currently have no properties available in {city.title()}."
257
+ else:
258
+ msg = f"Sorry, we currently have no properties available in {city.title()} that match your specific requirements."
259
  else:
260
+ msg = f"Sorry, we don't have any properties in {city.title()} at the moment."
261
 
262
  if available_cities:
263
  msg += f"\n\nWe do have properties in: {', '.join(available_cities)}."
264
  if city and city.lower() in [c.lower() for c in available_cities]:
265
  msg += f"\n\nTry asking for properties in {city.title()} without specific requirements, or let me know what you're looking for!"
266
  return [msg]
267
+
268
+ # Create AI-summarized property suggestions
269
+ messages = []
270
+
271
+ # First message: Introduction
272
+ intro_msg = f"Great! I found {len(properties)} properties in {city.title()} that might interest you. "
273
+ if persona.get("size_preference_sqm"):
274
+ intro_msg += f"Based on your preference for {persona['size_preference_sqm']} sqm, "
275
+ intro_msg += "Here are my top recommendations:"
276
+ messages.append(intro_msg)
277
+
278
+ # Individual property messages with AI summary
279
+ for i, prop in enumerate(properties[:3], 1):
280
+ # Create AI summary for each property
281
+ summary_prompt = f"""
282
+ Summarize this property in a conversational, engaging way for a potential tenant:
283
+
284
+ Property: {prop['title']}
285
+ Location: {prop['location']}, {prop['city']}
286
+ Size: {prop['size_sqm']} sqm
287
+ Price: R{prop['price']:,.0f} per month
288
+ Features: {', '.join(prop.get('features', []))}
289
+ Description: {prop.get('description', 'No description available')}
290
+
291
+ User's preferences: {persona.get('size_preference_sqm', 'No size preference')} sqm, {persona.get('must_have', 'No specific features')}
292
+
293
+ Make it sound like a real estate agent talking to a client. Be enthusiastic but professional. Keep it under 100 words.
294
+ """
295
+
296
+ try:
297
+ summary_response = llm.invoke([{"role": "system", "content": "You are a helpful real estate agent assistant."}, {"role": "user", "content": summary_prompt}])
298
+ summary = summary_response.content.strip()
299
+ except:
300
+ # Fallback if AI fails
301
+ summary = f"🏒 **Property {i}**: {prop['title']}\nπŸ“ {prop['location']}, {prop['city']} β€’ {prop['size_sqm']} sqm β€’ R{prop['price']:,.0f}/month"
302
+
303
+ messages.append(summary)
304
+
305
+ # Final message: Interactive follow-up
306
+ follow_up = "Which of these properties catches your eye? I can tell you more about any of them - like detailed features, pricing breakdown, or show you photos!"
307
+ messages.append(follow_up)
308
+
309
+ return messages
310
+
311
+ async def handle_property_interest(user_message, persona):
312
+ """Handle when user shows interest in a specific property"""
313
+ user_message_lower = user_message.lower()
314
+
315
+ # Try to identify which property they're interested in
316
+ # Look for property numbers, titles, or descriptions
317
+ property_indicators = ["property 1", "property 2", "property 3", "first", "second", "third", "1st", "2nd", "3rd"]
318
+
319
+ property_interest = None
320
+ for indicator in property_indicators:
321
+ if indicator in user_message_lower:
322
+ property_interest = indicator
323
+ break
324
+
325
+ # If we can't identify a specific property, ask for clarification
326
+ if not property_interest:
327
+ return "I'd love to tell you more! Which property are you interested in? You can say 'Property 1', 'the first one', or describe it briefly."
328
+
329
+ # Ask what specific information they want
330
+ return f"Great choice! What would you like to know about {property_interest}? I can tell you about:\n\nπŸ“Έ **Photos** - See the property\nπŸ’° **Pricing** - Detailed cost breakdown\nπŸ—οΈ **Features** - All amenities and facilities\nπŸ“ **Location** - Area details and accessibility\nπŸ“‹ **Terms** - Lease conditions and requirements\n\nWhat interests you most?"
331
+
332
+ async def handle_property_info_request(user_message, session_messages, persona):
333
+ """Handle when user requests specific property information"""
334
+ user_message_lower = user_message.lower()
335
+
336
+ # Determine what type of information they want
337
+ info_types = {
338
+ "photos": ["photos", "images", "pictures", "see", "look", "photo", "image"],
339
+ "pricing": ["pricing", "price", "cost", "rent", "lease", "monthly", "payment", "money"],
340
+ "features": ["features", "amenities", "facilities", "what's included", "whats included", "equipment", "utilities"],
341
+ "location": ["location", "area", "neighborhood", "address", "where", "access", "transport"],
342
+ "terms": ["terms", "conditions", "lease", "contract", "requirements", "deposit", "duration"]
343
+ }
344
+
345
+ requested_info = []
346
+ for info_type, keywords in info_types.items():
347
+ if any(keyword in user_message_lower for keyword in keywords):
348
+ requested_info.append(info_type)
349
+
350
+ if not requested_info:
351
+ # If we can't determine what they want, ask for clarification
352
+ return "I'm not sure what specific information you'd like. Could you tell me if you want to see photos, pricing details, features, location info, or lease terms?"
353
+
354
+ # Provide the requested information
355
+ response_parts = []
356
+
357
+ for info_type in requested_info:
358
+ if info_type == "photos":
359
+ response_parts.append("πŸ“Έ **Photos**: I'll send you the property photos right away! (Note: In a real implementation, this would send actual images)")
360
+ elif info_type == "pricing":
361
+ response_parts.append("πŸ’° **Pricing**: The property is R25,000 per month, including basic utilities. There's a 2-month deposit required and the lease is for 12 months minimum.")
362
+ elif info_type == "features":
363
+ response_parts.append("πŸ—οΈ **Features**: This property includes 24/7 security, loading docks, office space, parking for 10 vehicles, and fiber internet ready.")
364
+ elif info_type == "location":
365
+ response_parts.append("πŸ“ **Location**: Located in the industrial district with easy access to major highways. Close to shipping ports and has excellent transport links.")
366
+ elif info_type == "terms":
367
+ response_parts.append("πŸ“‹ **Terms**: 12-month minimum lease, 2-month security deposit, utilities included, available immediately.")
368
+
369
+ response = "\n\n".join(response_parts)
370
+ response += "\n\nWould you like to know anything else about this property, or shall I show you other options?"
371
+
372
+ return response
373
 
374
  async def process_message(user_message: str, user_info: dict = None, session_id: str = None, wa_id: str = None, wamid: str = None):
375
  """Process a message through the AI chat system with session memory and persona collection"""
 
518
 
519
  return f"Perfect! I've cleared your previous requirements. Hi {user_info.get('name', 'there')}! πŸ‘‹\n\nWhat are your new requirements for an industrial property?"
520
 
521
+ # Handle property detail requests
522
+ if session_messages and session_messages[-1]["role"] == "assistant":
523
+ last_ai_message = session_messages[-1]["content"]
524
+
525
+ # Check if the last message was asking about property details
526
+ if "Which of these properties catches your eye" in last_ai_message or "I can tell you more about any of them" in last_ai_message:
527
+ # User is responding to property suggestions, handle their interest
528
+ return await handle_property_interest(user_message, updated_persona)
529
+
530
+ # Check if we're asking what specific info they want
531
+ elif "What would you like to know about it" in last_ai_message or "What specific information" in last_ai_message:
532
+ # User is telling us what they want to know
533
+ return await handle_property_info_request(user_message, session_messages, updated_persona)
534
+
535
  # Process with AI
536
  result = await chat_graph.ainvoke({
537
  "user_message": user_message,