Spaces:
Sleeping
Sleeping
| import os | |
| from dotenv import load_dotenv | |
| import gradio as gr | |
| from langchain_community.document_loaders import CSVLoader, PyPDFLoader | |
| from langchain_text_splitters import RecursiveCharacterTextSplitter | |
| from langchain_community.vectorstores import FAISS | |
| from langchain_huggingface import HuggingFaceEmbeddings, HuggingFaceEndpoint, ChatHuggingFace | |
| from langchain_core.messages import SystemMessage, HumanMessage, AIMessage | |
| load_dotenv() | |
| # Initialize the LangChain Hugging Face Model | |
| llm = HuggingFaceEndpoint( | |
| model="mistralai/Mistral-7B-Instruct-v0.2:featherless-ai", | |
| task="text-generation", | |
| max_new_tokens=512, | |
| huggingfacehub_api_token=os.getenv("HF_TOKEN") | |
| ) | |
| chat_model = ChatHuggingFace(llm=llm) | |
| def process_file(files): | |
| all_documents = [] | |
| # 1. Loop through all uploaded files and load them | |
| for file in files: | |
| if file.name.endswith('.csv'): | |
| loader = CSVLoader(file.name) | |
| elif file.name.endswith('.pdf'): | |
| loader = PyPDFLoader(file.name) | |
| else: | |
| continue # Skip unsupported formats silently | |
| documents = loader.load() | |
| all_documents.extend(documents) | |
| if not all_documents: | |
| return gr.update(value="❌ No valid CSV or PDF files found.", visible=True) | |
| # 2. Chunk all documents together | |
| text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) | |
| chunks = text_splitter.split_documents(all_documents) | |
| # 3. Embed and store in the Vector Database | |
| embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") | |
| vector_store = FAISS.from_documents(chunks, embeddings) | |
| # We return the UI update AND the newly created vector store | |
| return gr.update(value=f"✅ Database Ready! Processed {len(chunks)} chunks from {len(files)} files.", visible=True), vector_store | |
| def respond(message, history, system_prompt, vector_store): | |
| if vector_store is None: | |
| return "⚠️ Please upload a CSV or PDF file first!" | |
| # 1. Safely extract the current message string | |
| # Gradio might pass 'message' as a string, a dict, or a list of dicts. | |
| user_text = "" | |
| if isinstance(message, str): | |
| user_text = message | |
| elif isinstance(message, dict) and "text" in message: | |
| user_text = message["text"] | |
| elif isinstance(message, list): | |
| for item in message: | |
| if isinstance(item, dict) and item.get("type") == "text": | |
| user_text += item.get("text", "") | |
| # Retrieve top 3 most relevant chunks from FAISS | |
| relevant_docs = vector_store.similarity_search(user_text, k=3) | |
| context = "\n".join([doc.page_content for doc in relevant_docs]) | |
| messages = [] | |
| # Combine the user's dynamic System Prompt with the strict RAG instructions | |
| full_system_prompt = ( | |
| f"{system_prompt}\n\n" | |
| f"CONTEXT:\n{context}" | |
| ) | |
| messages.append(SystemMessage(content=full_system_prompt)) | |
| # 2. Reconstruct history by parsing the list of dicts | |
| for msg in history: | |
| role = msg.get("role") | |
| raw_content = msg.get("content") | |
| # Extract text from the content payload | |
| text_content = "" | |
| if isinstance(raw_content, str): | |
| text_content = raw_content | |
| elif isinstance(raw_content, list): | |
| for item in raw_content: | |
| if isinstance(item, dict) and item.get("type") == "text": | |
| text_content += item.get("text", "") | |
| # Map to LangChain message objects | |
| if role == "user": | |
| messages.append(HumanMessage(content=text_content)) | |
| elif role == "assistant": | |
| messages.append(AIMessage(content=text_content)) | |
| # Append the current extracted user message | |
| messages.append(HumanMessage(content=user_text)) | |
| # Invoke the LangChain model | |
| response = chat_model.invoke(messages) | |
| return response.content | |
| # --- UI SETUP --- | |
| with gr.Blocks() as demo: | |
| # Change 3: Define a session-specific state variable to hold the vector store | |
| session_vector_store = gr.State(value=None) | |
| gr.Markdown("# 🏢 Enterprise AI Support Bot") | |
| gr.Markdown("Upload a document (CSV or PDF) to inject knowledge, and customize the bot's persona on the fly!") | |
| with gr.Row(): | |
| file_upload = gr.File( | |
| label="Upload Document (.csv or .pdf)", | |
| file_types=[".csv", ".pdf"], | |
| file_count="multiple", | |
| ) | |
| status_text = gr.Markdown("Waiting for file upload...", visible=False) | |
| chatbot = gr.ChatInterface( | |
| fn=respond, | |
| # This adds an expandable accordion at the bottom of the chat UI | |
| additional_inputs=[ | |
| gr.Textbox( | |
| value=( | |
| f"You are an expert, friendly customer support agent.\n\n" | |
| "Use ONLY the following context to answer the user's question. " | |
| "If the answer is not in the context, politely say: " | |
| "'I am sorry but I don't have the information you need. Please allow me to connect you with a human operator.'" | |
| ), | |
| label="System Prompt (Define the Bot's Persona & Rules)", | |
| # lines=2 | |
| ), | |
| session_vector_store # Inject the state into the respond function | |
| ] | |
| ) | |
| # Wire the upload button to update BOTH the status text and the session state | |
| file_upload.upload( | |
| fn=process_file, | |
| inputs=file_upload, | |
| outputs=[status_text, session_vector_store] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |