Fix chatbot: lead persistence, CRM creation, RC delivery, mobile UI
- Store lead info in Redis session meta so bot remembers name across messages - Create Twenty CRM lead immediately from form data (bypass tool-call flow) - Store room→session reverse mapping in Redis for RC webhook delivery - Update system prompt: don't re-ask for form-provided info - Fix "Most Popular" badge clipped on mobile (overflow: visible) - Fix contact form inputs overflowing on small screens (box-sizing) - Reduce chat tooltip size on mobile to avoid overlapping content Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e78d6dc1c6
commit
8e73d77abc
6 changed files with 59 additions and 13 deletions
|
|
@ -8,7 +8,8 @@ RULES:
|
|||
- Respond in the same language the visitor uses. Default language: British English
|
||||
- Keep responses under 3 sentences unless more detail is genuinely needed
|
||||
- After 3-4 exchanges of genuine interest → suggest booking a free consultation
|
||||
- Naturally collect visitor's name, email, company — don't ask all at once, weave into conversation
|
||||
- If the visitor's name, email, or company was already provided via the form (shown in system context as "[System: The visitor has introduced themselves...]"), you already have it — DO NOT ask for it again. Use that info directly.
|
||||
- For visitors without form data, naturally collect name, email, company — don't ask all at once, weave into conversation
|
||||
- When you have collected name + email + company + their need, use the capture_lead tool
|
||||
- After capturing a lead, if the visitor reveals additional useful info (job title, phone, city, budget, timeline, specific requirements), use the update_lead tool to enrich their profile
|
||||
- Be warm, professional, and concise. No waffle
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from security import (
|
|||
)
|
||||
from llm import get_ai_response
|
||||
from rocketchat import get_or_create_room, send_message
|
||||
from twenty_crm import create_lead_in_crm
|
||||
|
||||
app = FastAPI(title="AImpress Chatbot API", docs_url=None, redoc_url=None)
|
||||
|
||||
|
|
@ -80,22 +81,43 @@ async def chat(req: ChatRequest):
|
|||
# Store user message and get conversation history
|
||||
messages = await store_messages(req.session_id, "user", message)
|
||||
|
||||
# If lead info provided, prepend context for LLM
|
||||
if req.lead and req.lead.name:
|
||||
lead_context = f"[System: The visitor has introduced themselves. Name: {req.lead.name}"
|
||||
if req.lead.email:
|
||||
lead_context += f", Email: {req.lead.email}"
|
||||
if req.lead.company:
|
||||
lead_context += f", Company: {req.lead.company}"
|
||||
lead_context += ". Use this info naturally — greet them by name. Don't re-ask for info you already have.]"
|
||||
messages = [{"role": "user", "content": lead_context}, {"role": "assistant", "content": "Understood."}] + messages
|
||||
|
||||
# Load session meta (twenty_person_id, etc.)
|
||||
# Load session meta (twenty_person_id, lead info, etc.)
|
||||
r = await get_redis()
|
||||
meta_raw = await r.get(f"chat:meta:{req.session_id}")
|
||||
session_meta = json.loads(meta_raw) if meta_raw else {}
|
||||
session_meta["page_context"] = req.page_context
|
||||
|
||||
# If lead info provided (first message), save to session meta + create CRM lead
|
||||
if req.lead and req.lead.name and "lead" not in session_meta:
|
||||
lead_info = {"name": req.lead.name}
|
||||
if req.lead.email:
|
||||
lead_info["email"] = req.lead.email
|
||||
if req.lead.company:
|
||||
lead_info["company"] = req.lead.company
|
||||
session_meta["lead"] = lead_info
|
||||
|
||||
# Create lead in Twenty CRM immediately from form data
|
||||
person_id = await create_lead_in_crm(
|
||||
name=req.lead.name,
|
||||
email=req.lead.email or "",
|
||||
company=req.lead.company or "",
|
||||
need=message,
|
||||
page_context=req.page_context or "/",
|
||||
)
|
||||
if person_id:
|
||||
session_meta["twenty_person_id"] = person_id
|
||||
|
||||
# Always prepend lead context from session meta (persists across messages)
|
||||
stored_lead = session_meta.get("lead")
|
||||
if stored_lead:
|
||||
lead_context = f"[System: The visitor has introduced themselves. Name: {stored_lead['name']}"
|
||||
if stored_lead.get("email"):
|
||||
lead_context += f", Email: {stored_lead['email']}"
|
||||
if stored_lead.get("company"):
|
||||
lead_context += f", Company: {stored_lead['company']}"
|
||||
lead_context += ". Use this info naturally — greet them by name. Don't re-ask for info you already have.]"
|
||||
messages = [{"role": "user", "content": lead_context}, {"role": "assistant", "content": "Understood."}] + messages
|
||||
|
||||
# Get AI response
|
||||
try:
|
||||
reply, lead_data = await get_ai_response(messages, session_meta)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import httpx
|
||||
import logging
|
||||
from config import settings
|
||||
from security import get_redis
|
||||
|
||||
logger = logging.getLogger("rocketchat")
|
||||
|
||||
|
|
@ -55,6 +56,9 @@ async def get_or_create_room(session_id: str, visitor_name: str = "Website Visit
|
|||
room_id = resp.json().get("room", {}).get("_id")
|
||||
if room_id:
|
||||
_room_cache[session_id] = room_id
|
||||
# Store reverse mapping for webhook delivery
|
||||
r = await get_redis()
|
||||
await r.set(f"chat:room_session:{room_id}", session_id, ex=86400)
|
||||
return room_id
|
||||
except Exception as e:
|
||||
logger.error(f"RC get_or_create_room error: {e}")
|
||||
|
|
|
|||
|
|
@ -122,4 +122,14 @@
|
|||
bottom: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
.chat-bubble__greeting {
|
||||
max-width: 170px;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.chat-bubble__greeting p {
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@
|
|||
}
|
||||
|
||||
.form-group input {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 12px;
|
||||
|
|
@ -151,4 +153,9 @@
|
|||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
padding: 0.75rem 0.9rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
transition: border-color 0.3s, box-shadow 0.4s, transform 0.35s;
|
||||
}
|
||||
|
||||
|
|
@ -138,6 +138,8 @@
|
|||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
pointer-events: none;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.retainer-card:hover {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue