""" Vrushket AI Assistant - Portfolio Chatbot A RAG-powered chatbot that answers questions about Vrushket More """ import os import gradio as gr from groq import Groq import chromadb from chromadb.utils import embedding_functions from pathlib import Path # Initialize Groq client client = Groq(api_key=os.environ.get("GROQ_API_KEY")) # Initialize ChromaDB with sentence transformers EMBED_MODEL = "all-MiniLM-L6-v2" embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(model_name=EMBED_MODEL) # Create ChromaDB collection chroma_client = chromadb.Client() collection = chroma_client.create_collection( name="vrushket_knowledge", embedding_function=embedding_func, metadata={"hnsw:space": "cosine"} ) # System prompt for the chatbot SYSTEM_PROMPT = """ You are Vrushket's AI assistant on his portfolio website. Be helpful, friendly, and CONCISE. RESPONSE GUIDELINES: - Keep responses to 2-4 sentences for simple questions - Use bullet points for lists (max 4-5 items) - Be warm but professional - avoid excessive enthusiasm or exclamation marks - Answer what's asked directly, don't over-explain - If asked about opportunities: Yes, actively looking! Suggest reaching out via email. CONTACT INFO (share when relevant): - Email: vmore2@binghamton.edu - LinkedIn: linkedin.com/in/vrushketmore - GitHub: github.com/vmore2 You represent Vrushket - a passionate AI/ML engineer. Stay authentic but brief. """ def load_knowledge_base(): """Load all markdown files from knowledge_base folder into ChromaDB""" knowledge_dir = Path(__file__).parent / "knowledge_base" documents = [] metadatas = [] ids = [] doc_id = 0 for md_file in knowledge_dir.glob("*.md"): content = md_file.read_text(encoding="utf-8") # Split into chunks (by sections) chunks = [] current_chunk = "" for line in content.split("\n"): if line.startswith("## ") and current_chunk: chunks.append(current_chunk.strip()) current_chunk = line + "\n" else: current_chunk += line + "\n" if current_chunk: chunks.append(current_chunk.strip()) # Add chunks to collection for chunk in chunks: if len(chunk) > 50: # Skip very short chunks documents.append(chunk) metadatas.append({"source": md_file.name}) ids.append(f"doc_{doc_id}") doc_id += 1 # Add to ChromaDB if documents: collection.add( documents=documents, metadatas=metadatas, ids=ids ) return len(documents) def retrieve_context(query: str, n_results: int = 5) -> str: """Retrieve relevant context from knowledge base""" results = collection.query( query_texts=[query], n_results=n_results ) if results and results['documents']: context_parts = results['documents'][0] return "\n\n---\n\n".join(context_parts) return "" def chat(message: str, history: list) -> str: """Main chat function with RAG""" # Retrieve relevant context context = retrieve_context(message) # Build messages for Groq messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "system", "content": f"CONTEXT FROM KNOWLEDGE BASE:\n\n{context}"} ] # Add conversation history for human, assistant in history: messages.append({"role": "user", "content": human}) messages.append({"role": "assistant", "content": assistant}) # Add current message messages.append({"role": "user", "content": message}) # Call Groq API try: response = client.chat.completions.create( model="llama-3.3-70b-versatile", messages=messages, temperature=0.7, max_tokens=350, ) return response.choices[0].message.content except Exception as e: return f"I apologize, but I'm having trouble responding right now. Please try again or contact Vrushket directly at vmore2@binghamton.edu. Error: {str(e)}" # Load knowledge base on startup print("Loading knowledge base...") num_docs = load_knowledge_base() print(f"Loaded {num_docs} document chunks into knowledge base") # Create Gradio interface TITLE = "💬 Chat with Vrushket's AI" DESCRIPTION = """ Hey! 👋 Ask me anything about Vrushket's skills, projects, or experience! """ CUSTOM_CSS = """ /* DARK BACKGROUND EVERYWHERE */ :root, *, body, .gradio-container, .wrap, .main, .contain { --body-background-fill: #0a0f1a !important; --background-fill-primary: #0a0f1a !important; --background-fill-secondary: #0a0f1a !important; --block-background-fill: #0a0f1a !important; --neutral-50: #ffffff !important; --neutral-100: #f1f5f9 !important; --neutral-200: #e2e8f0 !important; --body-text-color: #ffffff !important; --body-text-color-subdued: #cbd5e1 !important; background-color: #0a0f1a !important; color: #ffffff !important; } /* OUTER MESSAGE CONTAINER - NO BORDER */ .message, .message.bot, .message.user, [data-testid="bot"], [data-testid="user"] { background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; } /* INNER BUBBLE - THE DARK BOX WE KEEP */ .message-bubble-border, [class*="bubble-border"] { background: #1e293b !important; border: 1px solid #334155 !important; border-radius: 12px !important; padding: 12px 16px !important; } /* TEXT - WHITE - COMPREHENSIVE COVERAGE */ .message p, .message span, .message div, .prose p, .prose span, .prose div, .markdown, .markdown p, .markdown span, .markdown div, .chatbot p, .chatbot span, .chatbot div, [class*="message"] p, [class*="message"] span, [class*="message"] div, [class*="bot"] p, [class*="bot"] span, [class*="bot"] div, [class*="user"] p, [class*="user"] span, [class*="user"] div, .message-bubble-border p, .message-bubble-border span, .message-bubble-border div, [class*="bubble"] p, [class*="bubble"] span, [class*="bubble"] div { color: #ffffff !important; font-size: 15px !important; line-height: 1.5 !important; background: transparent !important; -webkit-text-fill-color: #ffffff !important; } /* FORCE ALL TEXT WHITE IN CHAT */ .chatbot, .chatbot *, [class*="chatbot"], [class*="chatbot"] *, .prose, .prose *, .markdown, .markdown * { color: #ffffff !important; -webkit-text-fill-color: #ffffff !important; } /* BOT MESSAGE SPECIFIC */ [data-testid="bot"] *, .bot *, .message.bot * { color: #ffffff !important; -webkit-text-fill-color: #ffffff !important; } /* USER MESSAGE SPECIFIC */ [data-testid="user"] *, .user *, .message.user * { color: #ffffff !important; -webkit-text-fill-color: #ffffff !important; } /* INPUT */ textarea, input, [class*="textbox"] { background: #1e293b !important; color: #f1f5f9 !important; -webkit-text-fill-color: #f1f5f9 !important; border: 1px solid #334155 !important; border-radius: 12px !important; } /* BUTTONS */ button { background: #1e293b !important; color: #94a3b8 !important; border: 1px solid #334155 !important; border-radius: 8px !important; } button:hover { background: #334155 !important; color: #f1f5f9 !important; } button.primary, [class*="primary"] { background: #0891b2 !important; color: white !important; border: none !important; } /* HIDE JUNK */ footer, .built-with, .label-wrap, label span, .avatar-container, .avatar { display: none !important; } /* CHATBOT AREA */ .chatbot, [class*="chatbot"] { background: #0a0f1a !important; } /* LINKS */ a, .prose a, .markdown a { color: #38bdf8 !important; -webkit-text-fill-color: #38bdf8 !important; } """ demo = gr.ChatInterface( fn=chat, css=CUSTOM_CSS, ) if __name__ == "__main__": demo.launch()