Web RAG System
Extract content from web pages and ask questions using AI-powered retrieval
import gradio as gr import requests import time import os # Use localhost for HF Spaces since both services run in the same container API_BASE_URL = "http://localhost:8000" def extract_links(url): """Extract links from the given URL""" endpoint = f"{API_BASE_URL}/extract_links" payload = {"url": url} try: response = requests.post(endpoint, json=payload, timeout=30) if response.status_code == 200: return response.json()["unique_links"] else: raise Exception(f"Failed to extract links: {response.text}") except requests.exceptions.RequestException as e: raise Exception(f"Connection error: {str(e)}") def extract_text(urls): """Extract text from URLs""" endpoint = f"{API_BASE_URL}/extract_text" try: response = requests.post(endpoint, json=urls, timeout=60) if response.status_code == 200: return response.json()["file_saved"] else: raise Exception(f"Failed to extract text: {response.text}") except requests.exceptions.RequestException as e: raise Exception(f"Connection error: {str(e)}") def perform_rag(file_path, prompt): """Perform RAG on the extracted text""" endpoint = f"{API_BASE_URL}/rag" payload = {"file_path": file_path, "prompt": prompt} try: response = requests.post(endpoint, json=payload, timeout=60) if response.status_code == 200: return response.json() else: raise Exception(f"Failed to perform RAG: {response.text}") except requests.exceptions.RequestException as e: raise Exception(f"Connection error: {str(e)}") def check_api_health(): """Check if FastAPI is running""" try: response = requests.get(f"{API_BASE_URL}/", timeout=5) return response.status_code == 200 except: return False def process_multiple_links(url, prompt): """Process multiple links from a webpage""" if not url or not prompt: return "❌ Error: Please provide both URL and prompt", "", "" if not check_api_health(): return "❌ Error: FastAPI service is not available. Please wait a moment and try again.", "", "" try: links = extract_links(url) sample_links = links[:5] file_path = extract_text(sample_links) result = perform_rag(file_path, prompt) status_msg = f"✅ Processed {len(sample_links)} pages from {len(links)} total links found" response_text = f"**Query:** {result['user_query']}\n\n**Response:** {result['assistant_response']}" sources_text = result['sources'] return status_msg, response_text, sources_text except Exception as e: return f"❌ Error: {str(e)}", "", "" def process_homepage_only(url, prompt): """Process homepage content only""" if not url or not prompt: return "❌ Error: Please provide both URL and prompt", "", "" if not check_api_health(): return "❌ Error: FastAPI service is not available. Please wait a moment and try again.", "", "" try: file_path = extract_text([url]) result = perform_rag(file_path, prompt) status_msg = "✅ Processed homepage content" response_text = f"**Query:** {result['user_query']}\n\n**Response:** {result['assistant_response']}" sources_text = result['sources'] return status_msg, response_text, sources_text except Exception as e: return f"❌ Error: {str(e)}", "", "" # Dark theme custom CSS with animations custom_css = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap'); * { font-family: 'Inter', sans-serif !important; } /* Dark animated gradient background */ .gradio-container { background: linear-gradient(-45deg, #0f0c29, #302b63, #24243e, #1a1a2e); background-size: 400% 400%; animation: gradientShift 15s ease infinite; } @keyframes gradientShift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Main container with dark glassmorphism */ .main-container { backdrop-filter: blur(20px); background: rgba(20, 20, 30, 0.85); border-radius: 24px; padding: 2rem; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.1); animation: fadeInUp 0.8s ease; } @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } } /* Animated title with neon glow */ .animated-title { background: linear-gradient(135deg, #00f2fe 0%, #4facfe 50%, #00c6ff 100%); background-size: 200% 200%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; animation: gradientText 3s ease infinite; font-weight: 700; font-size: 3.5rem; text-align: center; margin-bottom: 0.5rem; filter: drop-shadow(0 0 20px rgba(0, 242, 254, 0.5)); } @keyframes gradientText { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Floating animation for icons */ .floating-icon { animation: float 3s ease-in-out infinite; display: inline-block; } @keyframes float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-10px); } } /* Dark input fields with neon borders */ textarea, input[type="text"] { font-size: 1.1rem !important; border-radius: 12px !important; border: 2px solid rgba(0, 242, 254, 0.3) !important; transition: all 0.3s ease !important; background: rgba(30, 30, 45, 0.9) !important; color: #e0e0e0 !important; } textarea:focus, input[type="text"]:focus { border-color: #00f2fe !important; box-shadow: 0 0 20px rgba(0, 242, 254, 0.4) !important; transform: translateY(-2px); background: rgba(35, 35, 50, 0.95) !important; } /* Dark labels */ label { font-weight: 600 !important; color: #b0b0b0 !important; font-size: 1.1rem !important; transition: all 0.3s ease; } /* Neon buttons with hover effects */ .gr-button { background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%) !important; color: #0a0a0f !important; border: none !important; border-radius: 12px !important; padding: 0.75rem 2rem !important; font-size: 1.1rem !important; font-weight: 600 !important; transition: all 0.3s ease !important; box-shadow: 0 4px 15px rgba(0, 242, 254, 0.4) !important; } .gr-button:hover { transform: translateY(-3px) !important; box-shadow: 0 6px 25px rgba(0, 242, 254, 0.6) !important; filter: brightness(1.1); } .gr-button:active { transform: translateY(-1px) !important; } /* Dark output boxes with glassmorphism */ .output-box { background: rgba(30, 30, 45, 0.95) !important; border-radius: 16px !important; border: 1px solid rgba(0, 242, 254, 0.2) !important; backdrop-filter: blur(10px); animation: slideIn 0.5s ease; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3) !important; color: #e0e0e0 !important; padding: 1.5rem !important; min-height: 200px !important; } /* Markdown container styling */ .output-box .prose, .output-box .markdown { font-size: 1.2rem !important; line-height: 1.8 !important; } /* Markdown styling */ .output-box h1 { color: #00f2fe !important; margin-top: 1.5rem !important; margin-bottom: 1rem !important; font-size: 2rem !important; } .output-box h2 { color: #00f2fe !important; margin-top: 1.5rem !important; margin-bottom: 0.75rem !important; font-size: 1.6rem !important; } .output-box h3 { color: #4facfe !important; margin-top: 1rem !important; margin-bottom: 0.5rem !important; font-size: 1.3rem !important; } .output-box p { color: #e0e0e0 !important; line-height: 1.9 !important; margin-bottom: 1.2rem !important; font-size: 1.15rem !important; } .output-box strong { color: #4facfe !important; font-weight: 600 !important; font-size: 1.2rem !important; } .output-box code { background: rgba(0, 242, 254, 0.15) !important; padding: 3px 8px !important; border-radius: 6px !important; color: #00f2fe !important; font-size: 1.1rem !important; border: 1px solid rgba(0, 242, 254, 0.3) !important; } .output-box ul, .output-box ol { font-size: 1.15rem !important; line-height: 1.8 !important; margin-left: 1.5rem !important; color: #e0e0e0 !important; } .output-box li { margin-bottom: 0.5rem !important; } /* Enhanced response area glow effect */ .output-box:hover { border-color: rgba(0, 242, 254, 0.4) !important; box-shadow: 0 6px 25px rgba(0, 242, 254, 0.3) !important; transition: all 0.3s ease !important; } /* JSON viewer styling */ .output-box pre { background: rgba(20, 20, 30, 0.9) !important; border-radius: 10px !important; padding: 1.5rem !important; overflow-x: auto !important; border: 1px solid rgba(0, 242, 254, 0.3) !important; font-size: 1.05rem !important; line-height: 1.6 !important; } .output-box .json-holder { background: rgba(20, 20, 30, 0.9) !important; border-radius: 10px !important; padding: 1.5rem !important; font-size: 1.05rem !important; } .output-box .json-key { color: #00f2fe !important; font-weight: 600 !important; } .output-box .json-string { color: #90ee90 !important; } .output-box .json-number { color: #ffa07a !important; } /* Special styling for AI response area */ div[data-testid="markdown"] { background: rgba(35, 35, 50, 0.6) !important; padding: 1.5rem !important; border-radius: 12px !important; border: 1px solid rgba(0, 242, 254, 0.15) !important; } @keyframes slideIn { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } } /* Dark tab styling */ .tab-nav button { border-radius: 12px !important; font-weight: 600 !important; transition: all 0.3s ease !important; font-size: 1.05rem !important; background: rgba(30, 30, 45, 0.6) !important; color: #b0b0b0 !important; border: 1px solid rgba(0, 242, 254, 0.2) !important; } .tab-nav button:hover { background: rgba(40, 40, 55, 0.8) !important; border-color: rgba(0, 242, 254, 0.4) !important; } .tab-nav button[aria-selected="true"] { background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%) !important; color: #0a0a0f !important; box-shadow: 0 4px 15px rgba(0, 242, 254, 0.4) !important; border: none !important; } /* Info cards with pulse animation */ .info-card { animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.02); } } /* Dark example box styling */ .example-box { background: linear-gradient(135deg, rgba(0, 242, 254, 0.1) 0%, rgba(79, 172, 254, 0.1) 100%); border-radius: 16px; padding: 1.5rem; border-left: 4px solid #00f2fe; margin-top: 2rem; transition: all 0.3s ease; animation: fadeIn 1s ease; } .example-box:hover { transform: translateX(5px); box-shadow: 0 5px 20px rgba(0, 242, 254, 0.2); } .example-box h3, .example-box p { color: #e0e0e0 !important; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } /* Dark note box with shimmer effect */ .note-box { background: linear-gradient(135deg, rgba(0, 242, 254, 0.08) 0%, rgba(79, 172, 254, 0.08) 100%); border-radius: 12px; padding: 1rem; border-left: 4px solid #00f2fe; margin-top: 1rem; position: relative; overflow: hidden; } .note-box::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(0, 242, 254, 0.2), transparent); animation: shimmer 3s infinite; } .note-box p { color: #00c6ff !important; position: relative; z-index: 1; } @keyframes shimmer { 0% { left: -100%; } 100% { left: 100%; } } /* Dark subtitle animation */ .subtitle { color: #b0b0b0; font-size: 1.3rem; text-align: center; margin-bottom: 2rem; animation: fadeInDown 1s ease; } @keyframes fadeInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } /* Loading spinner styles */ .loading { display: inline-block; width: 20px; height: 20px; border: 3px solid rgba(0, 242, 254, 0.3); border-radius: 50%; border-top-color: #00f2fe; animation: spin 1s ease-in-out infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Neon glow effect on containers */ .main-container::before { content: ''; position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; background: linear-gradient(45deg, #00f2fe, #4facfe, #00f2fe); border-radius: 24px; z-index: -1; opacity: 0.3; filter: blur(10px); animation: neonPulse 3s ease-in-out infinite; } @keyframes neonPulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.5; } } """ # Main interface with tabs with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as interface: gr.HTML("""
Extract content from web pages and ask questions using AI-powered retrieval
URL: https://openai.com
Question: What are the main products and services offered?
ℹ️ Note: If you encounter connection errors, please wait a moment for the system to initialize and try again.