zenaight commited on
Commit ·
5c9a3a3
1
Parent(s): 41e7566
data not saving for user persona
Browse files- ai_chat.py +3 -2
- persona_manager.py +69 -7
ai_chat.py
CHANGED
|
@@ -105,13 +105,14 @@ def chat_with_session_memory(state):
|
|
| 105 |
system_message = """You are a friendly, professional industrial property agent assistant. Be conversational and human-like in your responses.
|
| 106 |
|
| 107 |
Key guidelines:
|
| 108 |
-
-
|
| 109 |
- Use the user's name if available to make it personal
|
| 110 |
- Be helpful and informative about industrial properties
|
| 111 |
- Don't immediately jump to asking questions - have a natural conversation first
|
| 112 |
- If someone asks for clarification about property terms, explain clearly and helpfully
|
| 113 |
- Only ask about their property needs when they show interest in searching or viewing properties
|
| 114 |
-
- Keep responses conversational and not robotic
|
|
|
|
| 115 |
|
| 116 |
if user_info.get("name") and user_info["name"] != "Unknown":
|
| 117 |
system_message += f"\nThe user's name is {user_info['name']} - use their name to make it personal."
|
|
|
|
| 105 |
system_message = """You are a friendly, professional industrial property agent assistant. Be conversational and human-like in your responses.
|
| 106 |
|
| 107 |
Key guidelines:
|
| 108 |
+
- ALWAYS respond naturally to greetings (hi, hello, etc.) with a warm greeting back FIRST
|
| 109 |
- Use the user's name if available to make it personal
|
| 110 |
- Be helpful and informative about industrial properties
|
| 111 |
- Don't immediately jump to asking questions - have a natural conversation first
|
| 112 |
- If someone asks for clarification about property terms, explain clearly and helpfully
|
| 113 |
- Only ask about their property needs when they show interest in searching or viewing properties
|
| 114 |
+
- Keep responses conversational and not robotic
|
| 115 |
+
- If user says "hi" or "hello", respond with a greeting like "Hi [Name]! How can I help you with industrial properties today?" """
|
| 116 |
|
| 117 |
if user_info.get("name") and user_info["name"] != "Unknown":
|
| 118 |
system_message += f"\nThe user's name is {user_info['name']} - use their name to make it personal."
|
persona_manager.py
CHANGED
|
@@ -65,6 +65,7 @@ async def create_user_persona(wa_id: str) -> Dict:
|
|
| 65 |
async def update_persona_field(wa_id: str, field: str, value) -> bool:
|
| 66 |
"""Update a specific field in user persona"""
|
| 67 |
if not supabase:
|
|
|
|
| 68 |
return False
|
| 69 |
|
| 70 |
try:
|
|
@@ -72,7 +73,9 @@ async def update_persona_field(wa_id: str, field: str, value) -> bool:
|
|
| 72 |
field: value,
|
| 73 |
"updated_at": datetime.utcnow().isoformat()
|
| 74 |
}
|
| 75 |
-
|
|
|
|
|
|
|
| 76 |
return True
|
| 77 |
except Exception as e:
|
| 78 |
print(f"Error updating persona field {field}: {e}")
|
|
@@ -123,12 +126,14 @@ async def parse_user_response(user_message: str, field: str) -> Tuple[bool, any,
|
|
| 123 |
|
| 124 |
Rules:
|
| 125 |
- For intent: accept "buy", "lease", "rent", "purchase", "own"
|
| 126 |
-
- For budget: extract numeric values (e.g., "around 500k" -> 500000)
|
| 127 |
-
- For size: extract square meters (e.g., "1500 sqm" -> 1500)
|
| 128 |
- For location: extract location names
|
| 129 |
- For must_have: extract features as array (e.g., ["truck access", "yard space"])
|
|
|
|
| 130 |
- If user asks for clarification, set is_clarification=true
|
| 131 |
- If user says "not sure", "skip", "later", set is_skip=true
|
|
|
|
| 132 |
"""
|
| 133 |
|
| 134 |
messages = [
|
|
@@ -185,8 +190,15 @@ async def fallback_parse_response(user_message: str, field: str) -> Tuple[bool,
|
|
| 185 |
if any(word in user_message_lower for word in field_words):
|
| 186 |
return False, None, PERSONA_FIELDS[field]["clarification"]
|
| 187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
# Check for skip requests
|
| 189 |
-
skip_words = ["not sure", "skip", "later", "don't know", "maybe later", "pass"]
|
| 190 |
if any(word in user_message_lower for word in skip_words):
|
| 191 |
return True, None, PERSONA_FIELDS[field]["skip_response"]
|
| 192 |
|
|
@@ -199,9 +211,33 @@ async def fallback_parse_response(user_message: str, field: str) -> Tuple[bool,
|
|
| 199 |
|
| 200 |
elif field == "budget":
|
| 201 |
import re
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
numbers = re.findall(r'\d+', user_message)
|
| 203 |
if numbers:
|
| 204 |
-
# Assume the largest number is the budget
|
| 205 |
budget = max(int(n) for n in numbers)
|
| 206 |
if "k" in user_message_lower or "thousand" in user_message_lower:
|
| 207 |
budget *= 1000
|
|
@@ -211,6 +247,26 @@ async def fallback_parse_response(user_message: str, field: str) -> Tuple[bool,
|
|
| 211 |
|
| 212 |
elif field == "size_preference_sqm":
|
| 213 |
import re
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
numbers = re.findall(r'\d+', user_message)
|
| 215 |
if numbers:
|
| 216 |
size = max(int(n) for n in numbers)
|
|
@@ -273,15 +329,21 @@ async def should_ask_persona_question(persona: Dict, conversation_context: str =
|
|
| 273 |
# Check for greetings or casual conversation - don't ask persona questions
|
| 274 |
casual_indicators = [
|
| 275 |
"hi", "hello", "hey", "good morning", "good afternoon", "good evening",
|
| 276 |
-
"how are you", "what's up", "thanks", "thank you", "bye", "goodbye"
|
|
|
|
| 277 |
]
|
| 278 |
|
|
|
|
| 279 |
is_casual = any(indicator in conversation_lower for indicator in casual_indicators)
|
| 280 |
|
| 281 |
-
#
|
| 282 |
if is_casual:
|
| 283 |
return False, None
|
| 284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
# Only ask if user is actively searching for properties
|
| 286 |
if is_searching:
|
| 287 |
return True, missing_fields[0]
|
|
|
|
| 65 |
async def update_persona_field(wa_id: str, field: str, value) -> bool:
|
| 66 |
"""Update a specific field in user persona"""
|
| 67 |
if not supabase:
|
| 68 |
+
print(f"Supabase not configured - skipping update for {field}: {value}")
|
| 69 |
return False
|
| 70 |
|
| 71 |
try:
|
|
|
|
| 73 |
field: value,
|
| 74 |
"updated_at": datetime.utcnow().isoformat()
|
| 75 |
}
|
| 76 |
+
print(f"Updating persona field {field} with value {value} for user {wa_id}")
|
| 77 |
+
response = supabase.table("user_personas").update(update_data).eq("wa_id", wa_id).execute()
|
| 78 |
+
print(f"Persona update response: {response.data if response.data else 'No data returned'}")
|
| 79 |
return True
|
| 80 |
except Exception as e:
|
| 81 |
print(f"Error updating persona field {field}: {e}")
|
|
|
|
| 126 |
|
| 127 |
Rules:
|
| 128 |
- For intent: accept "buy", "lease", "rent", "purchase", "own"
|
| 129 |
+
- For budget: extract numeric values (e.g., "around 500k" -> 500000, "$500,000" -> 500000)
|
| 130 |
+
- For size: extract square meters (e.g., "1500 sqm" -> 1500, "10000 sq ft" -> 929)
|
| 131 |
- For location: extract location names
|
| 132 |
- For must_have: extract features as array (e.g., ["truck access", "yard space"])
|
| 133 |
+
- If user says "yes", "yeah", "sure" to a suggestion, extract the suggested value
|
| 134 |
- If user asks for clarification, set is_clarification=true
|
| 135 |
- If user says "not sure", "skip", "later", set is_skip=true
|
| 136 |
+
- Be generous in parsing - if you can reasonably extract a value, do so
|
| 137 |
"""
|
| 138 |
|
| 139 |
messages = [
|
|
|
|
| 190 |
if any(word in user_message_lower for word in field_words):
|
| 191 |
return False, None, PERSONA_FIELDS[field]["clarification"]
|
| 192 |
|
| 193 |
+
# Check for affirmative responses (yes, sure, ok, etc.)
|
| 194 |
+
affirmative_words = ["yes", "yeah", "yep", "sure", "ok", "okay", "correct", "right", "that's right", "exactly"]
|
| 195 |
+
if any(word in user_message_lower for word in affirmative_words):
|
| 196 |
+
# For affirmative responses, we need to get the value from context
|
| 197 |
+
# This will be handled by the LLM parsing, but we can provide a fallback
|
| 198 |
+
return True, "confirmed", "Got it! Thanks for confirming."
|
| 199 |
+
|
| 200 |
# Check for skip requests
|
| 201 |
+
skip_words = ["not sure", "skip", "later", "don't know", "maybe later", "pass", "no idea"]
|
| 202 |
if any(word in user_message_lower for word in skip_words):
|
| 203 |
return True, None, PERSONA_FIELDS[field]["skip_response"]
|
| 204 |
|
|
|
|
| 211 |
|
| 212 |
elif field == "budget":
|
| 213 |
import re
|
| 214 |
+
# Look for currency patterns
|
| 215 |
+
budget_patterns = [
|
| 216 |
+
r'\$(\d+(?:,\d{3})*)',
|
| 217 |
+
r'(\d+(?:,\d{3})*)\s*dollars',
|
| 218 |
+
r'(\d+)\s*k',
|
| 219 |
+
r'(\d+)\s*thousand',
|
| 220 |
+
r'(\d+)\s*m',
|
| 221 |
+
r'(\d+)\s*million'
|
| 222 |
+
]
|
| 223 |
+
|
| 224 |
+
for pattern in budget_patterns:
|
| 225 |
+
match = re.search(pattern, user_message_lower)
|
| 226 |
+
if match:
|
| 227 |
+
budget_str = match.group(1).replace(',', '')
|
| 228 |
+
budget = int(budget_str)
|
| 229 |
+
|
| 230 |
+
# Apply multipliers
|
| 231 |
+
if "k" in pattern or "thousand" in pattern:
|
| 232 |
+
budget *= 1000
|
| 233 |
+
elif "m" in pattern or "million" in pattern:
|
| 234 |
+
budget *= 1000000
|
| 235 |
+
|
| 236 |
+
return True, budget, f"Thanks! I'll look for properties around ${budget:,}."
|
| 237 |
+
|
| 238 |
+
# Fallback: just look for numbers
|
| 239 |
numbers = re.findall(r'\d+', user_message)
|
| 240 |
if numbers:
|
|
|
|
| 241 |
budget = max(int(n) for n in numbers)
|
| 242 |
if "k" in user_message_lower or "thousand" in user_message_lower:
|
| 243 |
budget *= 1000
|
|
|
|
| 247 |
|
| 248 |
elif field == "size_preference_sqm":
|
| 249 |
import re
|
| 250 |
+
# Look for numbers followed by sqm, sq ft, square meters, etc.
|
| 251 |
+
size_patterns = [
|
| 252 |
+
r'(\d+)\s*sqm',
|
| 253 |
+
r'(\d+)\s*square\s*meters',
|
| 254 |
+
r'(\d+)\s*sq\s*ft',
|
| 255 |
+
r'(\d+)\s*square\s*feet',
|
| 256 |
+
r'(\d+)\s*ft',
|
| 257 |
+
r'(\d+)\s*feet'
|
| 258 |
+
]
|
| 259 |
+
|
| 260 |
+
for pattern in size_patterns:
|
| 261 |
+
match = re.search(pattern, user_message_lower)
|
| 262 |
+
if match:
|
| 263 |
+
size = int(match.group(1))
|
| 264 |
+
# Convert sq ft to sqm if needed
|
| 265 |
+
if 'ft' in pattern and 'sq' in pattern:
|
| 266 |
+
size = int(size * 0.0929) # Convert sq ft to sqm
|
| 267 |
+
return True, size, f"Perfect! {size} sqm should give you good options."
|
| 268 |
+
|
| 269 |
+
# Fallback: just look for numbers
|
| 270 |
numbers = re.findall(r'\d+', user_message)
|
| 271 |
if numbers:
|
| 272 |
size = max(int(n) for n in numbers)
|
|
|
|
| 329 |
# Check for greetings or casual conversation - don't ask persona questions
|
| 330 |
casual_indicators = [
|
| 331 |
"hi", "hello", "hey", "good morning", "good afternoon", "good evening",
|
| 332 |
+
"how are you", "what's up", "thanks", "thank you", "bye", "goodbye",
|
| 333 |
+
"morning", "afternoon", "evening", "sup", "yo"
|
| 334 |
]
|
| 335 |
|
| 336 |
+
# Check if the message is primarily a greeting
|
| 337 |
is_casual = any(indicator in conversation_lower for indicator in casual_indicators)
|
| 338 |
|
| 339 |
+
# If it's a greeting, don't ask persona questions
|
| 340 |
if is_casual:
|
| 341 |
return False, None
|
| 342 |
|
| 343 |
+
# Also check if the message is very short (likely a greeting)
|
| 344 |
+
if len(user_message.strip()) <= 10 and is_casual:
|
| 345 |
+
return False, None
|
| 346 |
+
|
| 347 |
# Only ask if user is actively searching for properties
|
| 348 |
if is_searching:
|
| 349 |
return True, missing_fields[0]
|