diff --git a/chatbot-api/knowledge.py b/chatbot-api/knowledge.py index f76bb1d..6960cf4 100644 --- a/chatbot-api/knowledge.py +++ b/chatbot-api/knowledge.py @@ -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 diff --git a/chatbot-api/main.py b/chatbot-api/main.py index 9ed6ca0..64ccae9 100644 --- a/chatbot-api/main.py +++ b/chatbot-api/main.py @@ -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) diff --git a/chatbot-api/rocketchat.py b/chatbot-api/rocketchat.py index 6f5e247..d4466f4 100644 --- a/chatbot-api/rocketchat.py +++ b/chatbot-api/rocketchat.py @@ -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}") diff --git a/src/components/ChatBubble.css b/src/components/ChatBubble.css index a1282b0..ed2e535 100644 --- a/src/components/ChatBubble.css +++ b/src/components/ChatBubble.css @@ -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; + } } diff --git a/src/components/ContactForm.css b/src/components/ContactForm.css index 84c4cb6..ace083b 100644 --- a/src/components/ContactForm.css +++ b/src/components/ContactForm.css @@ -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; + } } diff --git a/src/pages/PricingPage.css b/src/pages/PricingPage.css index eeae1ad..3ae40a8 100644 --- a/src/pages/PricingPage.css +++ b/src/pages/PricingPage.css @@ -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 {