Spaces:
Sleeping
Sleeping
File size: 5,532 Bytes
04c71fb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | import os
import requests
from src.rag.router import classify_query, extract_filters
from src.rag.retriever import retrieve_faq, load_vector_store
from src.rag.searcher import (
search_properties, get_leases_expiring,
get_leases_vacant_or_pending
)
GROQ_MODEL = "llama-3.3-70b-versatile"
SYSTEM_PROMPT = """You are a helpful AI assistant for a resale real estate business in Mumbai, India.
RULES:
- Answer ONLY from the data provided to you in this prompt.
- Never invent prices, features, lease status, or property details.
- If you cannot find the info, say: "I can't find this in our system right now. Please check with our sales team."
- Be friendly, short, and clear.
- Use Indian number formatting: ₹1,20,00,000 (1.2 crore) or ₹85 lakh (₹85,00,000).
- After property listings, always add: "For a site visit or more details, WhatsApp us or contact our sales team."
- After FAQ answers, optionally add: "If you need more info, feel free to ask."
- For manager queries, be factual and structured with tables.
"""
def _fmt_price(price_inr: float) -> str:
"""Format price in Indian notation."""
if price_inr >= 10000000:
return f"₹{price_inr/10000000:.2f} Cr"
else:
return f"₹{price_inr/100000:.1f} L"
def format_properties(props: list[dict]) -> str:
if not props:
return "We don't have any properties matching your filters in current listings."
lines = []
for i, p in enumerate(props, 1):
lines.append(
f"{i}. **{p['title']}**\n"
f" • {p['bhk']} BHK | {p['property_type']} | {p['area_sqft']} sq ft\n"
f" • Price: {_fmt_price(p['price_inr'])} | Floor: {p['floor']}/{p['total_floors']}\n"
f" • Location: {p['location']}, {p['society']}\n"
f" • Furnishing: {p['furnishing']} | Parking: {p['parking']} | Grade: {p['condition_grade']}\n"
f" • Amenities: {p['amenities']}"
)
return "\n\n".join(lines)
def format_lease_table(records: list[dict]) -> str:
if not records:
return "No records found for this query."
lines = [
"| Prop ID | Property | Location | Rent/mo | Status | Lease End | Follow-up |",
"|---------|----------|----------|---------|--------|-----------|-----------|"
]
for r in records:
rent = f"₹{r['monthly_rent']:,}" if r.get('monthly_rent') else "—"
lease_end = r.get('lease_end') or "—"
lines.append(
f"| {r['property_id']} | {r['title']} ({r['bhk']}BHK) "
f"| {r['location']} | {rent} "
f"| {r['lease_status']} | {lease_end} | {r['followup_person']} |"
)
return "\n".join(lines)
def call_groq(api_key: str, messages: list) -> str:
url = "https://api.groq.com/openai/v1/chat/completions"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"model": GROQ_MODEL,
"messages": messages,
"max_tokens": 1024,
"temperature": 0.3
}
resp = requests.post(url, json=payload, headers=headers, timeout=30)
if resp.status_code != 200:
raise ValueError(f"Groq API error {resp.status_code}: {resp.text[:200]}")
return resp.json()["choices"][0]["message"]["content"]
def chat(query: str, role: str = "customer", history: list = None) -> dict:
query_type = classify_query(query, role)
context = ""
properties = []
lease_records = []
if query_type == "property_filter":
filters = extract_filters(query)
properties = search_properties(**filters)
prop_text = format_properties(properties)
context = f"PROPERTY INVENTORY RESULTS:\n{prop_text}"
elif query_type == "lease" and role == "manager":
q_lower = query.lower()
if "vacant" in q_lower or "expired" in q_lower or "pending" in q_lower:
lease_records = get_leases_vacant_or_pending()
else:
days = 30
if "15 days" in q_lower:
days = 15
elif "week" in q_lower:
days = 7
elif "month" in q_lower or "30 days" in q_lower:
days = 30
elif "60 days" in q_lower or "2 months" in q_lower:
days = 60
lease_records = get_leases_expiring(days)
table = format_lease_table(lease_records)
context = f"LEASE DATA:\n{table}"
elif query_type == "faq":
faq_context = retrieve_faq(query)
context = f"FAQ KNOWLEDGE BASE:\n{faq_context}"
# Build messages for Groq (OpenAI-compatible)
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
if history:
for h in history[-6:]:
msg_role = "user" if h["role"] == "user" else "assistant"
messages.append({"role": msg_role, "content": h["content"]})
user_content = f"CONTEXT:\n{context}\n\nUSER QUERY: {query}"
messages.append({"role": "user", "content": user_content})
try:
api_key = os.environ.get("GROQ_API_KEY", "").strip()
if not api_key:
raise ValueError(
"GROQ_API_KEY is not set. Go to Space Settings → Repository Secrets and add GROQ_API_KEY."
)
reply = call_groq(api_key, messages)
except Exception as e:
reply = f"⚠️ AI unavailable: {str(e)[:200]}"
return {
"reply": reply,
"query_type": query_type,
"properties": properties,
"lease_records": lease_records,
}
|