zenaight commited on
Commit
a35f267
·
1 Parent(s): 729c393

Implement image handling in AI chat and enhance WhatsApp messaging

Browse files

- Introduced a new `handle_image_request` function to process user requests for property images, allowing the AI to respond with relevant image messages when triggered.
- Updated the `process_message` function to return both the AI response and associated property details, facilitating image requests.
- Enhanced the WhatsApp messaging functionality with a new `send_multiple_messages` function to handle sending both text and image messages, including delays to avoid rate limiting.
- These changes aim to improve user interaction by providing a more comprehensive response to image requests while maintaining clarity in communication.

Files changed (3) hide show
  1. ai_chat.py +57 -5
  2. main.py +20 -5
  3. whatsapp.py +47 -1
ai_chat.py CHANGED
@@ -58,19 +58,18 @@ def chat_with_session_memory(state):
58
  f"- {p.get('title')} in {p.get('location')}, {p.get('city')}: "
59
  f"{p.get('size_sqm')} sqm, {p.get('price')} ({p.get('price_type')})\n"
60
  )
61
- # Include all available data for the LLM to use
62
  if p.get("listing_url"):
63
  system_message += f" URL: {p.get('listing_url')}\n"
64
- if p.get("images"):
65
- system_message += f" Images: {', '.join(p.get('images', [])[:3])}\n"
66
  if p.get("features"):
67
  system_message += f" Features: {', '.join(p.get('features', [])[:5])}\n"
68
  if p.get("floorplan_pdf"):
69
  system_message += f" Floorplan: {p.get('floorplan_pdf')}\n"
70
  if p.get("video_url"):
71
  system_message += f" Video: {p.get('video_url')}\n"
 
72
 
73
- system_message += "\n\nIMPORTANT: You may only reference listings passed in state['properties']. If the user requests more detail, respond with whatever is in that listing dict (URL, images, features, etc.). If information is not available, respond with 'For this listing, I don't have [specific detail] available right now'."
74
 
75
  # Build messages array with history
76
  messages = [{"role": "system", "content": system_message}]
