Spaces:
Sleeping
Sleeping
| 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(""" | |
| <div class="main-container"> | |
| <h1 class="animated-title"> | |
| <span class="floating-icon">π</span> Web RAG System | |
| </h1> | |
| <p class="subtitle"> | |
| Extract content from web pages and ask questions using AI-powered retrieval | |
| </p> | |
| </div> | |
| """) | |
| with gr.Tabs() as tabs: | |
| with gr.Tab("π Multiple Links Analysis"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| url_input_multi = gr.Textbox( | |
| label="π Website URL", | |
| placeholder="https://example.com", | |
| elem_classes="output-box" | |
| ) | |
| prompt_input_multi = gr.Textbox( | |
| label="π Your Question", | |
| placeholder="What is this website about?", | |
| lines=3, | |
| elem_classes="output-box" | |
| ) | |
| submit_btn_multi = gr.Button("β¨ Analyze Multiple Links", variant="primary") | |
| with gr.Row(): | |
| with gr.Column(): | |
| status_output_multi = gr.Textbox(label="π Status", elem_classes="output-box") | |
| response_output_multi = gr.Markdown(label="π€ AI Response", elem_classes="output-box") | |
| sources_output_multi = gr.JSON(label="π Sources", elem_classes="output-box") | |
| submit_btn_multi.click( | |
| fn=process_multiple_links, | |
| inputs=[url_input_multi, prompt_input_multi], | |
| outputs=[status_output_multi, response_output_multi, sources_output_multi] | |
| ) | |
| with gr.Tab("π Homepage Only Analysis"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| url_input_home = gr.Textbox( | |
| label="π Website URL", | |
| placeholder="https://example.com", | |
| elem_classes="output-box" | |
| ) | |
| prompt_input_home = gr.Textbox( | |
| label="π Your Question", | |
| placeholder="What is this website about?", | |
| lines=3, | |
| elem_classes="output-box" | |
| ) | |
| submit_btn_home = gr.Button("β¨ Analyze Homepage", variant="primary") | |
| with gr.Row(): | |
| with gr.Column(): | |
| status_output_home = gr.Textbox(label="π Status", elem_classes="output-box") | |
| response_output_home = gr.Markdown(label="π€ AI Response", elem_classes="output-box") | |
| sources_output_home = gr.JSON(label="π Sources", elem_classes="output-box") | |
| submit_btn_home.click( | |
| fn=process_homepage_only, | |
| inputs=[url_input_home, prompt_input_home], | |
| outputs=[status_output_home, response_output_home, sources_output_home] | |
| ) | |
| gr.HTML(""" | |
| <div class="example-box"> | |
| <h3 style="margin-top: 0; font-size: 1.4rem;"> | |
| <span class="floating-icon">π‘</span> Example Usage | |
| </h3> | |
| <p style="font-size: 1.1rem;"><strong>URL:</strong> https://openai.com</p> | |
| <p style="font-size: 1.1rem;"><strong>Question:</strong> What are the main products and services offered?</p> | |
| </div> | |
| <div class="note-box"> | |
| <p style="margin: 0; font-size: 1.05rem;"> | |
| βΉοΈ <strong>Note:</strong> If you encounter connection errors, please wait a moment for the system to initialize and try again. | |
| </p> | |
| </div> | |
| """) | |
| # Launch the interface | |
| if __name__ == "__main__": | |
| interface.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| show_error=True, | |
| quiet=False | |
| ) |