Spaces:
Sleeping
Sleeping
| """ | |
| app.py β BGP Troubleshooting RAG Chatbot (Cloud Edition) | |
| Deployed on HuggingFace Spaces. Run: streamlit run app.py | |
| HuggingFace Spaces auto-detects app.py and runs it as the entry point. | |
| The GROQ_API_KEY must be set in Space Settings β Variables and Secrets. | |
| WHY ERROR HANDLING FOR MISSING API KEY MATTERS: | |
| In cloud deployments, environment variables are set by operators, not users. | |
| If someone forks your Space and forgets to add their GROQ_API_KEY, the app | |
| will crash with an ugly traceback. Explicit error handling with st.error() | |
| gives them a clear, actionable message instead of a 500 error. This is the | |
| difference between a hobby script and a production app. | |
| """ | |
| import os | |
| from dotenv import load_dotenv | |
| load_dotenv() # loads .env locally; no-op on HF Spaces (key comes from Secrets instead) | |
| import streamlit as st | |
| from rag_chain import query_bgp, get_chunk_count, get_vectorstore, get_embeddings | |
| # ββ Page Configuration ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.set_page_config( | |
| page_title="BGP Troubleshooting RAG β Cloud Edition", | |
| page_icon="π", | |
| layout="wide", | |
| initial_sidebar_state="expanded", | |
| ) | |
| # ββ API Key Guard (must be first β stops execution if key missing) βββββββββββββ | |
| if not os.environ.get("GROQ_API_KEY"): | |
| st.error( | |
| "β οΈ **GROQ_API_KEY not set.**\n\n" | |
| "To deploy your own copy:\n" | |
| "1. Go to your HuggingFace Space\n" | |
| "2. Click **Settings** β **Variables and Secrets**\n" | |
| "3. Add a new Secret: `GROQ_API_KEY` = your key from [console.groq.com](https://console.groq.com)\n\n" | |
| "For local development: `export GROQ_API_KEY=gsk_...`" | |
| ) | |
| st.stop() # Halts rendering β nothing below this runs without the key | |
| # ββ Session State Initialization ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| if "chain_loaded" not in st.session_state: | |
| st.session_state.chain_loaded = False | |
| # ββ Example Questions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| EXAMPLE_QUESTIONS = [ | |
| "Why is my BGP neighbor stuck in IDLE state?", | |
| "What does %BGP-5-ADJCHANGE neighbor Down mean?", | |
| "How do I verify BGP authentication is working?", | |
| "My BGP session keeps flapping β what should I check?", | |
| "Based on the telemetry logs, what is the most common failure?", | |
| ] | |
| # ββ Sidebar βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with st.sidebar: | |
| st.title("π BGP RAG Chatbot") | |
| st.caption("Cloud Edition") | |
| # Deployment info | |
| st.markdown("### π Deployment") | |
| st.markdown( | |
| """ | |
| | Component | Value | | |
| |-----------|-------| | |
| | **LLM** | Groq API β llama-3.1-8b-instant | | |
| | **Embeddings** | all-MiniLM-L6-v2 (Local) | | |
| | **Vector DB** | FAISS (Pre-built index) | | |
| | **Hosted on** | HuggingFace Spaces | | |
| """ | |
| ) | |
| # Knowledge base stats | |
| st.markdown("### π Knowledge Base") | |
| chunk_count = get_chunk_count() | |
| if chunk_count > 0: | |
| st.success(f"β Index loaded β **{chunk_count:,}** chunks") | |
| else: | |
| st.warning("β οΈ Index not loaded yet") | |
| st.markdown( | |
| """ | |
| **Documents indexed:** | |
| - bgp_states_guide.txt | |
| - bgp_troubleshooting_guide.txt | |
| - bgp_show_commands.txt | |
| - bgp_error_messages.txt | |
| - bgp_configuration_guide.txt | |
| - bgp_telemetry_logs.txt | |
| """ | |
| ) | |
| # Example questions | |
| st.markdown("### π‘ Example Questions") | |
| st.caption("Click to populate the input") | |
| for q in EXAMPLE_QUESTIONS: | |
| if st.button(q, key=f"example_{q[:20]}", use_container_width=True): | |
| st.session_state["pending_question"] = q | |
| # Footer | |
| st.divider() | |
| st.caption( | |
| "π [Local Version (nettune)](https://github.com/vanip3/bgp-rag-chatbot) \n" | |
| "Powered by [Groq](https://groq.com) Β· [LangChain](https://langchain.com) Β· [FAISS](https://github.com/facebookresearch/faiss)" | |
| ) | |
| if st.button("ποΈ Clear Chat History", use_container_width=True): | |
| st.session_state.messages = [] | |
| st.rerun() | |
| # ββ Main Area βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.title("π BGP Troubleshooting RAG Chatbot") | |
| st.markdown( | |
| "**Powered by Groq (Llama 3.1) + FAISS + Cisco BGP Knowledge Base** " | |
| "| Deployed on HuggingFace Spaces \n" | |
| "Ask any BGP troubleshooting question β answers grounded in Cisco documentation and real telemetry logs." | |
| ) | |
| st.divider() | |
| # Display chat history | |
| for message in st.session_state.messages: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| if message["role"] == "assistant" and "sources" in message: | |
| with st.expander("π Sources retrieved"): | |
| for src in message["sources"]: | |
| st.markdown(f"- `{src}`") | |
| # Handle example question button clicks (populates input via session state) | |
| pending = st.session_state.pop("pending_question", None) | |
| # Chat input | |
| user_input = st.chat_input("Ask a BGP troubleshooting question...") | |
| # Use the pending example question if a button was clicked, otherwise use typed input | |
| question = pending or user_input | |
| if question: | |
| # Show user message | |
| st.session_state.messages.append({"role": "user", "content": question}) | |
| with st.chat_message("user"): | |
| st.markdown(question) | |
| # Generate response | |
| with st.chat_message("assistant"): | |
| with st.spinner("π Searching knowledge base and querying Groq..."): | |
| try: | |
| result = query_bgp(question) | |
| answer = result["answer"] | |
| sources = result["sources"] | |
| st.markdown(answer) | |
| if sources: | |
| with st.expander("π Sources retrieved"): | |
| for src in sources: | |
| st.markdown(f"- `{src}`") | |
| # Save to history with sources | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": answer, | |
| "sources": sources, | |
| }) | |
| except Exception as e: | |
| error_msg = f"β Error generating response: {str(e)}" | |
| st.error(error_msg) | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": error_msg, | |
| "sources": [], | |
| }) | |
| # Re-run to clear the pending question state cleanly | |
| if pending: | |
| st.rerun() | |