import gradio as gr import requests import json import os from typing import List, Tuple # --- Configuration --- API_URL = "https://monocopter.net" API_TOKEN = os.getenv("API_TOKEN", "") HEADERS = { "Authorization": f"Bearer {API_TOKEN}", "Content-Type": "application/json" } # --- Core API Functions --- def query_rag_system(query: str, max_cards: int = 10) -> dict: # Default changed to 10 """Query the RAG system with a historical question.""" try: payload = {"query": query, "max_cards": max_cards, "include_sources": True} response = requests.post(f"{API_URL}/query", json=payload, headers=HEADERS) response.raise_for_status() # Raise an error for bad status codes return response.json() except requests.exceptions.RequestException as e: return {"error": f"Network Error: {e}"} except Exception as e: return {"error": str(e)} def search_cards_semantic(query: str, max_cards: int = 10) -> dict: # Default changed to 10 """Perform semantic search on cards.""" try: payload = {"query": query, "max_cards": max_cards} response = requests.post(f"{API_URL}/search", json=payload, headers=HEADERS) response.raise_for_status() # Raise an error for bad status codes return response.json() except requests.exceptions.RequestException as e: return {"error": f"Network Error: {e}"} except Exception as e: return {"error": str(e)} # --- Chatbot Functions --- def format_rag_response(result: dict) -> str: """Format the RAG response for display in the chatbot.""" if "error" in result: return f"❌ **Error**: {result['error']}" answer = result.get("answer", "I couldn't generate an answer for your question.") cards_used = result.get("cards_used", []) sources = result.get("sources", []) processing_time = result.get("processing_time", 0) # Build formatted response response = f"**Answer:**\n{answer}\n\n" if sources: response += f"**📚 Sources ({len(sources)} documents):**\n" # MODIFICATION: Loop through all sources instead of just the top 3 for i, source in enumerate(sources, 1): response += f"{i}. {source}\n" response += "\n" if cards_used: response += f"**đŸ—ƒī¸ Retrieved {len(cards_used)} relevant cards** | " response += f"**âąī¸ {processing_time:.2f}s**" return response def format_search_response(result: dict) -> str: """Format the search response for display.""" if "error" in result: return f"❌ **Error**: {result['error']}" cards = result.get("cards", []) if not cards: return "🔍 No relevant historical information found for your search." response = f"🔍 **Found {len(cards)} relevant historical entries:**\n\n" # MODIFICATION: Show all results instead of just the top 3 for i, card in enumerate(cards, 1): title = card.get('title', 'Untitled') summary = card.get('summary', 'No summary available') relevance = card.get('relevance_score', 0) # Truncate summary if too long if len(summary) > 200: summary = summary[:200] + "..." response += f"**{i}. {title}** (Relevance: {relevance:.3f})\n{summary}\n\n" processing_time = result.get("processing_time", 0) response += f"âąī¸ Search completed in {processing_time:.2f}s" return response def chatbot_respond(message: str, history: List[Tuple[str, str]], search_mode: bool) -> Tuple[str, List[Tuple[str, str]]]: """Main chatbot response function.""" if not message.strip(): return "", history # MODIFICATION: Set max_cards to 10 for both modes max_cards = 10 if search_mode: # Use semantic search for browsing/exploring result = search_cards_semantic(message, max_cards) response = format_search_response(result) else: # Use RAG for question-answering result = query_rag_system(message, max_cards) response = format_rag_response(result) # Add to history history.append((message, response)) return "", history def clear_chat(): """Clear the chat history.""" return [], "" # --- Gradio Interface --- def create_interface(): with gr.Blocks( title="Historical Knowledge Assistant", theme=gr.themes.Soft(), css=""" .chat-container { max-height: 600px; overflow-y: auto; } .examples { margin: 10px 0; } """ ) as demo: gr.Markdown(""" # đŸ›ī¸ Historical Knowledge Assistant Ask questions about historical events, people, and concepts. Powered by your Rolodex RAG database. """) with gr.Row(): with gr.Column(scale=4): chatbot = gr.Chatbot( label="Historical Q&A", height=500, show_copy_button=True, container=True, elem_classes=["chat-container"] ) with gr.Row(): msg_input = gr.Textbox( placeholder="Ask about historical events, people, or concepts...", label="Your Question", lines=2, scale=4 ) with gr.Row(): send_btn = gr.Button("đŸ’Ŧ Ask", variant="primary", scale=1) clear_btn = gr.Button("đŸ—‘ī¸ Clear", variant="secondary", scale=1) with gr.Column(scale=1): search_mode = gr.Checkbox( label="🔍 Search Mode", value=False, info="Toggle between Q&A (off) and Search (on)" ) gr.Markdown(""" ### 💡 Example Questions **Question & Answer Mode:** - What caused the American Revolution? - How did colonial resistance evolve? - Who were key figures in Bacon's Rebellion? **Search Mode:** - colonial resistance - Boston Massacre - taxation without representation ### â„šī¸ Tips - **Q&A Mode**: Ask complete questions for detailed answers - **Search Mode**: Use keywords to explore topics - Sources and processing time shown with each response """, elem_classes=["examples"]) # Event handlers msg_input.submit( chatbot_respond, inputs=[msg_input, chatbot, search_mode], outputs=[msg_input, chatbot] ) send_btn.click( chatbot_respond, inputs=[msg_input, chatbot, search_mode], outputs=[msg_input, chatbot] ) clear_btn.click( clear_chat, outputs=[chatbot, msg_input] ) return demo # --- Launch Configuration --- if __name__ == "__main__": print("🚀 Launching Historical Knowledge Assistant...") print(f"🌐 API URL: {API_URL}") if API_TOKEN: print(f"🔑 Using API token: {API_TOKEN[:4]}...{API_TOKEN[-4:]}") else: print("🔑 API token not found. Please set the API_TOKEN secret in your Space settings.") demo = create_interface() # For Hugging Face Spaces deployment demo.launch( share=False, server_name="0.0.0.0", server_port=7860, show_error=True )