zenaight commited on
Commit
46e8095
Β·
1 Parent(s): 008b296

db still not saving repleaced with ai

Browse files
Files changed (2) hide show
  1. ai_chat.py +17 -2
  2. persona_manager.py +109 -31
ai_chat.py CHANGED
@@ -9,7 +9,8 @@ from persona_manager import (
9
  parse_user_response,
10
  update_persona_field,
11
  PERSONA_FIELDS,
12
- get_persona_summary
 
13
  )
14
 
15
  def chat_with_session_memory(state):
@@ -210,6 +211,13 @@ async def process_message(user_message: str, user_info: dict = None, session_id:
210
  parsed_response = await parse_user_response(user_message, field)
211
  break
212
 
 
 
 
 
 
 
 
213
  # Process with AI
214
  result = await chat_graph.ainvoke({
215
  "user_message": user_message,
@@ -226,7 +234,14 @@ async def process_message(user_message: str, user_info: dict = None, session_id:
226
  })
227
 
228
  print("LangGraph result:", result) # Debugging
229
- # Save persona field if present
 
 
 
 
 
 
 
230
  if result.get("persona_field") and result.get("persona_value") is not None and wa_id:
231
  print(f"Saving persona field {result['persona_field']} with value {result['persona_value']} for user {wa_id}")
232
  await update_persona_field(wa_id, result["persona_field"], result["persona_value"])
 
9
  parse_user_response,
10
  update_persona_field,
11
  PERSONA_FIELDS,
12
+ get_persona_summary,
13
+ extract_persona_from_message
14
  )
15
 
16
  def chat_with_session_memory(state):
 
211
  parsed_response = await parse_user_response(user_message, field)
212
  break
213
 
214
+ # Proactive persona extraction from any message
215
+ extracted_persona = {}
216
+ if not is_persona_question and wa_id:
217
+ # Try to extract persona fields from the current message
218
+ extracted_persona = await extract_persona_from_message(user_message, persona)
219
+ print(f"Extracted persona from message: {extracted_persona}")
220
+
221
  # Process with AI
222
  result = await chat_graph.ainvoke({
223
  "user_message": user_message,
 
234
  })
235
 
236
  print("LangGraph result:", result) # Debugging
237
+
238
+ # Save extracted persona fields first
239
+ for field, value in extracted_persona.items():
240
+ if value is not None:
241
+ print(f"Saving extracted persona field {field} with value {value} for user {wa_id}")
242
+ await update_persona_field(wa_id, field, value)
243
+
244
+ # Save persona field if present in result
245
  if result.get("persona_field") and result.get("persona_value") is not None and wa_id:
246
  print(f"Saving persona field {result['persona_field']} with value {result['persona_value']} for user {wa_id}")
247
  await update_persona_field(wa_id, result["persona_field"], result["persona_value"])
persona_manager.py CHANGED
@@ -308,7 +308,7 @@ async def fallback_parse_response(user_message: str, field: str) -> Tuple[bool,
308
 
309
  async def should_ask_persona_question(persona: Dict, conversation_context: str = "") -> Tuple[bool, Optional[str]]:
310
  """
311
- Determine if we should ask a persona question
312
  Returns: (should_ask, field_to_ask)
313
  """
314
  # Check if persona is complete
@@ -317,38 +317,58 @@ async def should_ask_persona_question(persona: Dict, conversation_context: str =
317
  if not missing_fields:
318
  return False, None
319
 
320
- # Check if user is asking about properties (indicates they want to search)
321
- search_indicators = [
322
- "property", "warehouse", "industrial", "space", "building",
323
- "available", "looking for", "need", "find", "search", "show me", "what do you have"
324
- ]
325
-
326
- conversation_lower = conversation_context.lower()
327
- is_searching = any(indicator in conversation_lower for indicator in search_indicators)
328
-
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]
350
-
351
- return False, None
352
 
353
  def get_persona_summary(persona: Dict) -> str:
354
  """Get a human-readable summary of the user's persona"""
@@ -372,4 +392,62 @@ def get_persona_summary(persona: Dict) -> str:
372
  if summary_parts:
373
  return " | ".join(summary_parts)
374
  else:
375
- return "New user - profile incomplete"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
  async def should_ask_persona_question(persona: Dict, conversation_context: str = "") -> Tuple[bool, Optional[str]]:
310
  """
311
+ Determine if we should ask a persona question using AI
312
  Returns: (should_ask, field_to_ask)
313
  """
314
  # Check if persona is complete
 
317
  if not missing_fields:
318
  return False, None
319
 
320
+ if not OPENAI_API_KEY:
321
+ # Fallback to basic keyword detection
322
+ conversation_lower = conversation_context.lower()
323
+
324
+ # Check for greetings
325
+ casual_indicators = ["hi", "hello", "hey", "thanks", "thank you", "bye", "goodbye"]
326
+ is_casual = any(indicator in conversation_lower for indicator in casual_indicators)
327
+
328
+ if is_casual:
329
+ return False, None
330
+
331
+ # Check for property-related keywords
332
+ search_indicators = ["property", "warehouse", "industrial", "space", "building", "looking for", "need", "find"]
333
+ is_searching = any(indicator in conversation_lower for indicator in search_indicators)
334
+
335
+ if is_searching:
336
+ return True, missing_fields[0]
337
+
 
 
 
338
  return False, None
339
 
340
+ try:
341
+ # Use AI to determine if we should ask persona questions
342
+ system_prompt = f"""You are a property agent assistant. Determine if the user is showing interest in finding a property and needs persona questions asked.
343
+
344
+ Current conversation context: "{conversation_context}"
345
+
346
+ Available persona fields to ask about: {missing_fields}
347
+
348
+ Rules:
349
+ - If the user is just greeting (hi, hello, etc.) β†’ return "no"
350
+ - If the user is asking about properties, locations, business needs, or showing interest in finding space β†’ return "yes"
351
+ - If the user mentions locations (including abbreviations like cpt, pta, jhb) β†’ return "yes"
352
+ - If the user mentions business types (clothing, manufacturing, etc.) β†’ return "yes"
353
+
354
+ Return ONLY "yes" or "no"."""
355
+
356
+ messages = [
357
+ {"role": "system", "content": system_prompt},
358
+ {"role": "user", "content": conversation_context}
359
+ ]
360
+
361
+ response = llm.invoke(messages)
362
+ should_ask = response.content.strip().lower() == "yes"
363
+
364
+ if should_ask:
365
+ return True, missing_fields[0]
366
+ else:
367
+ return False, None
368
+
369
+ except Exception as e:
370
+ print(f"Error in AI persona question detection: {e}")
371
  return False, None
 
 
 
 
 
 
372
 
373
  def get_persona_summary(persona: Dict) -> str:
374
  """Get a human-readable summary of the user's persona"""
 
392
  if summary_parts:
393
  return " | ".join(summary_parts)
394
  else:
395
+ return "New user - profile incomplete"
396
+
397
+ async def extract_persona_from_message(user_message: str, current_persona: Dict) -> Dict:
398
+ """
399
+ Proactively extract persona fields from any user message using AI
400
+ Returns: dict of field -> value for any fields that can be extracted
401
+ """
402
+ if not OPENAI_API_KEY:
403
+ return {}
404
+
405
+ try:
406
+ # Create a comprehensive prompt for AI extraction
407
+ system_prompt = """You are a property agent assistant. Extract any persona information from the user's message.
408
+
409
+ Available persona fields:
410
+ - intent: "buy" or "lease" (extract from words like buy, purchase, own, lease, rent)
411
+ - location_preference: Extract location names, including abbreviations (e.g., "cpt" = "cape town", "jhb" = "johannesburg", "pta" = "pretoria")
412
+ - budget: Extract numeric values with currency/budget indicators (e.g., "500k" = 500000, "$1m" = 1000000)
413
+ - size_preference_sqm: Extract size in square meters (convert from sq ft if needed)
414
+ - must_have: Extract features as array (e.g., ["truck access", "office space"])
415
+
416
+ Return ONLY a JSON object with the extracted fields. Only include fields that are clearly mentioned or implied.
417
+ If no relevant information is found for a field, don't include it in the response.
418
+
419
+ Examples:
420
+ - "I want a property in cpt" β†’ {"location_preference": "cape town"}
421
+ - "Looking for warehouse in pta around 500k" β†’ {"location_preference": "pretoria", "budget": 500000}
422
+ - "Need space for clothing business with office" β†’ {"must_have": ["office"]}
423
+ """
424
+
425
+ messages = [
426
+ {"role": "system", "content": system_prompt},
427
+ {"role": "user", "content": user_message}
428
+ ]
429
+
430
+ response = llm.invoke(messages)
431
+
432
+ # Parse the JSON response
433
+ import json
434
+ try:
435
+ extracted = json.loads(response.content)
436
+
437
+ # Validate and clean the extracted data
438
+ cleaned_extracted = {}
439
+
440
+ # Only include fields that aren't already set in current_persona
441
+ for field, value in extracted.items():
442
+ if field in PERSONA_FIELDS and not current_persona.get(field) and value is not None:
443
+ cleaned_extracted[field] = value
444
+
445
+ return cleaned_extracted
446
+
447
+ except json.JSONDecodeError:
448
+ print(f"Failed to parse AI extraction response: {response.content}")
449
+ return {}
450
+
451
+ except Exception as e:
452
+ print(f"Error in AI persona extraction: {e}")
453
+ return {}