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

Refactor property search logic to support flexible ranges

Browse files

- Updated the `search_properties` function to allow for flexible budget and size ranges, enabling searches within ±50% of the specified values.
- Removed the must-have features from the database search, shifting the responsibility to the chat response logic for better user interaction.
- Simplified the `extract_and_search_properties` function by eliminating multiple fallback passes, streamlining the property search process and improving response clarity.
- These changes aim to enhance the user experience by providing more relevant property listings while maintaining clear communication regarding search results.

Files changed (2) hide show
  1. ai_chat.py +4 -49
  2. database.py +13 -9
ai_chat.py CHANGED
@@ -348,64 +348,19 @@ async def extract_and_search_properties(state):
348
  if intent.get("size_preference_sqm") is not None:
349
  filters["size_preference_sqm"] = intent["size_preference_sqm"]
350
 
351
- # Add must-have features if set
352
- must_have = intent.get("must_have", [])
353
- if must_have and len(must_have) > 0:
354
- filters["must_have"] = must_have
355
-
356
- # Search for properties
357
  print(f"DEBUG - Searching with filters: {filters}")
358
  properties = await search_properties(filters)
359
  state["properties"] = properties
360
- print(f"DEBUG - Found {len(properties)} properties with strict filters")
361
 
362
- # Pass 1: strict filters
363
  if properties:
364
  print("DEBUG - Properties found, returning properties to continue to chat")
365
  return {"properties": properties}
366
 
367
- # Pass 2: drop must-haves
368
- if "must_have" in filters:
369
- filters2 = {k: v for k, v in filters.items() if k != "must_have"}
370
- props2 = await search_properties(filters2)
371
- if props2:
372
- return {
373
- "properties": props2,
374
- "search_status_message": (
375
- "I don't have an exact match with those features, "
376
- "but here are listings matching your location, budget and size."
377
- )
378
- }
379
-
380
- # Pass 3: drop size
381
- if "size_preference_sqm" in filters:
382
- filters3 = {k: v for k, v in filters.items() if k not in ["must_have", "size_preference_sqm"]}
383
- props3 = await search_properties(filters3)
384
- if props3:
385
- return {
386
- "properties": props3,
387
- "search_status_message": (
388
- "I don't have that exact size, "
389
- "but here are listings matching your location and budget."
390
- )
391
- }
392
-
393
- # Pass 4: drop budget
394
- if "budget" in filters:
395
- filters4 = {k: v for k, v in filters.items() if k not in ["must_have", "size_preference_sqm", "budget"]}
396
- props4 = await search_properties(filters4)
397
- if props4:
398
- return {
399
- "properties": props4,
400
- "search_status_message": (
401
- f"I don't have listings within your budget in {filters4['location_preference']}, "
402
- "but here are some options in that area."
403
- )
404
- }
405
-
406
- # Final fallback: no listings at all
407
  state["response"] = (
408
- f"I don't have any listings right now matching your criteria. "
409
  "I'll notify you as soon as something becomes available. "
410
  "Feel free to reach out any time!"
411
  )
 
348
  if intent.get("size_preference_sqm") is not None:
349
  filters["size_preference_sqm"] = intent["size_preference_sqm"]
350
 
351
+ # Search for properties with flexible ranges
 
 
 
 
 
352
  print(f"DEBUG - Searching with filters: {filters}")
353
  properties = await search_properties(filters)
354
  state["properties"] = properties
355
+ print(f"DEBUG - Found {len(properties)} properties with flexible ranges")
356
 
 
357
  if properties:
358
  print("DEBUG - Properties found, returning properties to continue to chat")
359
  return {"properties": properties}
360
 
361
+ # No properties found with any filters
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  state["response"] = (
363
+ f"I don't have any listings right now in {location}. "
364
  "I'll notify you as soon as something becomes available. "
365
  "Feel free to reach out any time!"
366
  )
database.py CHANGED
@@ -288,7 +288,7 @@ async def update_user_intent(session_id: str, updates: dict):
288
  # --- Property Search ---
289
  async def search_properties(filters: dict) -> list:
290
  """
291
- Returns up to 5 active properties matching the user's filters.
292
  """
293
  if not supabase:
294
  return []
@@ -310,20 +310,24 @@ async def search_properties(filters: dict) -> list:
310
  # Fallback to location field
311
  query = supabase.table("properties").select("*").eq("is_active", True).ilike("location", f"%{loc}%")
312
 
313
- # b. Budget cap
314
  budget = filters.get("budget")
315
  if budget is not None:
316
- query = query.lte("price", budget)
 
 
 
317
 
318
- # c. Minimum size
319
  size = filters.get("size_preference_sqm")
320
  if size is not None:
321
- query = query.gte("size_sqm", size)
 
 
 
322
 
323
- # d. Must-have features
324
- features = filters.get("must_have") or []
325
- for feat in features:
326
- query = query.contains("features", [feat])
327
 
328
  # e. Order & limit
329
  query = query.order("is_featured", desc=True).order("price").limit(5)
 
288
  # --- Property Search ---
289
  async def search_properties(filters: dict) -> list:
290
  """
291
+ Returns up to 5 active properties matching the user's filters with flexible ranges.
292
  """
293
  if not supabase:
294
  return []
 
310
  # Fallback to location field
311
  query = supabase.table("properties").select("*").eq("is_active", True).ilike("location", f"%{loc}%")
312
 
313
+ # b. Budget range (target ± 50% tolerance)
314
  budget = filters.get("budget")
315
  if budget is not None:
316
+ # Allow properties within 50% of the target budget
317
+ min_budget = int(budget * 0.5) # 50% below target
318
+ max_budget = int(budget * 1.5) # 50% above target
319
+ query = query.gte("price", min_budget).lte("price", max_budget)
320
 
321
+ # c. Size range (target ± 50% tolerance)
322
  size = filters.get("size_preference_sqm")
323
  if size is not None:
324
+ # Allow properties within 50% of the target size
325
+ min_size = int(size * 0.5) # 50% below target
326
+ max_size = int(size * 1.5) # 50% above target
327
+ query = query.gte("size_sqm", min_size).lte("size_sqm", max_size)
328
 
329
+ # Note: Must-have features are removed from database search
330
+ # They will be handled by the LLM in the chat response
 
 
331
 
332
  # e. Order & limit
333
  query = query.order("is_featured", desc=True).order("price").limit(5)