CoolFace
Apppublic

Vsai2004/AI_Customer_Support_Bot

sourceHugging Facemitupdated 10mo agoView on Hugging Face
0likes
app.py395 linesDownload Raw Back to root
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