Vsai2004/AI_Customer_Support_Bot
0
1import streamlit as st2import os3import time4from datetime import datetime5import pandas as pd6import tempfile7import logging8from typing import Optional, List9import random10 11# Hugging Face Spaces compatible imports12try:13 import pdfplumber14 from langchain_text_splitters import RecursiveCharacterTextSplitter15 from langchain_huggingface import HuggingFaceEmbeddings16 from langchain_community.vectorstores import FAISS17 from langchain_core.documents import Document18 from transformers import AutoTokenizer, AutoModelForQuestionAnswering, pipeline19except ImportError as e:20 st.error(f"Missing dependency: {e}")21 st.stop()22 23logging.basicConfig(24 level=logging.INFO,25 format='%(asctime)s - %(levelname)s - %(message)s'26)27 28# Configure Streamlit page29st.set_page_config(30 page_title="AI Customer Support Bot",31 page_icon="๐ค",32 layout="wide"33)34 35# Simplified SupportBotAgent for HF Spaces36class SupportBotAgent:37 def __init__(self, document_content: str, document_name: str = "document"):38 self.document_name = document_name39 self.similarity_threshold = 1.5 # Adjusted threshold for FAISS distance metric40 self.qa_confidence_threshold = 0.3 # Increased for better answer quality41 self.max_context_length = 1024 # Increased context for better answers42 try:43 # Initialize embeddings with caching for HF Spaces44 self.embeddings = HuggingFaceEmbeddings(45 model_name="sentence-transformers/all-MiniLM-L6-v2",46 cache_folder="./models" 47 )48 self.vectorstore = self._process_document(document_content)49 self.qa_pipeline = self._setup_qa_chain()50 st.success(f"Bot initialized with {document_name}")51 except Exception as e:52 st.error(f"Error initializing bot: {str(e)}")53 raise54 55 def _process_document(self, content: str):56 # Split content into chunks57 texts = [section.strip() for section in content.split("\n\n") if section.strip()]58 if not texts:59 texts = [line.strip() for line in content.split("\n") if line.strip()]60 61 documents = [Document(page_content=text) for text in texts if text.strip()]62 63 text_splitter = RecursiveCharacterTextSplitter(64 chunk_size=500,65 chunk_overlap=150,66 length_function=len,67 separators=["\n\n", "\n", ". ", "? ", "! ", " ", ""]68 )69 split_docs = text_splitter.split_documents(documents)70 71 # Create vector store72 vectorstore = FAISS.from_documents(split_docs, self.embeddings)73 return vectorstore74 75 def _setup_qa_chain(self):76 try:77 model_name = "distilbert-base-uncased-distilled-squad"78 qa_pipeline = pipeline(79 "question-answering",80 model=model_name,81 cache_dir="./models"82 )83 return qa_pipeline84 except Exception as e:85 st.warning(f"QA pipeline setup failed: {e}")86 return None87 88 def answer_query(self, query: str) -> dict:89 try:90 docs_and_scores = self.vectorstore.similarity_search_with_score(query, k=5)91 92 if not docs_and_scores:93 return {94 "answer": "I don't have enough information to answer that question.",95 "context_used": None96 }97 98 best_doc, best_score = docs_and_scores[0]99 100 # Check if score is too high (poor match)101 if best_score > 1.5:102 return {103 "answer": f"I don't have specific information about '{query}' in my knowledge base.",104 "context_used": None105 }106 107 # Prepare context108 context = best_doc.page_content109 110 # Add additional context from other relevant chunks111 if len(docs_and_scores) > 1:112 for doc, score in docs_and_scores[1:4]:113 if score < 2.0: # Include reasonably similar chunks114 context += " " + doc.page_content115 116 # Limit context length117 if len(context) > self.max_context_length:118 context = context[:self.max_context_length]119 120 # Try QA pipeline121 if self.qa_pipeline:122 try:123 qa_result = self.qa_pipeline(question=query, context=context)124 if qa_result["score"] > self.qa_confidence_threshold and qa_result["answer"].strip():125 answer = qa_result["answer"].strip()126 else:127 answer = context.strip()128 except:129 answer = context.strip()130 else:131 answer = context.strip()132 133 return {134 "answer": answer,135 "context_used": context136 }137 138 except Exception as e:139 return {140 "answer": f"Error processing question: {str(e)}",141 "context_used": None142 }143 144 def get_feedback(self, answer: str) -> str:145 if not isinstance(answer, str) or not answer.strip():146 return "not helpful"147 148 answer = answer.strip()149 150 if "I don't have" in answer or "Error" in answer:151 return "not helpful"152 elif len(answer) < 30:153 return "too vague"154 elif len(answer) > 200:155 return random.choice(["good", "too vague"])156 else:157 return random.choice(["good", "good", "too vague", "not helpful"])158 159 def adjust_response(self, query: str, response: dict, feedback: str) -> dict:160 try:161 if feedback == "too vague" and response["context_used"]:162 if "Additional Info:" not in response["answer"]:163 docs = self.vectorstore.similarity_search(query, k=2)164 extra_context = "\n".join([doc.page_content for doc in docs[:1]])165 response["answer"] += f"\n\nAdditional Info:\n{extra_context[:150]}..."166 167 elif feedback == "not helpful":168 rephrased_query = f"Please explain in detail: {query}"169 return self.answer_query(rephrased_query)170 171 return response172 except:173 return response174 175# Initialize session state176if 'bot' not in st.session_state:177 st.session_state.bot = None178if 'chat_history' not in st.session_state:179 st.session_state.chat_history = []180 181# Default sample documents182SAMPLE_DOCUMENTS = {183 "Customer Support FAQ": """184Resetting Your Password185To reset your password, go to the login page and click "Forgot Password." Enter your email address and follow the link sent to your email inbox.186 187Refund Policy188We offer full refunds within 30 days of purchase for any reason. To request a refund, contact our support team at support@example.com with your order number.189 190Contacting Support191Our support team is available to help you with any questions. Email us at support@example.com or call 1-800-555-1234 during business hours (9 AM - 5 PM EST).192 193Account Management194You can update your account information by logging into your account dashboard. From there, you can change your email, update billing information, and manage preferences.195 196Technical Support197If you're experiencing technical issues, first try clearing your browser cache and cookies. Make sure you're using a supported browser (Chrome, Firefox, Safari, or Edge).198""",199 200 "API Documentation": """201API Documentation202Our REST API allows developers to integrate with our platform. Use the base URL https://api.example.com/v1/ for all requests.203 204Rate Limiting205API calls are limited to 1000 requests per hour per API key. Exceeded limits return a 429 status code.206 207Error Handling208API errors return standard HTTP status codes. 400 for bad requests, 401 for unauthorized, 404 for not found, and 500 for server errors.209 210SDK Support211We provide official SDKs for Python, JavaScript, and Java. Community-maintained SDKs are available for other languages.212 213Webhooks214Set up webhooks to receive real-time notifications about events. Configure webhook URLs in your dashboard.215""",216 217 "Product Guide": """218Getting Started219Welcome to our platform! This guide will help you get started quickly and efficiently.220 221Installation222Download the software from our website and run the installer. Follow the on-screen instructions to complete the setup.223 224Basic Features225Our platform includes document management, collaboration tools, and automated workflows. Navigate using the main menu on the left side.226 227Advanced Features228Power users can access advanced features through the settings menu. This includes API access, custom integrations, and bulk operations.229 230Troubleshooting231Common issues include login problems, slow performance, and sync errors. Most issues can be resolved by refreshing your browser or clearing the cache.232"""233}234 235def main():236 st.title("๐ค AI Customer Support Bot")237 st.markdown("### Upload a document or select a sample to get started!")238 239 # Sidebar240 with st.sidebar:241 st.header("Configuration")242 243 # Document selection244 st.subheader("Choose Document Source")245 246 option = st.radio(247 "Select input method:",248 ["Sample Documents", "Upload File", "Paste Text"]249 )250 251 document_content = None252 document_name = None253 254 if option == "Sample Documents":255 selected_doc = st.selectbox("Choose a sample:", list(SAMPLE_DOCUMENTS.keys()))256 if st.button("Load Sample Document"):257 document_content = SAMPLE_DOCUMENTS[selected_doc]258 document_name = selected_doc259 260 elif option == "Upload File":261 uploaded_file = st.file_uploader("Upload a text or PDF file", type=['txt', 'pdf'])262 if uploaded_file and st.button("Process Upload"):263 if uploaded_file.type == "application/pdf":264 try:265 with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:266 tmp_file.write(uploaded_file.getbuffer())267 with pdfplumber.open(tmp_file.name) as pdf:268 document_content = "\n".join([page.extract_text() for page in pdf.pages if page.extract_text()])269 document_name = uploaded_file.name270 except Exception as e:271 st.error(f"Error processing PDF: {e}")272 else:273 document_content = uploaded_file.read().decode('utf-8')274 document_name = uploaded_file.name275 276 elif option == "Paste Text":277 pasted_text = st.text_area("Paste your document content here:", height=200)278 if pasted_text and st.button("Process Text"):279 document_content = pasted_text280 document_name = "Pasted Document"281 282 # Initialize bot if we have content283 if document_content and document_name:284 try:285 with st.spinner("Initializing AI models..."):286 st.session_state.bot = SupportBotAgent(document_content, document_name)287 except Exception as e:288 st.error(f"Failed to initialize bot: {e}")289 290 # Bot status291 st.subheader("Bot Status")292 if st.session_state.bot:293 st.success("โ
Bot is ready!")294 else:295 st.warning("โ ๏ธ Please load a document first")296 297 # Clear history298 if st.button("Clear Chat History"):299 st.session_state.chat_history = []300 st.rerun()301 302 # Main chat interface303 col1, col2 = st.columns([2, 1])304 305 with col1:306 st.header("Chat Interface")307 308 # Sample questions309 if st.session_state.bot:310 st.subheader("Try these example questions:")311 examples = [312 "How do I reset my password?",313 "What's the refund policy?",314 "How do I contact support?",315 "What are the API rate limits?",316 "How do I get started?"317 ]318 319 cols = st.columns(len(examples))320 for i, example in enumerate(examples):321 if cols[i].button(example, key=f"example_{i}"):322 st.session_state.current_query = example323 324 # Query input325 query = st.text_input(326 "Ask your question:",327 value=st.session_state.get('current_query', ''),328 placeholder="Type your question here...",329 disabled=st.session_state.bot is None330 )331 332 if st.button("Ask Question", disabled=not query or st.session_state.bot is None):333 with st.spinner("Processing your question..."):334 start_time = time.time()335 336 # Get response with feedback loop337 response = st.session_state.bot.answer_query(query)338 339 for _ in range(2): # Max 2 feedback iterations340 feedback = st.session_state.bot.get_feedback(response['answer'])341 if feedback == "good":342 break343 response = st.session_state.bot.adjust_response(query, response, feedback)344 345 processing_time = time.time() - start_time346 347 # Add to history348 st.session_state.chat_history.append({349 'timestamp': datetime.now(),350 'query': query,351 'response': response['answer'],352 'processing_time': processing_time353 })354 355 # Clear the input356 st.session_state.current_query = ""357 st.rerun()358 359 # Display chat history360 if st.session_state.chat_history:361 st.subheader("Conversation History")362 363 for i, chat in enumerate(reversed(st.session_state.chat_history[-5:])): # Show last 5364 with st.expander(f"Q: {chat['query'][:50]}...", expanded=i==0):365 st.write(f"**Question:** {chat['query']}")366 st.write(f"**Answer:** {chat['response']}")367 st.caption(f"Response time: {chat['processing_time']:.2f}s | {chat['timestamp'].strftime('%H:%M:%S')}")368 369 with col2:370 st.header("Statistics")371 372 if st.session_state.chat_history:373 total_queries = len(st.session_state.chat_history)374 avg_time = sum(chat['processing_time'] for chat in st.session_state.chat_history) / total_queries375 376 st.metric("Total Questions", total_queries)377 st.metric("Avg Response Time", f"{avg_time:.2f}s")378 379 # Recent queries380 st.subheader("Recent Questions")381 for chat in st.session_state.chat_history[-3:]:382 st.write(f"โข {chat['query'][:40]}...")383 else:384 st.info("No conversations yet. Ask a question to get started!")385 386 # Model info387 st.subheader("Model Information")388 st.write("**Embedding:** all-MiniLM-L6-v2")389 st.write("**QA Model:** DistilBERT")390 st.write("**Vector Store:** FAISS")391 392if __name__ == "__main__":393 main()394 395 