dev-docs-rag-hf / app.py
iamfaham's picture
Upload 3 files
89e4c6f verified
import os
import gradio as gr
from rag_pipeline import create_rag_chain
import time
import logging
from appwrite_service import appwrite_service
# Check if running on Hugging Face Spaces
IS_HF_SPACES = os.getenv("SPACE_ID") is not None
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Predefined documentation sets
PREDEFINED_DOCS = {
"React": {
"name": "React Documentation",
"url": "https://react.dev/learn",
"description": "Official React documentation including hooks, components, and best practices",
"category": "Frontend Framework",
},
"Go": {
"name": "Go Documentation",
"url": "https://go.dev/doc/",
"description": "Official Go documentation including language features, standard library, and tutorials",
"category": "Programming Language",
},
"Python": {
"name": "Python Documentation",
"url": "https://docs.python.org/3/",
"description": "Official Python documentation covering language features, standard library, and tutorials",
"category": "Programming Language",
},
"Node.js": {
"name": "Node.js Documentation",
"url": "https://nodejs.org/en/docs/",
"description": "Node.js runtime documentation including APIs, modules, and development guides",
"category": "Runtime Environment",
},
"Vue.js": {
"name": "Vue.js Documentation",
"url": "https://vuejs.org/guide/",
"description": "Vue.js framework documentation with composition API, components, and routing",
"category": "Frontend Framework",
},
"Django": {
"name": "Django Documentation",
"url": "https://docs.djangoproject.com/en/stable/",
"description": "Django web framework documentation including models, views, and deployment",
"category": "Backend Framework",
},
"FastAPI": {
"name": "FastAPI Documentation",
"url": "https://fastapi.tiangolo.com/",
"description": "FastAPI framework documentation with automatic API documentation and validation",
"category": "Backend Framework",
},
"Docker": {
"name": "Docker Documentation",
"url": "https://docs.docker.com/",
"description": "Docker containerization platform documentation including images, containers, and orchestration",
"category": "DevOps",
},
"Kubernetes": {
"name": "Kubernetes Documentation",
"url": "https://kubernetes.io/docs/",
"description": "Kubernetes orchestration platform documentation including pods, services, and deployment",
"category": "DevOps",
},
"MongoDB": {
"name": "MongoDB Documentation",
"url": "https://docs.mongodb.com/",
"description": "MongoDB NoSQL database documentation including CRUD operations and aggregation",
"category": "Database",
},
"PostgreSQL": {
"name": "PostgreSQL Documentation",
"url": "https://www.postgresql.org/docs/",
"description": "PostgreSQL relational database documentation including SQL features and administration",
"category": "Database",
},
}
# Global variable to track selected documentation
selected_docs = {"key": None, "name": None, "url": None}
def select_documentation(doc_key):
"""Select a predefined documentation set"""
global selected_docs
if doc_key not in PREDEFINED_DOCS:
return "❌ Invalid documentation selection"
doc_info = PREDEFINED_DOCS[doc_key]
selected_docs["key"] = doc_key
selected_docs["name"] = doc_info["name"]
selected_docs["url"] = doc_info["url"]
# Check detailed status
status = get_detailed_status(doc_info["url"])
if "✅ Available" in status:
return f"✅ {doc_info['name']} is ready! You can now ask questions about it."
elif "⚠️" in status:
return f"⚠️ {doc_info['name']} selected but not fully available. Contact administrator."
else:
return f"❌ {doc_info['name']} is not available. Contact administrator."
def chat_with_rag(message, history):
"""Chat with RAG system"""
global selected_docs
if not message.strip():
return history, ""
# Check if documentation is selected and processed
if not selected_docs["key"]:
error_msg = "❌ Please select a documentation set first. Go to the 'Select Documentation' tab."
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": error_msg})
return history, ""
# Check if documentation is fully processed and available for chat
is_fully_processed = appwrite_service.is_fully_processed(selected_docs["url"])
if not is_fully_processed:
error_msg = f"❌ {selected_docs['name']} is not available for chat. Please contact the administrator to make this documentation available."
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": error_msg})
return history, ""
try:
# Create RAG chain for the selected documentation
rag_chain = create_rag_chain(selected_docs["url"])
response = rag_chain.invoke(message)
# Check if response is too long and truncate if necessary
max_display_length = 8000
if len(response) > max_display_length:
truncated_response = (
response[:max_display_length]
+ "\n\n... (response truncated due to length)"
)
response = truncated_response
# Add the exchange to history in the correct format for messages type
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": response})
return history, ""
except Exception as e:
error_msg = f"Sorry, I encountered an error: {str(e)}. Please try again."
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": error_msg})
return history, ""
def clear_chat():
"""Clear the chat history"""
return [], ""
def get_detailed_status(url):
"""Get detailed status of documentation availability"""
if not url:
return "❌ No URL provided"
try:
# Check if fully processed (has completion status)
is_fully_processed = appwrite_service.is_fully_processed(url)
if is_fully_processed:
return "✅ Available for Chat"
else:
return "❌ Not Available - Contact Admin"
except Exception as e:
return f"❌ Error checking status: {str(e)}"
def get_current_selection():
"""Get current documentation selection info with detailed status"""
global selected_docs
if selected_docs["key"]:
doc_info = PREDEFINED_DOCS[selected_docs["key"]]
status = get_detailed_status(selected_docs["url"])
return f"📚 {doc_info['name']}\n📖 {doc_info['description']}\n🔗 {doc_info['url']}\n\nStatus: {status}"
else:
return "❌ No documentation selected. Please select a documentation set from the list above."
# Create the Gradio interface
with gr.Blocks(
theme=gr.themes.Soft(),
css="""
.chatbot {
max-height: 600px !important;
overflow-y: auto !important;
}
.chatbot .message {
white-space: pre-wrap !important;
word-wrap: break-word !important;
max-width: 100% !important;
}
.chatbot .user-message, .chatbot .bot-message {
padding: 10px !important;
margin: 5px 0 !important;
border-radius: 8px !important;
}
.chatbot .bot-message {
background-color: #f0f8ff !important;
border-left: 4px solid #007acc !important;
}
.chatbot .user-message {
background-color: #e6f3ff !important;
border-left: 4px solid #28a745 !important;
}
.send-button {
background-color: #007acc !important;
color: white !important;
border: none !important;
border-radius: 8px !important;
padding: 10px 20px !important;
font-weight: bold !important;
transition: background-color 0.3s !important;
}
.send-button:hover {
background-color: #005a9e !important;
}
.clear-button {
background-color: #dc3545 !important;
color: white !important;
border: none !important;
border-radius: 8px !important;
padding: 8px 16px !important;
font-weight: bold !important;
transition: background-color 0.3s !important;
}
.clear-button:hover {
background-color: #c82333 !important;
}
.select-button {
background-color: #17a2b8 !important;
color: white !important;
border: none !important;
border-radius: 8px !important;
padding: 8px 16px !important;
font-weight: bold !important;
transition: background-color 0.3s !important;
}
.select-button:hover {
background-color: #138496 !important;
}
.doc-selector {
background-color: #f8f9fa !important;
border: 1px solid #ddd !important;
border-radius: 8px !important;
padding: 15px !important;
margin-bottom: 20px !important;
}
.doc-selector:hover {
border-color: #007acc !important;
background-color: #e6f3ff !important;
}
""",
) as demo:
gr.Markdown("# 🤖 Documentation Assistant")
gr.Markdown("Select documentation and start chatting!")
# Documentation Selection Section (Small section at top)
with gr.Group(elem_classes=["doc-selector"]):
gr.Markdown("### 📚 Select Documentation")
# Get available documentation from database
def get_available_docs():
"""Get only documentation that is available in the database"""
available_docs = {}
available_options = []
for key, doc_info in PREDEFINED_DOCS.items():
if appwrite_service.is_fully_processed(doc_info["url"]):
available_docs[key] = doc_info
available_options.append(f"{doc_info['name']} - {doc_info['url']}")
return available_docs, available_options
# Get available documentation
available_docs, doc_options = get_available_docs()
doc_keys = list(available_docs.keys())
if not available_docs:
gr.Markdown("❌ **No documentation is currently available.**")
gr.Markdown("Please contact the administrator to process documentation.")
else:
doc_dropdown = gr.Dropdown(
choices=doc_options,
label="Choose Documentation",
value=None,
interactive=True,
)
# Current selection display
current_selection = gr.Textbox(
label="Selected Documentation",
interactive=False,
value="No documentation selected",
lines=2,
)
# Chat Interface (Main section)
if available_docs:
gr.Markdown("### 💬 Chat with Documentation")
# Chat history
chatbot = gr.Chatbot(
label="Chat History",
height=500,
show_label=True,
type="messages",
)
# Input area with send button
with gr.Row():
with gr.Column(scale=4):
textbox = gr.Textbox(
placeholder="Ask a question about the documentation... (Press Enter or click Send)",
lines=2,
max_lines=5,
label="Your Question",
show_label=True,
)
with gr.Column(scale=1):
send_button = gr.Button(
"🚀 Send",
variant="primary",
size="lg",
elem_classes=["send-button"],
)
# Control buttons
with gr.Row():
clear_button = gr.Button(
"🗑️ Clear Chat", variant="secondary", elem_classes=["clear-button"]
)
# Example questions
with gr.Accordion("Example Questions", open=False):
gr.Markdown(
"""
Try these example questions after selecting documentation:
- **What is the main concept?**
- **How do I get started?**
- **What are the key features?**
- **Show me an example**
- **What are the best practices?**
"""
)
# Event handlers
def select_doc_from_dropdown(choice):
"""Handle documentation selection from dropdown"""
if not choice:
return "No documentation selected"
# Find the key for the selected option
selected_index = doc_options.index(choice)
selected_key = doc_keys[selected_index]
# Call the existing select_documentation function
return select_documentation(selected_key)
def send_message(message, history):
return chat_with_rag(message, history)
def update_selection():
return get_current_selection()
# Connect the dropdown
doc_dropdown.change(
fn=select_doc_from_dropdown,
inputs=[doc_dropdown],
outputs=[current_selection],
)
# Connect the send button
send_button.click(
fn=send_message,
inputs=[textbox, chatbot],
outputs=[chatbot, textbox],
api_name="send",
)
# Connect Enter key in textbox
textbox.submit(
fn=send_message,
inputs=[textbox, chatbot],
outputs=[chatbot, textbox],
api_name="send_enter",
)
# Connect clear button
clear_button.click(
fn=clear_chat, inputs=[], outputs=[chatbot, textbox], api_name="clear"
)
# Update selection info on load
demo.load(
fn=update_selection,
inputs=[],
outputs=[current_selection],
)
else:
gr.Markdown("### 💬 Chat Interface")
gr.Markdown("**No documentation is available for chat.**")
gr.Markdown("Please contact the administrator to process documentation first.")
if __name__ == "__main__":
demo.launch(
debug=False,
show_error=True,
)