zenaight commited on
Commit ·
df9de2d
1
Parent(s): b418dc7
Enhance property data handling and intent classification in AI chat
Browse files- Updated the `chat_with_session_memory` function to include address data for AI use while ensuring it is not displayed in listings unless requested, improving user privacy and clarity.
- Modified the intent classification logic to recognize requests for addresses, allowing the system to respond appropriately to user inquiries about property locations.
- Adjusted the database schema in `supabase_setup.sql` to add an address field to the properties table and create an index for efficient querying, enhancing data organization and retrieval.
- These changes aim to improve user interaction by providing more comprehensive responses regarding property details while maintaining privacy and clarity in communication.
- ai_chat.py +22 -9
- supabase_setup.sql +6 -1
ai_chat.py
CHANGED
|
@@ -22,6 +22,8 @@ def chat_with_session_memory(state):
|
|
| 22 |
props = state.get("properties", [])
|
| 23 |
search_status = state.get("search_status_message", "")
|
| 24 |
|
|
|
|
|
|
|
| 25 |
# Add system message with user context
|
| 26 |
system_message = (
|
| 27 |
f"Hello {user_info.get('name','there')}! You are a helpful and concise property agent. "
|
|
@@ -50,7 +52,7 @@ def chat_with_session_memory(state):
|
|
| 50 |
if search_status:
|
| 51 |
system_message += f"\n\n{search_status}"
|
| 52 |
|
| 53 |
-
# Include property data if available (without
|
| 54 |
if props:
|
| 55 |
system_message += "\n\nAvailable listings:\n"
|
| 56 |
for p in props[:5]:
|
|
@@ -58,7 +60,7 @@ 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 available data but NOT image URLs
|
| 62 |
if p.get("listing_url"):
|
| 63 |
system_message += f" URL: {p.get('listing_url')}\n"
|
| 64 |
if p.get("features"):
|
|
@@ -67,9 +69,19 @@ def chat_with_session_memory(state):
|
|
| 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 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
# Build messages array with history
|
| 75 |
messages = [{"role": "system", "content": system_message}]
|
|
@@ -269,11 +281,11 @@ async def extract_and_update_intent(state):
|
|
| 269 |
missing = [f for f in intent_fields if state["intent"].get(f) is None]
|
| 270 |
print(f"DEBUG - Missing intent fields: {missing}")
|
| 271 |
if missing:
|
| 272 |
-
# Check if user is asking for properties or
|
| 273 |
classification = state.get("classification")
|
| 274 |
print(f"DEBUG - Intent update classification check: '{classification}'")
|
| 275 |
-
if classification in ["search_listings", "request_images"]:
|
| 276 |
-
# User is asking for properties
|
| 277 |
print(f"DEBUG - User asking for {classification}, skipping preference questions")
|
| 278 |
return {"response": None}
|
| 279 |
|
|
@@ -301,6 +313,7 @@ async def classify_user_intent(state):
|
|
| 301 |
Classify the user's message into exactly one of:
|
| 302 |
- search_listings (user wants to see property listings)
|
| 303 |
- request_images (user wants to see images/photos/pictures of a listing)
|
|
|
|
| 304 |
- request_details (user wants other info like features, floorplan, video)
|
| 305 |
- other (anything else)
|
| 306 |
Return only the tag.
|
|
@@ -320,7 +333,7 @@ async def extract_and_search_properties(state):
|
|
| 320 |
# Only search when the LLM tagged this as a listings request
|
| 321 |
classification = state.get("classification")
|
| 322 |
print(f"DEBUG - Property search classification check: '{classification}'")
|
| 323 |
-
if classification not in ["search_listings", "request_images"]:
|
| 324 |
print(f"DEBUG - Skipping property search, classification is '{classification}'")
|
| 325 |
return {"response": None}
|
| 326 |
|
|
|
|
| 22 |
props = state.get("properties", [])
|
| 23 |
search_status = state.get("search_status_message", "")
|
| 24 |
|
| 25 |
+
|
| 26 |
+
|
| 27 |
# Add system message with user context
|
| 28 |
system_message = (
|
| 29 |
f"Hello {user_info.get('name','there')}! You are a helpful and concise property agent. "
|
|
|
|
| 52 |
if search_status:
|
| 53 |
system_message += f"\n\n{search_status}"
|
| 54 |
|
| 55 |
+
# Include property data if available (without showing images or addresses in listings)
|
| 56 |
if props:
|
| 57 |
system_message += "\n\nAvailable listings:\n"
|
| 58 |
for p in props[:5]:
|
|
|
|
| 60 |
f"- {p.get('title')} in {p.get('location')}, {p.get('city')}: "
|
| 61 |
f"{p.get('size_sqm')} sqm, {p.get('price')} ({p.get('price_type')})\n"
|
| 62 |
)
|
| 63 |
+
# Include available data but NOT image URLs or addresses in public listings
|
| 64 |
if p.get("listing_url"):
|
| 65 |
system_message += f" URL: {p.get('listing_url')}\n"
|
| 66 |
if p.get("features"):
|
|
|
|
| 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 |
+
# Include address data for AI use (but don't show in listings unless requested)
|
| 74 |
+
if p.get("address"):
|
| 75 |
+
system_message += f" Google Maps Address (for requests only): {p.get('address')}\n"
|
| 76 |
+
|
| 77 |
+
# Let AI know what additional info is available on request
|
| 78 |
+
available_extras = []
|
| 79 |
+
if p.get("images"): available_extras.append("images")
|
| 80 |
+
if p.get("address"): available_extras.append("address")
|
| 81 |
+
if available_extras:
|
| 82 |
+
system_message += f" Available on request: {', '.join(available_extras)}\n"
|
| 83 |
+
|
| 84 |
+
system_message += "\n\nIMPORTANT: You may only reference listings passed in state['properties']. When users ask for images, photos, or pictures, let them know that images are available and will be sent separately. When users ask for the address, location, or where a property is located, provide the Google Maps address from the property data. The addresses are Google Maps compatible for navigation. If information is not available, respond with 'For this listing, I don't have [specific detail] available right now'."
|
| 85 |
|
| 86 |
# Build messages array with history
|
| 87 |
messages = [{"role": "system", "content": system_message}]
|
|
|
|
| 281 |
missing = [f for f in intent_fields if state["intent"].get(f) is None]
|
| 282 |
print(f"DEBUG - Missing intent fields: {missing}")
|
| 283 |
if missing:
|
| 284 |
+
# Check if user is asking for properties, images, or address (using AI classification)
|
| 285 |
classification = state.get("classification")
|
| 286 |
print(f"DEBUG - Intent update classification check: '{classification}'")
|
| 287 |
+
if classification in ["search_listings", "request_images", "request_address"]:
|
| 288 |
+
# User is asking for properties, images, or address - don't interrupt with preference questions
|
| 289 |
print(f"DEBUG - User asking for {classification}, skipping preference questions")
|
| 290 |
return {"response": None}
|
| 291 |
|
|
|
|
| 313 |
Classify the user's message into exactly one of:
|
| 314 |
- search_listings (user wants to see property listings)
|
| 315 |
- request_images (user wants to see images/photos/pictures of a listing)
|
| 316 |
+
- request_address (user wants the address/location of a listing)
|
| 317 |
- request_details (user wants other info like features, floorplan, video)
|
| 318 |
- other (anything else)
|
| 319 |
Return only the tag.
|
|
|
|
| 333 |
# Only search when the LLM tagged this as a listings request
|
| 334 |
classification = state.get("classification")
|
| 335 |
print(f"DEBUG - Property search classification check: '{classification}'")
|
| 336 |
+
if classification not in ["search_listings", "request_images", "request_address"]:
|
| 337 |
print(f"DEBUG - Skipping property search, classification is '{classification}'")
|
| 338 |
return {"response": None}
|
| 339 |
|
supabase_setup.sql
CHANGED
|
@@ -54,6 +54,7 @@ CREATE TABLE IF NOT EXISTS properties (
|
|
| 54 |
description TEXT,
|
| 55 |
location TEXT NOT NULL,
|
| 56 |
city TEXT NOT NULL,
|
|
|
|
| 57 |
size_sqm INTEGER,
|
| 58 |
price INTEGER,
|
| 59 |
price_type TEXT DEFAULT 'monthly',
|
|
@@ -86,6 +87,7 @@ CREATE INDEX IF NOT EXISTS idx_user_intents_updated_at ON user_intents(updated_a
|
|
| 86 |
CREATE INDEX IF NOT EXISTS idx_properties_is_active ON properties(is_active);
|
| 87 |
CREATE INDEX IF NOT EXISTS idx_properties_city ON properties(city);
|
| 88 |
CREATE INDEX IF NOT EXISTS idx_properties_location ON properties(location);
|
|
|
|
| 89 |
CREATE INDEX IF NOT EXISTS idx_properties_price ON properties(price);
|
| 90 |
CREATE INDEX IF NOT EXISTS idx_properties_size_sqm ON properties(size_sqm);
|
| 91 |
CREATE INDEX IF NOT EXISTS idx_properties_is_featured ON properties(is_featured);
|
|
@@ -166,12 +168,13 @@ CREATE TRIGGER update_properties_updated_at
|
|
| 166 |
EXECUTE FUNCTION update_updated_at_column();
|
| 167 |
|
| 168 |
-- Insert some sample properties for testing
|
| 169 |
-
INSERT INTO properties (title, description, location, city, size_sqm, price, price_type, listing_url, images, features, is_active, is_featured) VALUES
|
| 170 |
(
|
| 171 |
'2-3 person office space in Edenvale',
|
| 172 |
'Modern office space perfect for small teams',
|
| 173 |
'Edenvale',
|
| 174 |
'Johannesburg',
|
|
|
|
| 175 |
40,
|
| 176 |
15000,
|
| 177 |
'monthly',
|
|
@@ -186,6 +189,7 @@ INSERT INTO properties (title, description, location, city, size_sqm, price, pri
|
|
| 186 |
'Large warehouse space with loading dock',
|
| 187 |
'Sandton',
|
| 188 |
'Johannesburg',
|
|
|
|
| 189 |
200,
|
| 190 |
25000,
|
| 191 |
'monthly',
|
|
@@ -200,6 +204,7 @@ INSERT INTO properties (title, description, location, city, size_sqm, price, pri
|
|
| 200 |
'Cozy office space in the heart of Cape Town',
|
| 201 |
'CBD',
|
| 202 |
'Cape Town',
|
|
|
|
| 203 |
25,
|
| 204 |
12000,
|
| 205 |
'monthly',
|
|
|
|
| 54 |
description TEXT,
|
| 55 |
location TEXT NOT NULL,
|
| 56 |
city TEXT NOT NULL,
|
| 57 |
+
address TEXT,
|
| 58 |
size_sqm INTEGER,
|
| 59 |
price INTEGER,
|
| 60 |
price_type TEXT DEFAULT 'monthly',
|
|
|
|
| 87 |
CREATE INDEX IF NOT EXISTS idx_properties_is_active ON properties(is_active);
|
| 88 |
CREATE INDEX IF NOT EXISTS idx_properties_city ON properties(city);
|
| 89 |
CREATE INDEX IF NOT EXISTS idx_properties_location ON properties(location);
|
| 90 |
+
CREATE INDEX IF NOT EXISTS idx_properties_address ON properties(address);
|
| 91 |
CREATE INDEX IF NOT EXISTS idx_properties_price ON properties(price);
|
| 92 |
CREATE INDEX IF NOT EXISTS idx_properties_size_sqm ON properties(size_sqm);
|
| 93 |
CREATE INDEX IF NOT EXISTS idx_properties_is_featured ON properties(is_featured);
|
|
|
|
| 168 |
EXECUTE FUNCTION update_updated_at_column();
|
| 169 |
|
| 170 |
-- Insert some sample properties for testing
|
| 171 |
+
INSERT INTO properties (title, description, location, city, address, size_sqm, price, price_type, listing_url, images, features, is_active, is_featured) VALUES
|
| 172 |
(
|
| 173 |
'2-3 person office space in Edenvale',
|
| 174 |
'Modern office space perfect for small teams',
|
| 175 |
'Edenvale',
|
| 176 |
'Johannesburg',
|
| 177 |
+
'Greenstone Shopping Centre, 1 Emerald Boulevard, Modderfontein, 1609, South Africa',
|
| 178 |
40,
|
| 179 |
15000,
|
| 180 |
'monthly',
|
|
|
|
| 189 |
'Large warehouse space with loading dock',
|
| 190 |
'Sandton',
|
| 191 |
'Johannesburg',
|
| 192 |
+
'Sandton City Shopping Centre, 83 Rivonia Rd, Sandhurst, Sandton, 2196, South Africa',
|
| 193 |
200,
|
| 194 |
25000,
|
| 195 |
'monthly',
|
|
|
|
| 204 |
'Cozy office space in the heart of Cape Town',
|
| 205 |
'CBD',
|
| 206 |
'Cape Town',
|
| 207 |
+
'V&A Waterfront, Dock Rd, Cape Town, 8001, South Africa',
|
| 208 |
25,
|
| 209 |
12000,
|
| 210 |
'monthly',
|