@@ -425,4 +424,57 @@ async def process_message(user_message: str, user_info: dict = None, session_id:
425
  await save_message(session_id, wa_id, wamid, "user", user_message)
426
  await save_message(session_id, wa_id, f"{wamid}_ai", "assistant", result["response"])
427
 
428
- return result["response"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  f"- {p.get('title')} in {p.get('location')}, {p.get('city')}: "
59
  f"{p.get('size_sqm')} sqm, {p.get('price')} ({p.get('price_type')})\n"
60
  )
61
+ # Include all available data for the LLM to use (but not image URLs in listings)
62
  if p.get("listing_url"):
63
  system_message += f" URL: {p.get('listing_url')}\n"
 
 
64
  if p.get("features"):
65
  system_message += f" Features: {', '.join(p.get('features', [])[:5])}\n"
66
  if p.get("floorplan_pdf"):
67
  system_message += f" Floorplan: {p.get('floorplan_pdf')}\n"
68
  if p.get("video_url"):
69
  system_message += f" Video: {p.get('video_url')}\n"
70
+ # Note: Images are available but not shown in listings - users must ask for them
71
 
72
+ system_message += "\n\nIMPORTANT: You may only reference listings passed in state['properties']. If the user requests more detail, respond with whatever is in that listing dict (URL, images, features, etc.). If information is not available, respond with 'For this listing, I don't have [specific detail] available right now'. When users ask for images, photos, or pictures, let them know that images are available and will be sent separately."
73
 
74
  # Build messages array with history
75
  messages = [{"role": "system", "content": system_message}]
 
424
  await save_message(session_id, wa_id, wamid, "user", user_message)
425
  await save_message(session_id, wa_id, f"{wamid}_ai", "assistant", result["response"])
426
 
427
+ return {
428
+ "response": result["response"],
429
+ "properties": result.get("properties", [])
430
+ }
431
+
432
+ async def handle_image_request(state):
433
+ """
434
+ Handle requests for property images and return image messages to send.
435
+ """
436
+ user_message = state["user_message"].lower()
437
+ props = state.get("properties", [])
438
+
439
+ # Check if user is asking for images (more specific triggers)
440
+ image_triggers = [
441
+ "show me the images", "send me the images", "show me photos", "send me photos",
442
+ "show me pictures", "send me pictures", "i want to see", "can i see",
443
+ "image", "images", "photo", "photos", "picture", "pictures"
444
+ ]
445
+
446
+ # Check if any image trigger is in the message
447
+ is_image_request = any(trigger in user_message for trigger in image_triggers)
448
+
449
+ # Also check if user is asking about a specific property's images
450
+ if not is_image_request and props:
451
+ property_title = props[0].get("title", "").lower()
452
+ if property_title and any(word in user_message for word in property_title.split()):
453
+ is_image_request = any(trigger in user_message for trigger in ["image", "photo", "picture"])
454
+
455
+ if not is_image_request or not props:
456
+ return None
457
+
458
+ # Get the first property (assuming user is asking about the most recent listing)
459
+ property_data = props[0]
460
+ images = property_data.get("images", [])
461
+
462
+ if not images:
463
+ return ["Sorry, I don't have any images available for this listing."]
464
+
465
+ # Prepare image messages
466
+ image_messages = []
467
+ property_title = property_data.get("title", "This property")
468
+
469
+ # Add a text message first to introduce the images
470
+ image_messages.append(f"Here are the images for {property_title}:")
471
+
472
+ for i, image_url in enumerate(images[:5]): # Limit to 5 images
473
+ caption = f"{property_title} - Image {i+1}" if i > 0 else f"{property_title}"
474
+ image_messages.append({
475
+ "type": "image",
476
+ "url": image_url,
477
+ "caption": caption
478
+ })
479
+
480
+ return image_messages
main.py CHANGED
@@ -4,8 +4,8 @@ from fastapi.responses import JSONResponse
4
  # Import our modular components
5
  from config import supabase
6
  from database import get_or_create_user, update_user_activity, get_or_create_active_session, get_user_persona, get_or_create_user_intent
7
- from whatsapp import send_whatsapp_message
8
- from ai_chat import process_message
9
  from api_routes import router
10
 
11
  # --- FastAPI App Setup ---
@@ -53,7 +53,7 @@ async def receive_message(req: Request):
53
  intent = await get_or_create_user_intent(session["id"], wa_id)
54
 
55
  # Process with AI including session memory
56
- ai_response = await process_message(
57
  user_message=user_message,
58
  user_info=user_info,
59
  session_id=session["id"],
@@ -63,8 +63,23 @@ async def receive_message(req: Request):
63
  intent=intent
64
  )
65
 
66
- # Send response back to WhatsApp
67
- await send_whatsapp_message(wa_id, ai_response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  return JSONResponse({
70
  "status": "replied",
 
4
  # Import our modular components
5
  from config import supabase
6
  from database import get_or_create_user, update_user_activity, get_or_create_active_session, get_user_persona, get_or_create_user_intent
7
+ from whatsapp import send_whatsapp_message, send_multiple_messages
8
+ from ai_chat import process_message, handle_image_request
9
  from api_routes import router
10
 
11
  # --- FastAPI App Setup ---
 
53
  intent = await get_or_create_user_intent(session["id"], wa_id)
54
 
55
  # Process with AI including session memory
56
+ ai_result = await process_message(
57
  user_message=user_message,
58
  user_info=user_info,
59
  session_id=session["id"],
 
63
  intent=intent
64
  )
65
 
66
+ ai_response = ai_result["response"]
67
+ properties = ai_result.get("properties", [])
68
+
69
+ # Check if this is an image request
70
+ state = {
71
+ "user_message": user_message,
72
+ "properties": properties
73
+ }
74
+
75
+ image_messages = await handle_image_request(state)
76
+
77
+ if image_messages:
78
+ # Send images
79
+ await send_multiple_messages(wa_id, image_messages)
80
+ else:
81
+ # Send regular text response
82
+ await send_whatsapp_message(wa_id, ai_response)
83
 
84
  return JSONResponse({
85
  "status": "replied",
whatsapp.py CHANGED
@@ -1,4 +1,5 @@
1
  import httpx
 
2
  from config import PHONE_NUMBER_ID, WHATSAPP_API_TOKEN
3
 
4
  async def send_whatsapp_message(wa_id: str, message: str):
@@ -21,4 +22,49 @@ async def send_whatsapp_message(wa_id: str, message: str):
21
  return resp.status_code == 200
22
  except Exception as e:
23
  print(f"Failed to send message: {e}")
24
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import httpx
2
+ import asyncio
3
  from config import PHONE_NUMBER_ID, WHATSAPP_API_TOKEN
4
 
5
  async def send_whatsapp_message(wa_id: str, message: str):
 
22
  return resp.status_code == 200
23
  except Exception as e:
24
  print(f"Failed to send message: {e}")
25
+ return False
26
+
27
+ async def send_whatsapp_image(wa_id: str, image_url: str, caption: str = ""):
28
+ """Send an image via WhatsApp Business API"""
29
+ url = f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages"
30
+ headers = {
31
+ "Authorization": f"Bearer {WHATSAPP_API_TOKEN}",
32
+ "Content-Type": "application/json"
33
+ }
34
+ payload = {
35
+ "messaging_product": "whatsapp",
36
+ "to": wa_id,
37
+ "type": "image",
38
+ "image": {
39
+ "link": image_url,
40
+ "caption": caption
41
+ }
42
+ }
43
+ try:
44
+ async with httpx.AsyncClient() as client:
45
+ resp = await client.post(url, headers=headers, json=payload)
46
+ print(f"Sent image → {resp.status_code}: {resp.text}")
47
+ return resp.status_code == 200
48
+ except Exception as e:
49
+ print(f"Failed to send image: {e}")
50
+ return False
51
+
52
+ async def send_multiple_messages(wa_id: str, messages: list):
53
+ """Send multiple messages with delays to avoid rate limiting"""
54
+ for i, message in enumerate(messages):
55
+ if isinstance(message, dict) and message.get("type") == "image":
56
+ # Send image
57
+ success = await send_whatsapp_image(wa_id, message["url"], message.get("caption", ""))
58
+ else:
59
+ # Send text message
60
+ success = await send_whatsapp_message(wa_id, str(message))
61
+
62
+ if not success:
63
+ print(f"Failed to send message {i+1}")
64
+ return False
65
+
66
+ # Add delay between messages to avoid rate limiting (1 second)
67
+ if i < len(messages) - 1: # Don't delay after the last message
68
+ await asyncio.sleep(1)
69
+
70
+ return True