| import os |
| import warnings |
| from typing import Dict, Tuple |
|
|
| |
| warnings.filterwarnings("ignore", category=FutureWarning, module="google.generativeai") |
|
|
| import google.generativeai as genai |
| from backend.memory import ConversationMemory |
| from backend.knowledge_base import KnowledgeBase |
| from backend.intent_detector import IntentDetector |
| from backend.lead_scorer import LeadScorer |
| from backend.storage import Storage |
|
|
|
|
| class ConversationEngine: |
| """Main conversation orchestration engine.""" |
| |
| def __init__(self): |
| """Initialize conversation engine.""" |
| self.api_key = os.getenv("GEMINI_API_KEY") |
| if self.api_key: |
| genai.configure(api_key=self.api_key) |
| |
| self.model = genai.GenerativeModel("gemini-2.5-flash") if self.api_key else None |
| |
| self.memory = ConversationMemory(max_messages=10) |
| self.knowledge_base = KnowledgeBase("data/faq.json") |
| self.intent_detector = IntentDetector(api_key=self.api_key) |
| self.lead_scorer = LeadScorer() |
| self.storage = Storage("data") |
| |
| |
| self.current_intent = "unknown" |
| self.lead_data = {} |
| self.support_data = {} |
| self.lead_questions = [ |
| "What type of business are you in or what industry?", |
| "What's the main problem you're trying to solve?", |
| "What's your approximate budget range?", |
| "What's the best email to reach you?" |
| ] |
| self.lead_question_index = 0 |
| |
| self.support_questions = [ |
| "Could you describe the issue you're experiencing?", |
| "Do you have an order ID or reference number?" |
| ] |
| self.support_question_index = 0 |
| |
| def process_message(self, user_message: str) -> Tuple[str, Dict]: |
| """ |
| Process user message and generate response. |
| |
| Args: |
| user_message: User's input |
| |
| Returns: |
| Tuple of (response, metadata) |
| """ |
| |
| self.memory.add_message("user", user_message) |
| |
| |
| intent, confidence = self.intent_detector.detect_intent(user_message) |
| |
| if self.current_intent == "unknown" and confidence > 0.6: |
| self.current_intent = intent |
| self.memory.set_intent(intent) |
| |
| |
| if not self.memory.user_name: |
| name = self.intent_detector.extract_name(user_message) |
| if name: |
| self.memory.set_user_name(name) |
| |
| |
| if self.current_intent == "lead": |
| response, metadata = self._handle_lead_flow(user_message) |
| elif self.current_intent == "support": |
| response, metadata = self._handle_support_flow(user_message) |
| else: |
| response, metadata = self._handle_general_flow(user_message) |
| |
| |
| self.memory.add_message("assistant", response) |
| |
| return response, metadata |
| |
| def _handle_lead_flow(self, user_message: str) -> Tuple[str, Dict]: |
| """Handle lead qualification flow.""" |
| metadata = {"intent": "lead", "type": ""} |
| |
| |
| email = self.intent_detector.extract_email(user_message) |
| if email: |
| self.lead_data["email"] = email |
| |
| |
| if self.lead_question_index < len(self.lead_questions): |
| question = self.lead_questions[self.lead_question_index] |
| self.lead_question_index += 1 |
| |
| |
| if self.lead_question_index == 1: |
| self.lead_data["business_type"] = user_message |
| metadata["type"] = "business_info" |
| elif self.lead_question_index == 2: |
| self.lead_data["problem"] = user_message |
| metadata["type"] = "problem_info" |
| elif self.lead_question_index == 3: |
| self.lead_data["budget"] = user_message |
| metadata["type"] = "budget_info" |
| elif self.lead_question_index == 4: |
| if email: |
| self.lead_data["email"] = email |
| metadata["type"] = "contact_info" |
| |
| |
| if self.lead_question_index >= len(self.lead_questions): |
| response = self._finalize_lead() |
| metadata["lead_finalized"] = True |
| |
| |
| self.lead_data["conversation"] = self.memory.get_context() |
| self.lead_data["user_name"] = self.memory.user_name |
| lead_score = self.lead_scorer.score_lead(self.memory.get_context(), self.lead_data) |
| self.lead_data["scoring"] = lead_score |
| |
| lead_id = self.storage.save_lead(self.lead_data) |
| metadata["lead_id"] = lead_id |
| else: |
| response = f"{question}" |
| |
| return response, metadata |
| else: |
| response = f"{self.lead_questions[-1]}" |
| return response, metadata |
| |
| def _handle_support_flow(self, user_message: str) -> Tuple[str, Dict]: |
| """Handle support ticket flow.""" |
| metadata = {"intent": "support", "type": ""} |
| |
| |
| if self.support_question_index < len(self.support_questions): |
| question = self.support_questions[self.support_question_index] |
| self.support_question_index += 1 |
| |
| if self.support_question_index == 1: |
| self.support_data["issue_description"] = user_message |
| metadata["type"] = "issue_info" |
| elif self.support_question_index == 2: |
| self.support_data["reference_id"] = user_message |
| metadata["type"] = "reference_info" |
| |
| if self.support_question_index >= len(self.support_questions): |
| response = self._finalize_support() |
| metadata["ticket_finalized"] = True |
| |
| |
| self.support_data["conversation"] = self.memory.get_context() |
| self.support_data["user_name"] = self.memory.user_name |
| |
| ticket_id = self.storage.save_support_ticket(self.support_data) |
| metadata["ticket_id"] = ticket_id |
| else: |
| response = f"{question}" |
| |
| return response, metadata |
| else: |
| response = f"{self.support_questions[-1]}" |
| return response, metadata |
| |
| def _handle_general_flow(self, user_message: str) -> Tuple[str, Dict]: |
| """Handle general queries with FAQ and LLM.""" |
| metadata = {"intent": "general", "used_faq": False} |
| |
| |
| faq_result = self.knowledge_base.retrieve_answer(user_message) |
| if faq_result: |
| metadata["used_faq"] = True |
| return faq_result["answer"], metadata |
| |
| |
| if self.model: |
| return self._generate_with_gemini(user_message, metadata) |
| else: |
| return self._generate_grounded_response(user_message, metadata) |
| |
| def _generate_with_gemini(self, user_message: str, metadata: Dict) -> Tuple[str, Dict]: |
| """Generate response using Gemini API.""" |
| try: |
| context = self.memory.get_formatted_context() |
| user_name = f" {self.memory.user_name}" if self.memory.user_name else "" |
| |
| prompt = f"""You are a helpful and professional customer support AI assistant for a website support + lead qualification service. |
| |
| IMPORTANT RULES: |
| 1. Be helpful, friendly, and natural |
| 2. NEVER make up information or hallucinate details |
| 3. If you don't know something, say "I don't have that information, but I can connect you with our support team." |
| 4. Keep answers concise (2-3 sentences max for general queries) |
| 5. If the user seems interested in your services, ask about their needs |
| |
| User name: {user_name if user_name else "Visitor"} |
| |
| Previous conversation: |
| {context} |
| |
| Current user message: {user_message} |
| |
| Respond conversationally and helpfully. Keep your response brief and natural.""" |
| |
| response = self.model.generate_content(prompt, safety_settings=[ |
| {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}, |
| {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"}, |
| {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"}, |
| {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"}, |
| ]) |
| |
| return response.text, metadata |
| |
| except Exception as e: |
| print(f"Error generating response: {e}") |
| return self._generate_grounded_response(user_message, metadata) |
| |
| def _generate_grounded_response(self, user_message: str, metadata: Dict) -> Tuple[str, Dict]: |
| """Generate grounded response without API.""" |
| response = "Thank you for your message. To better assist you, could you tell me more about what you're looking for? Are you interested in our services, or do you have a support question?" |
| return response, metadata |
| |
| def _finalize_lead(self) -> str: |
| """Generate lead finalization message.""" |
| user_name = self.memory.user_name or "there" |
| return f"Thank you for the information! I've captured your details. One of our specialists will reach out to you shortly to discuss how we can help your {self.lead_data.get('business_type', 'business')} succeed. We appreciate your interest!" |
| |
| def _finalize_support(self) -> str: |
| """Generate support ticket finalization message.""" |
| user_name = self.memory.user_name or "there" |
| return f"Thank you for providing those details. Your support ticket has been created and our team will investigate your issue. You'll receive an update shortly via email. We appreciate your patience!" |
| |
| def reset(self): |
| """Reset conversation state for new session.""" |
| self.memory.clear() |
| self.current_intent = "unknown" |
| self.lead_data = {} |
| self.support_data = {} |
| self.lead_question_index = 0 |
| self.support_question_index = 0 |
| |
| def get_debug_info(self) -> Dict: |
| """Get debug information about current session.""" |
| return { |
| "current_intent": self.current_intent, |
| "user_name": self.memory.user_name, |
| "lead_data": self.lead_data, |
| "support_data": self.support_data, |
| "message_count": len(self.memory.get_context()), |
| "memory_summary": self.memory.get_summary() |
| } |
|
|