OR_Search / app.py
ambrosfitz's picture
Update app.py
de95dee verified
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
)