AIDA / app /ml /models /user_role_context_handler.py
destinyebuka's picture
dora
4c9881b
#!/usr/bin/env python3
"""
user_role_context_handler.py - Handle different user roles and rental models
Supports: Airbnb (host/guest), African rentals (landlord/renter/tenant)
"""
import logging
from typing import Dict, Tuple, Optional
from enum import Enum
import re
logger = logging.getLogger(__name__)
class RentalModel(Enum):
"""Different rental models"""
AIRBNB = "airbnb" # Short-stay, host/guest model
AFRICAN_RENTAL = "african" # Long-term rent, landlord/tenant model
ROOMMATE = "roommate" # Room sharing in existing space
MIXED = "mixed" # Both types possible
UNKNOWN = "unknown"
class UserRole:
"""Handle different user roles across rental models"""
# Airbnb roles
AIRBNB_HOST = "airbnb_host"
AIRBNB_GUEST = "airbnb_guest"
# African rental roles
LANDLORD = "landlord"
RENTER = "renter"
TENANT = "tenant" # Alias for renter
# Roommate roles
HOMEOWNER_SEEKING_ROOMMATE = "homeowner_seeking_roommate" # Has space, looking for roommate
ROOMMATE_SEEKER = "roommate_seeker" # Looking for a room to share
# Generic
OWNER = "owner"
BUYER = "buyer"
SELLER = "seller"
class UserRoleDetector:
"""Intelligently detect user role from context"""
def __init__(self):
# Keywords for role detection
self.host_keywords = {
"airbnb": ["host", "hosting", "list my property", "list my place", "rent out", "share"],
"african": ["landlord", "owner", "property owner", "im renting out", "im listing"]
}
self.guest_keywords = {
"airbnb": ["guest", "book", "looking for place", "need accommodation", "airbnb"],
"african": ["renter", "tenant", "looking to rent", "seeking", "want to rent", "im looking for"]
}
self.buyer_keywords = ["buy", "purchase", "for sale", "selling", "acquire"]
self.seller_keywords = ["sell", "selling", "sale", "list for sale"]
# Roommate keywords
self.homeowner_seeking_roommate_keywords = [
"looking for a roommate", "need a roommate", "seeking roommate",
"want to share my", "have a spare room", "room available",
"looking to share", "share my apartment", "share my house",
"my place is too big", "extra room", "can share"
]
self.roommate_seeker_keywords = [
"looking for a room", "seeking a room", "need a room",
"looking for roommate", "want to share a place", "room for rent",
"share accommodation", "shared apartment", "shared house",
"need accommodation", "looking for a place to share"
]
logger.info("πŸ” User Role Detector initialized")
def detect_rental_model(self, user_message: str, location: str = None) -> RentalModel:
"""Detect which rental model user is in"""
msg_lower = user_message.lower().strip()
# Keywords indicating Airbnb model
airbnb_indicators = ["airbnb", "short stay", "nightly", "daily", "vacation rental", "host"]
# Keywords indicating African rental model
african_indicators = ["landlord", "tenant", "renter", "monthly rent", "long term", "furnished room"]
# Keywords indicating roommate model
roommate_indicators = ["roommate", "share my", "spare room", "share apartment", "shared house", "share a place"]
# Check for explicit indicators
for indicator in roommate_indicators:
if indicator in msg_lower:
logger.info(f"🏘️ Detected roommate model: '{indicator}'")
return RentalModel.ROOMMATE
for indicator in airbnb_indicators:
if indicator in msg_lower:
logger.info(f"🏨 Detected Airbnb model: '{indicator}'")
return RentalModel.AIRBNB
for indicator in african_indicators:
if indicator in msg_lower:
logger.info(f"🏒 Detected African rental model: '{indicator}'")
return RentalModel.AFRICAN_RENTAL
# Location-based inference (African locations more likely = African model)
if location:
african_countries = ["benin", "nigeria", "kenya", "ghana", "south africa", "uganda", "senegal"]
if any(country in location.lower() for country in african_countries):
logger.info(f"πŸ“ African location detected: {location}")
return RentalModel.AFRICAN_RENTAL
# Default to mixed
return RentalModel.MIXED
def detect_user_role(self, user_message: str, rental_model: RentalModel = None) -> Tuple[str, float]:
"""
Detect user role from message
Returns: (role, confidence)
"""
msg_lower = user_message.lower().strip()
if rental_model is None:
rental_model = self.detect_rental_model(user_message)
# ==================== SELLER / LANDLORD ====================
# Check for explicit landlord/owner language
landlord_explicit = ["im a landlord", "im the landlord", "i own", "i own this", "as a landlord"]
for phrase in landlord_explicit:
if phrase in msg_lower:
logger.info(f"βœ… Explicit landlord detected: '{phrase}'")
return UserRole.LANDLORD, 0.99
# Check for listing/rental language
if rental_model == RentalModel.AFRICAN_RENTAL:
landlord_signals = [
"im listing", "list my", "im renting out", "property for rent",
"available for rent", "i have a", "i own a"
]
for signal in landlord_signals:
if signal in msg_lower:
logger.info(f"🏠 African landlord signal: '{signal}'")
return UserRole.LANDLORD, 0.90
if rental_model == RentalModel.AIRBNB:
host_signals = ["im hosting", "im a host", "list on airbnb", "airbnb host", "share my place"]
for signal in host_signals:
if signal in msg_lower:
logger.info(f"🏨 Airbnb host signal: '{signal}'")
return UserRole.AIRBNB_HOST, 0.90
# ==================== BUYER / SELLER (SALE) ====================
# Explicit sale language
seller_signals = ["im selling", "for sale", "sell my", "selling property", "list for sale"]
for signal in seller_signals:
if signal in msg_lower:
logger.info(f"πŸ’° Seller detected: '{signal}'")
return UserRole.SELLER, 0.95
buyer_signals = ["want to buy", "looking to purchase", "im buying", "purchase property"]
for signal in buyer_signals:
if signal in msg_lower:
logger.info(f"πŸ’³ Buyer detected: '{signal}'")
return UserRole.BUYER, 0.95
# ==================== RENTER / GUEST ====================
# Check for explicit renter language
renter_explicit = ["im a tenant", "im a renter", "im looking to rent", "looking for a place to rent"]
for phrase in renter_explicit:
if phrase in msg_lower:
logger.info(f"βœ… Explicit renter/tenant detected: '{phrase}'")
if rental_model == RentalModel.AFRICAN_RENTAL:
return UserRole.TENANT, 0.99
else:
return UserRole.AIRBNB_GUEST, 0.99
# ==================== ROOMMATE ROLES ====================
# Homeowner seeking roommate
for keyword in self.homeowner_seeking_roommate_keywords:
if keyword in msg_lower:
logger.info(f"βœ… Homeowner seeking roommate detected: '{keyword}'")
return UserRole.HOMEOWNER_SEEKING_ROOMMATE, 0.90
# Roommate seeker
for keyword in self.roommate_seeker_keywords:
if keyword in msg_lower:
logger.info(f"βœ… Roommate seeker detected: '{keyword}'")
return UserRole.ROOMMATE_SEEKER, 0.90
# Guest/renter signals
if rental_model == RentalModel.AFRICAN_RENTAL:
renter_signals = [
"looking for a", "need a", "seeking", "want to rent",
"im looking for", "show me", "what do you have", "available rooms"
]
for signal in renter_signals:
if signal in msg_lower:
logger.info(f"πŸ” African renter signal: '{signal}'")
return UserRole.RENTER, 0.80
if rental_model == RentalModel.AIRBNB:
guest_signals = [
"looking for accommodation", "need a place", "book",
"where can i stay", "available places", "show me listings"
]
for signal in guest_signals:
if signal in msg_lower:
logger.info(f"πŸ” Airbnb guest signal: '{signal}'")
return UserRole.AIRBNB_GUEST, 0.80
logger.warning(f"⚠️ Could not determine user role from: {user_message}")
return None, 0.0
def validate_role_consistency(self, user_role: str, rental_model: RentalModel) -> bool:
"""Validate that role matches rental model"""
valid_combinations = {
RentalModel.AIRBNB: [UserRole.AIRBNB_HOST, UserRole.AIRBNB_GUEST],
RentalModel.AFRICAN_RENTAL: [UserRole.LANDLORD, UserRole.RENTER, UserRole.TENANT],
RentalModel.ROOMMATE: [UserRole.HOMEOWNER_SEEKING_ROOMMATE, UserRole.ROOMMATE_SEEKER],
RentalModel.MIXED: [UserRole.LANDLORD, UserRole.RENTER, UserRole.TENANT,
UserRole.AIRBNB_HOST, UserRole.AIRBNB_GUEST,
UserRole.HOMEOWNER_SEEKING_ROOMMATE, UserRole.ROOMMATE_SEEKER],
}
valid = valid_combinations.get(rental_model, [])
if user_role in valid:
logger.info(f"βœ… Role {user_role} valid for {rental_model.value}")
return True
logger.warning(f"⚠️ Role {user_role} may not match {rental_model.value}")
return False
class RoleBasedInferenceEngine:
"""Adapt inference based on user role and rental model"""
def __init__(self):
self.role_detector = UserRoleDetector()
logger.info("🧠 Role-based Inference Engine initialized")
def infer_listing_type(self, state: Dict, user_message: str, rental_model: RentalModel = None) -> Tuple[str, float]:
"""
Infer listing type based on user role and rental model
Returns: (listing_type, confidence)
"""
# Detect rental model
if rental_model is None:
rental_model = self.role_detector.detect_rental_model(user_message, state.get("location"))
# Detect user role
user_role, role_confidence = self.role_detector.detect_user_role(user_message, rental_model)
logger.info(f"πŸ” Rental Model: {rental_model.value}")
logger.info(f"πŸ‘€ User Role: {user_role} (confidence: {role_confidence:.0%})")
# Store in state for later use
state["rental_model"] = rental_model.value
state["user_role"] = user_role
# ==================== AIRBNB MODEL ====================
if rental_model == RentalModel.AIRBNB:
# Host listing = short-stay
if user_role == UserRole.AIRBNB_HOST:
logger.info("πŸ“ Host β†’ short-stay listing")
return "short-stay", 0.98
# Guest searching = just needs to search
if user_role == UserRole.AIRBNB_GUEST:
logger.info("πŸ“ Guest β†’ searching for short-stay")
return "short-stay", 0.95
# ==================== AFRICAN RENTAL MODEL ====================
elif rental_model == RentalModel.AFRICAN_RENTAL:
# Landlord listing = rent listing
if user_role in [UserRole.LANDLORD, UserRole.OWNER]:
logger.info("πŸ“ Landlord β†’ rent listing")
return "rent", 0.98
# Renter/tenant searching = rent listing
if user_role in [UserRole.RENTER, UserRole.TENANT]:
logger.info("πŸ“ Tenant/Renter β†’ searching for rent")
return "rent", 0.95
# ==================== ROOMMATE MODEL ====================
elif rental_model == RentalModel.ROOMMATE:
# Homeowner seeking roommate = roommate listing
if user_role == UserRole.HOMEOWNER_SEEKING_ROOMMATE:
logger.info("πŸ“ Homeowner β†’ roommate listing")
return "roommate", 0.98
# Roommate seeker = searching roommate
if user_role == UserRole.ROOMMATE_SEEKER:
logger.info("πŸ“ Roommate seeker β†’ searching for roommate")
return "roommate", 0.95
# ==================== SALE MODEL (both) ====================
if user_role == UserRole.SELLER:
logger.info("πŸ“ Seller β†’ sale listing")
return "sale", 0.98
if user_role == UserRole.BUYER:
logger.info("πŸ“ Buyer β†’ searching for sale")
return "sale", 0.95
# Fallback: check explicit listing_type
explicit_type = state.get("listing_type")
if explicit_type:
logger.info(f"πŸ“ Using explicit listing_type: {explicit_type}")
return explicit_type, 0.85
logger.warning("⚠️ Could not infer listing_type, defaulting to rent")
return "rent", 0.5
def adapt_field_extraction(self, state: Dict, user_message: str) -> Dict:
"""
Adapt field extraction based on user role and rental model
"""
rental_model = self.role_detector.detect_rental_model(user_message, state.get("location"))
user_role, _ = self.role_detector.detect_user_role(user_message, rental_model)
extraction_config = {
"rental_model": rental_model.value,
"user_role": user_role,
"required_fields": [],
"price_type_suggestions": [],
"amenity_focus": [],
"validation_rules": []
}
# ==================== AIRBNB HOST ====================
if user_role == UserRole.AIRBNB_HOST:
extraction_config["required_fields"] = [
"location", "bedrooms", "bathrooms", "price", "amenities"
]
extraction_config["price_type_suggestions"] = ["nightly", "daily", "weekly"]
extraction_config["amenity_focus"] = ["wifi", "parking", "pool", "kitchen", "ac"]
extraction_config["validation_rules"] = [
"price must be per night (nightly/daily)",
"bedrooms minimum 1",
"bathrooms can be shared"
]
# ==================== AIRBNB GUEST ====================
elif user_role == UserRole.AIRBNB_GUEST:
extraction_config["required_fields"] = ["location", "check_in", "check_out"]
extraction_config["price_type_suggestions"] = ["nightly"]
extraction_config["amenity_focus"] = ["wifi", "kitchen", "parking"]
extraction_config["validation_rules"] = [
"check dates for availability",
"show prices in nightly rates"
]
# ==================== LANDLORD (African) ====================
elif user_role == UserRole.LANDLORD:
extraction_config["required_fields"] = [
"location", "bedrooms", "bathrooms", "price", "price_type", "furnished"
]
extraction_config["price_type_suggestions"] = ["monthly", "yearly"]
extraction_config["amenity_focus"] = [
"furnished", "kitchen", "water", "electricity", "security"
]
extraction_config["validation_rules"] = [
"price must be monthly or yearly",
"specify if furnished/unfurnished",
"include utility info if available",
"bedrooms and bathrooms required"
]
# ==================== RENTER/TENANT (African) ====================
elif user_role in [UserRole.RENTER, UserRole.TENANT]:
extraction_config["required_fields"] = [
"location", "budget", "bedrooms", "price_type"
]
extraction_config["price_type_suggestions"] = ["monthly", "yearly"]
extraction_config["amenity_focus"] = [
"furnished", "security", "water", "electricity", "parking"
]
extraction_config["validation_rules"] = [
"show monthly/yearly prices",
"filter by budget",
"highlight furnished options",
"show security features"
]
# ==================== HOMEOWNER SEEKING ROOMMATE ====================
elif user_role == UserRole.HOMEOWNER_SEEKING_ROOMMATE:
extraction_config["required_fields"] = [
"location", "bedrooms_available", "bathrooms_available", "price", "price_type"
]
extraction_config["price_type_suggestions"] = ["monthly", "yearly"]
extraction_config["amenity_focus"] = [
"furnished", "utilities_included", "kitchen_access", "laundry",
"internet", "parking", "living_room_access"
]
extraction_config["validation_rules"] = [
"price must be monthly or yearly",
"specify which rooms are available",
"describe house/apartment condition",
"list utilities included",
"mention house rules"
]
# ==================== ROOMMATE SEEKER ====================
elif user_role == UserRole.ROOMMATE_SEEKER:
extraction_config["required_fields"] = [
"location", "budget", "move_in_date"
]
extraction_config["price_type_suggestions"] = ["monthly", "yearly"]
extraction_config["amenity_focus"] = [
"furnished", "utilities_included", "kitchen_access", "internet",
"parking", "proximity_to_work"
]
extraction_config["validation_rules"] = [
"show monthly/yearly prices",
"filter by budget",
"check roommate compatibility",
"show lease terms"
]
# ==================== SELLER ====================
elif user_role == UserRole.SELLER:
extraction_config["required_fields"] = [
"location", "bedrooms", "bathrooms", "price", "property_type"
]
extraction_config["price_type_suggestions"] = ["fixed"]
extraction_config["amenity_focus"] = ["land size", "property type", "condition"]
extraction_config["validation_rules"] = [
"price is total sale price",
"property type required (apartment, house, etc)",
"include land/property size if known"
]
# ==================== BUYER ====================
elif user_role == UserRole.BUYER:
extraction_config["required_fields"] = [
"location", "budget", "bedrooms", "property_type"
]
extraction_config["price_type_suggestions"] = []
extraction_config["amenity_focus"] = ["property type", "land size", "condition"]
extraction_config["validation_rules"] = [
"show total sale prices",
"filter by budget range",
"group by property type"
]
logger.info(f"βœ… Extraction config adapted for {user_role}")
return extraction_config
def get_role_context_prompt(self, user_role: str, rental_model: str) -> str:
"""Get AI prompt context based on role"""
prompts = {
UserRole.AIRBNB_HOST: """
You are helping an Airbnb host list their property.
- Focus on: short-stay rental features, nightly rates, guest amenities
- Price type: nightly/daily/weekly
- Emphasize: WiFi, kitchen, parking, cleanliness
""",
UserRole.AIRBNB_GUEST: """
You are helping someone find an Airbnb accommodation.
- Focus on: guest experience, amenities, location convenience
- Price type: show nightly rates
- Emphasize: cleanliness, safety, host responsiveness
""",
UserRole.LANDLORD: """
You are helping an African landlord/property owner list a rental.
- Focus on: long-term rental (monthly/yearly), tenant features, property durability
- Price type: monthly or yearly
- Emphasize: furnished/unfurnished, utilities, security, maintenance
- Include: lease terms, deposit requirements
""",
UserRole.RENTER: """
You are helping a tenant/renter find an apartment or room.
- Focus on: long-term rental suitability, affordability, amenities for living
- Price type: monthly or yearly budget
- Emphasize: security, utilities included, furnished options, commute
- Ask about: move-in date, lease length, budget
""",
UserRole.TENANT: """
You are helping a tenant/renter find an apartment or room.
- Focus on: long-term rental suitability, affordability, amenities for living
- Price type: monthly or yearly budget
- Emphasize: security, utilities included, furnished options, commute
- Ask about: move-in date, lease length, budget
""",
UserRole.SELLER: """
You are helping someone sell a property.
- Focus on: property value, unique features, condition, potential
- Price type: total sale price
- Emphasize: location, size, renovations, investment potential
- Include: property history, legal documents status
""",
UserRole.BUYER: """
You are helping someone find and purchase a property.
- Focus on: property value, investment potential, location
- Price type: show total purchase price
- Emphasize: property condition, neighborhood, future value
- Include: financing options, inspection recommendations
""",
UserRole.HOMEOWNER_SEEKING_ROOMMATE: """
You are helping someone find a roommate to share their home with.
- Focus on: compatibility, house/apartment details, shared spaces
- Price type: monthly or yearly
- Emphasize: house rules, utilities included, available rooms, amenities
- Include: lease terms, deposit, move-in date, roommate preferences
- Ask about: their lifestyle, work schedule, cleanliness standards
""",
UserRole.ROOMMATE_SEEKER: """
You are helping someone find a room to share with a roommate.
- Focus on: affordability, roommate compatibility, location, utilities
- Price type: monthly or yearly budget
- Emphasize: house rules, amenities, commute, lifestyle fit
- Include: move-in date, lease length, deposit requirements
- Ask about: budget, preferred location, work/study location, lifestyle
"""
}
return prompts.get(user_role, "")
# ==================== EXAMPLE USAGE ====================
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
engine = RoleBasedInferenceEngine()
# Test cases
test_cases = [
# Airbnb host
("I'm a host on Airbnb and want to list my apartment in Lagos", "Lagos"),
# Airbnb guest
("I'm looking for accommodation on Airbnb in Accra next week", "Accra"),
# African landlord
("I'm a landlord in Cotonou with a 2-bedroom apartment for monthly rent", "Cotonou"),
# African tenant
("I'm looking to rent a furnished room in Nairobi, my budget is 30000 KES per month", "Nairobi"),
# Homeowner seeking roommate
("My house in Lagos is too big for just me. I have 2 extra bedrooms and want to share", "Lagos"),
# Roommate seeker
("I'm looking for a room to share in Accra, somewhere near my workplace", "Accra"),
# Seller
("I want to sell my house in Lagos for 50 million NGN", "Lagos"),
# Buyer
("I'm looking to buy a 3-bedroom apartment in Cape Town", "Cape Town"),
]
print("\n" + "="*70)
print("🧠 ROLE-BASED INFERENCE ENGINE TEST")
print("="*70 + "\n")
for message, location in test_cases:
print(f"πŸ“ Message: {message}")
print(f"πŸ“ Location: {location}\n")
state = {"location": location}
listing_type, confidence = engine.infer_listing_type(state, message)
print(f"βœ… Listing Type: {listing_type} (confidence: {confidence:.0%})")
config = engine.adapt_field_extraction(state, message)
print(f"πŸ“‹ Required fields: {', '.join(config['required_fields'])}")
print(f"πŸ’° Price types: {', '.join(config['price_type_suggestions'])}")
prompt = engine.get_role_context_prompt(config['user_role'], config['rental_model'])
print(f"🎯 AI Context:\n{prompt}")
print("-" * 70 + "\n")