""" SCRIPT 5/5: app.py - Gradio Web Interface for Shoe RAG Pipeline Colab - https://colab.research.google.com/drive/1rq-ywjykHBw7xPXCmd3DmZdK6T9bhDtA?usp=sharing This script provides a web-based interface for the complete RAG pipeline using Gradio. It integrates all components (retrieval, augmentation, generation) into a user-friendly web app. Key Concepts: - Gradio: Python library for creating web interfaces for ML models - Web Interface: User-friendly way to interact with RAG pipeline - Multimodal Input: Supporting both text and image inputs - Real-time Processing: Live interaction with the RAG system - Step-by-step Visualization: Showing detailed pipeline execution Required Dependencies: - gradio: Web interface framework - All dependencies from other pipeline components - Custom CSS for styling Commands to run: # Launch Gradio web interface python app.py # Launch with custom port python app.py --port 7860 # Launch with public sharing enabled python app.py --share # Launch with custom host python app.py --host 0.0.0.0 --port 8080 # Setup database and launch app python app.py --setup-db """ import argparse import os from typing import Any, Dict, List, Optional import gradio as gr from generator import get_available_models from rag_pipeline import run_complete_shoes_rag_pipeline_with_details # Import components from other modules from retriever import MyntraShoesEnhanced, create_shoes_table_from_hf from openai import OpenAI def is_huggingface_space(): """ Checks if the code is running within a Hugging Face Spaces environment. Returns: bool: True if running in HF Spaces, False otherwise. """ if os.environ.get("SYSTEM") == "spaces": return True else: return False def gradio_rag_pipeline( query, image, search_type, use_advanced_prompts, model_provider, model_name, openai_api_key, ): """Gradio interface function for RAG pipeline.""" try: # Validate inputs based on model provider if model_provider == "openai": if not openai_api_key or openai_api_key.strip() == "": return ( "ā OpenAI API key is required for OpenAI models.", "", "", "", "", [], ) # Check if both text and image inputs are provided - this is not allowed has_text_input = query and query.strip() has_image_input = image is not None if has_text_input and has_image_input: return ( "ā Error: Please provide either a text query OR an image, not both. Choose one input type at a time.", "", "", "", "", [], ) # Determine the actual query based on inputs if search_type == "image" and image is not None: actual_query = image elif search_type == "text" and query.strip(): actual_query = query elif search_type == "auto": if image is not None: actual_query = image elif query.strip(): actual_query = query else: return ( "ā Please provide either a text query or upload an image.", "", "", "", "", [], ) else: return ( "ā Please provide appropriate input for the selected search type.", "", "", "", "", [], ) # Run the RAG pipeline with detailed tracking rag_result = run_complete_shoes_rag_pipeline_with_details( database="myntra_shoes_db", table_name="myntra_shoes_table", schema=MyntraShoesEnhanced, search_query=actual_query, limit=3, use_llm=True, use_advanced_prompts=use_advanced_prompts, search_type=search_type, model_provider=model_provider, model_name=model_name, openai_api_key=openai_api_key if model_provider == "openai" else None, ) # Extract detailed step information retrieval_details = rag_result.get( "retrieval_details", "No retrieval details available" ) augmentation_details = rag_result.get( "augmentation_details", "No augmentation details available" ) generation_details = rag_result.get( "generation_details", "No generation details available" ) # Format the response response = rag_result.get("response", "No response generated") search_type_used = rag_result.get("search_type", "unknown") # Format results for display results_text = f"š Search Type: {search_type_used}\n\n" if rag_result.get("prompt_analysis"): results_text += ( f"š Query Type: {rag_result['prompt_analysis']['query_type']}\n" ) results_text += ( f"š Results Found: {rag_result['prompt_analysis']['num_results']}\n\n" ) # Prepare image gallery data image_gallery = [] results_details = [] for i, result in enumerate(rag_result["results"], 1): product_type = result.get("product_type", "Shoe") gender = result.get("gender", "Unisex") color = result.get("color", "Various colors") pattern = result.get("pattern", "Standard") description = result.get("description", "No description available") image_path = result.get("image_path") # Add to gallery if image exists if image_path and os.path.exists(image_path): # Create detailed caption for the image caption = f"#{i} - {product_type} for {gender}" if color and color not in ["None", None, ""]: caption += f" | Color: {color}" if pattern and pattern not in ["None", None, ""]: caption += f" | Pattern: {pattern}" image_gallery.append((image_path, caption)) # Format detailed description detail_text = f"**{i}. {product_type} for {gender}**\n" detail_text += f" ⢠Color: {color}\n" detail_text += f" ⢠Pattern: {pattern}\n" if description: # Show full description without truncation detail_text += f" ⢠Description: {description}\n" detail_text += "\n" results_details.append(detail_text) # Combine all details formatted_results = "".join(results_details) return ( response, formatted_results, retrieval_details, augmentation_details, generation_details, image_gallery, ) except Exception as e: return ( f"ā Error: {str(e)}", "", "ā Error occurred", "ā Error occurred", "ā Error occurred", [], ) def create_gradio_app(): """Create and launch the Gradio application.""" # Custom CSS for better styling css = """ .gradio-container { max-width: 100% !important; width: 100% !important; margin: 0 !important; padding: 20px !important; background: #f5f5f5; } .main { max-width: 100% !important; width: 100% !important; } /* Header styling */ .header-section { text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 15px; margin-bottom: 30px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } .header-section h1 { font-size: 2.5em; margin-bottom: 15px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .header-section p { font-size: 1.2em; margin-bottom: 10px; opacity: 0.95; } .output-text { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; } .gallery-container { margin-top: 15px; width: 100%; } .search-section { background: #ffffff; padding: 25px; border-radius: 12px; margin-bottom: 20px; height: fit-content; box-shadow: 0 2px 10px rgba(0,0,0,0.08); border: 1px solid #e0e0e0; } .results-section { background: #ffffff; padding: 25px; border-radius: 12px; border: 1px solid #e0e0e0; height: fit-content; box-shadow: 0 2px 10px rgba(0,0,0,0.08); } .gallery-section { background: #ffffff; padding: 25px; border-radius: 12px; border: 1px solid #e0e0e0; margin-top: 20px; width: 100%; box-shadow: 0 2px 10px rgba(0,0,0,0.08); } /* Improve text readability */ .gradio-textbox textarea { background: #fafafa !important; border: 1px solid #ddd !important; color: #333 !important; } .gradio-textbox textarea:focus { background: #ffffff !important; border-color: #667eea !important; } /* Make gallery images larger */ .gallery img { max-height: 300px !important; object-fit: contain !important; } /* Improve button styling */ .primary-button { width: 100%; padding: 12px; font-size: 16px; font-weight: bold; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; border: none !important; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important; } .primary-button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6) !important; } /* Section headers */ .section-header { color: #667eea; font-weight: bold; border-bottom: 2px solid #667eea; padding-bottom: 5px; margin-bottom: 15px; } /* Model settings styling */ .model-settings { background: #f8f9ff; padding: 15px; border-radius: 8px; margin: 10px 0; border: 1px solid #e0e7ff; } /* API key input styling */ .api-key-input { border: 2px solid #fbbf24 !important; background: #fffbeb !important; } .api-key-input:focus { border-color: #f59e0b !important; box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1) !important; } /* RAG steps styling */ .rag-steps-section { background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); padding: 25px; border-radius: 12px; margin: 20px 0; border: 1px solid #e2e8f0; } .step-box { background: #ffffff; border: 2px solid #e5e7eb; border-radius: 10px; padding: 20px; margin: 10px 0; transition: all 0.3s ease; } .step-box:hover { border-color: #667eea; box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15); } /* Different colors for each step */ .retrieval-step { border-left: 4px solid #10b981; } .augmentation-step { border-left: 4px solid #3b82f6; } .generation-step { border-left: 4px solid #8b5cf6; } """ with gr.Blocks(css=css, title="š Shoe RAG Pipeline") as app: # Header Section with gr.Row(): with gr.Column(elem_classes=["header-section"]): gr.HTML( """
This demo showcases a complete Retrieval-Augmented Generation (RAG) pipeline for shoe recommendations and search.