#!/usr/bin/env python3 """ FAR Chatbot - Hugging Face Spaces Version Federal Acquisition Regulation Assistant with Clickable Citations """ import streamlit as st import time import logging import os import sys import re from datetime import datetime # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Import the chatbot from far_chatbot import FARChatbot try: import markdown except ImportError: markdown = None # Configure page st.set_page_config( page_title="FAR Chatbot - Federal Acquisition Regulation Assistant", page_icon="🏛️", layout="wide", initial_sidebar_state="expanded" ) # Authentication VALID_CREDENTIALS = {"testuser": "farbot2025"} def check_password(): def password_entered(): username = st.session_state.get("username", "") password = st.session_state.get("password", "") if username in VALID_CREDENTIALS and VALID_CREDENTIALS[username] == password: st.session_state["authenticated"] = True del st.session_state["password"] else: st.session_state["authenticated"] = False if "authenticated" not in st.session_state: st.session_state["authenticated"] = False if not st.session_state["authenticated"]: col1, col2, col3 = st.columns([1, 2, 1]) with col2: st.markdown("## 🏛️ FAR Chatbot Login") st.text_input("Username", key="username") st.text_input("Password", type="password", key="password") st.button("🔐 Log In", on_click=password_entered, type="primary", use_container_width=True) if st.session_state.get("authenticated") == False and "username" in st.session_state and st.session_state["username"]: st.error("❌ Invalid credentials") return False return True if not check_password(): st.stop() # CSS Styling st.markdown(""" """, unsafe_allow_html=True) # Session state if 'chatbot' not in st.session_state: st.session_state.chatbot = None if 'chat_history' not in st.session_state: st.session_state.chat_history = [] if 'show_sources' not in st.session_state: st.session_state.show_sources = True @st.cache_resource def load_chatbot(): """Load the FAR chatbot""" try: logger.info("Loading FAR Chatbot...") chatbot = FARChatbot( faiss_index_path="data/faiss_index.index", texts_path="data/texts.txt", use_gpt5=True ) return chatbot except Exception as e: logger.error(f"Error loading chatbot: {e}") st.error(f"Error loading chatbot: {e}") return None def get_acquisition_gov_url(citation: str) -> str: base_citation = re.sub(r'\([a-z]\)$', '', citation) return f"https://www.acquisition.gov/far/{base_citation}" def make_citations_clickable(response: str, search_results: list) -> str: if not markdown: return response text = response def make_link(citation): url = get_acquisition_gov_url(citation) return f'' # Replace citation patterns text = re.sub(r'\[FAR\s+(\d+\.\d+(?:-\d+)?(?:\([a-z]\))?)\]', lambda m: f'{make_link(m.group(1))}[{m.group(1)}]', text) text = re.sub(r'\[(\d+\.\d+(?:-\d+)?(?:\([a-z]\))?)\]', lambda m: f'{make_link(m.group(1))}[{m.group(1)}]', text) text = re.sub(r'(?', text) return markdown.markdown(text, extensions=['tables', 'fenced_code', 'nl2br']) # Header st.markdown("""

🏛️ FAR Chatbot

Federal Acquisition Regulation Assistant

""", unsafe_allow_html=True) # Sidebar with st.sidebar: st.markdown("## ⚙️ Settings") if st.session_state.chatbot is None: st.session_state.chatbot = load_chatbot() if st.session_state.chatbot: st.success("✅ Chatbot Ready!") else: st.error("❌ Failed to load") st.session_state.show_sources = st.checkbox("Show sources", value=True) st.markdown("### 💡 Sample Questions") samples = [ "What are small business set-asides?", "Explain the simplified acquisition threshold", "What is the micro-purchase threshold?", "When can I use sole source?" ] for q in samples: if st.button(f"💬 {q}", key=f"s_{hash(q)}"): st.session_state.current_question = q st.rerun() if st.button("🗑️ Clear Chat"): st.session_state.chat_history = [] st.rerun() if st.button("🚪 Logout"): st.session_state["authenticated"] = False st.rerun() # Main content if st.session_state.chatbot is None: st.error("Chatbot not loaded") st.stop() # Display chat history for entry in st.session_state.chat_history: question, answer, search_results, timestamp = entry[:4] st.markdown(f'
👤 You: {question}
', unsafe_allow_html=True) formatted = make_citations_clickable(answer, search_results) st.markdown(f'
🤖 FAR Bot:
{formatted}
', unsafe_allow_html=True) if st.session_state.show_sources and search_results: with st.expander("📚 Sources"): for cit, txt in search_results[:5]: url = get_acquisition_gov_url(cit) st.markdown(f"**[FAR {cit}]({url})**: {txt[:300]}...") # Input st.markdown("## 💬 Ask a Question") question = "" if 'current_question' in st.session_state: question = st.session_state.current_question del st.session_state.current_question question = st.text_input("Your question:", value=question, placeholder="Ask about FAR regulations...") if st.button("🚀 Ask", type="primary") and question.strip(): with st.spinner("Processing..."): try: result = st.session_state.chatbot.chat(question, top_k=None) timestamp = datetime.now().strftime("%H:%M") st.session_state.chat_history.append(( question, result['response'], result.get('search_results', []), timestamp )) st.rerun() except Exception as e: st.error(f"Error: {e}") st.markdown("---") st.markdown("🏛️ FAR Chatbot | Powered by GPT-4 Turbo | [acquisition.gov](https://acquisition.gov)")