hpmor / app.py
deenaik's picture
Deploy: HPMOR Q&A chatbot - 2025-10-15
d84fcd9
#!/usr/bin/env python3
"""HuggingFace Spaces app for HPMOR Q&A System - Cloud deployment version."""
import os
import sys
import gradio as gr
from typing import List, Tuple
from pathlib import Path
# Add src to path
sys.path.insert(0, str(Path(__file__).parent))
from src.config import config
from src.document_processor import HPMORProcessor
from src.vector_store import VectorStoreManager
from src.rag_engine import RAGEngine
# Force Groq-only mode for cloud deployment
os.environ["FORCE_GROQ_ONLY"] = "1"
class HFChatInterface:
"""Simplified chat interface for HuggingFace Spaces."""
def __init__(self):
"""Initialize the chat interface."""
print("Initializing HPMOR Q&A Chat Interface for HuggingFace Spaces...")
# Check if we need to setup
processed_docs = config.processed_data_dir / "documents.json"
if not processed_docs.exists():
print("Setting up system for first time...")
self.setup_system()
# Initialize RAG engine (Groq-only mode)
self.engine = RAGEngine(force_recreate=False)
print("System ready!")
def setup_system(self):
"""Set up the HPMOR Q&A system."""
print("Processing HPMOR document...")
processor = HPMORProcessor()
documents = processor.process(force_reprocess=False)
print(f"Processed {len(documents)} chunks")
print("Creating vector index...")
vector_store = VectorStoreManager()
vector_store.get_or_create_index(documents, force_recreate=False)
print("Setup complete!")
def format_sources(self, sources: List[dict]) -> str:
"""Format sources for display."""
if not sources:
return ""
formatted = []
for i, source in enumerate(sources[:3], 1): # Limit to top 3 sources
formatted.append(
f"**Source {i}** - Chapter {source['chapter_number']}: {source['chapter_title']}\n"
f"Relevance: {source['score']:.2f}\n"
f"*{source['text_preview'][:100]}...*"
)
return "\n\n".join(formatted)
def process_message(
self,
message: str,
history: List[List[str]],
show_sources: bool
) -> Tuple[str, str]:
"""Process a chat message and return response."""
if not message:
return "", "Please enter a question."
# Convert history to messages format
messages = []
for user_msg, assistant_msg in history:
if user_msg:
messages.append({"role": "user", "content": user_msg})
if assistant_msg:
messages.append({"role": "assistant", "content": assistant_msg})
messages.append({"role": "user", "content": message})
try:
# Get response from engine
response = self.engine.chat(messages, stream=False)
# Extract answer
if isinstance(response.get("answer"), str):
answer = response["answer"]
else:
answer = str(response.get("answer", "No response generated"))
# Format sources if requested
sources_text = ""
if show_sources and response.get("sources"):
sources_text = "\n\n---\n\n**πŸ“š Sources from HPMOR:**\n\n" + self.format_sources(response["sources"])
answer = answer + sources_text
return answer, ""
except Exception as e:
error_msg = f"I apologize, but I encountered an error: {str(e)}\n\nPlease make sure the Groq API key is properly configured."
return error_msg, ""
def create_interface() -> gr.Blocks:
"""Create the Gradio interface."""
# Initialize chat interface
chat_interface = HFChatInterface()
with gr.Blocks(title="Chat with Harry Potter-Evans-Verres", theme=gr.themes.Soft()) as interface:
gr.Markdown(
"""
# πŸ§™β€β™‚οΈ Chat with Harry James Potter-Evans-Verres
Hello! I'm Harry Potter-Evans-Verres from "Harry Potter and the Methods of Rationality."
Ask me anything about my adventures, experiments with magic, or my thoughts on rationality and science.
I'll respond based on my experiences and the scientific method, of course!
*Powered by RAG with ChromaDB and Groq API (llama-3.3-70b-versatile)*
"""
)
with gr.Row():
with gr.Column(scale=3):
chatbot = gr.Chatbot(
label="Chat",
height=500,
show_copy_button=True,
avatar_images=(None, "πŸ§™β€β™‚οΈ")
)
with gr.Row():
msg_input = gr.Textbox(
label="Your Question",
placeholder="Ask me anything... For example: 'What do you think about magic?' or 'Tell me about your experiments'",
lines=2,
scale=4
)
submit_btn = gr.Button("Send πŸ“¨", variant="primary", scale=1)
with gr.Column(scale=1):
gr.Markdown("### βš™οΈ Settings")
show_sources = gr.Checkbox(
value=True,
label="Show Sources from Book"
)
gr.Markdown(
"""
### πŸ’‘ Tips
- Ask about Harry's experiments
- Inquire about his views on magic
- Ask about other characters
- Request explanations of events
"""
)
# Example questions
gr.Examples(
examples=[
"Harry, how did you first react when you learned magic was real?",
"What's your opinion on the way Hogwarts teaches magic?",
"Can you explain your scientific experiments with magic?",
"What do you think about Hermione?",
"How do you apply rationality to magical problems?",
"What's your relationship with Professor Quirrell like?",
],
inputs=msg_input,
label="πŸ’¬ Example Questions"
)
# Event handlers
def respond(message, history, sources):
"""Handle message submission."""
answer, _ = chat_interface.process_message(message, history, sources)
history.append([message, answer])
return "", history
msg_input.submit(
respond,
inputs=[msg_input, chatbot, show_sources],
outputs=[msg_input, chatbot]
)
submit_btn.click(
respond,
inputs=[msg_input, chatbot, show_sources],
outputs=[msg_input, chatbot]
)
gr.Markdown(
"""
---
**About:** This chatbot uses Retrieval-Augmented Generation (RAG) to answer questions
based on "Harry Potter and the Methods of Rationality" by Eliezer Yudkowsky.
**Note:** Requires a Groq API key. Get one free at [console.groq.com](https://console.groq.com/)
"""
)
return interface
if __name__ == "__main__":
# Check for Groq API key
if not os.getenv("GROQ_API_KEY"):
print("WARNING: GROQ_API_KEY not found in environment variables!")
print("Please set it in your HuggingFace Space secrets.")
# Launch interface
interface = create_interface()
interface.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)