Spaces:
Build error
Build error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Amazon Multimodal Assistant - Redesigned</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --amazon-orange: #FF9900; | |
| --amazon-orange-dark: #E68A00; | |
| --amazon-blue: #146EB4; | |
| --amazon-header: #131921; | |
| --amazon-subnav: #232F3E; | |
| --page-bg: #FAFAFA; | |
| --panel-bg: #FFFFFF; | |
| --panel-muted: #F9FAFB; | |
| --border-subtle: #E5E7EB; | |
| --text-main: #2D3748; | |
| --text-muted: #4B5563; | |
| --success: #10B981; | |
| --warning: #F59E0B; | |
| } | |
| * { font-family: 'Inter', sans-serif; } | |
| body { background-color: var(--page-bg); color: var(--text-main); } | |
| .glass-effect { | |
| background: rgba(255, 255, 255, 0.25); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.18); | |
| } | |
| .hover-lift { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } | |
| .hover-lift:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
| } | |
| .search-input:focus { | |
| box-shadow: 0 0 0 3px rgba(255, 153, 0, 0.1); | |
| border-color: var(--amazon-orange); | |
| } | |
| /* Styles for dynamically generated product cards in main.js */ | |
| .product-card { transition: all 0.2s ease; } | |
| .product-card:hover { | |
| transform: scale(1.02); | |
| box-shadow: 0 8px 25px -5px rgba(0, 0, 0, 0.1); | |
| } | |
| .similarity-bar { | |
| background: linear-gradient(90deg, var(--amazon-orange) 0%, var(--amazon-orange-dark) 100%); | |
| height: 4px; | |
| border-radius: 2px; | |
| transition: width 0.8s ease; | |
| } | |
| .loading-skeleton { | |
| background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); | |
| background-size: 200% 100%; | |
| animation: loading 1.5s infinite; | |
| } | |
| @keyframes loading { | |
| 0% { background-position: 200% 0; } | |
| 100% { background-position: -200% 0; } | |
| } | |
| .fade-in { animation: fadeIn 0.5s ease-in; } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .status-indicator { animation: pulse 2s infinite; } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.7; } | |
| } | |
| .micro-interaction { transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); } | |
| .micro-interaction:active { transform: scale(0.98); } | |
| .answer-card { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(249, 250, 251, 0.9) 100%); | |
| border: 1px solid rgba(229, 231, 235, 0.5); | |
| } | |
| .evidence-highlight { | |
| background: linear-gradient(135deg, rgba(255, 153, 0, 0.1) 0%, rgba(255, 153, 0, 0.05) 100%); | |
| border: 1px solid rgba(255, 153, 0, 0.2); | |
| } | |
| .header-bg { background: linear-gradient(135deg, var(--amazon-header) 0%, var(--amazon-subnav) 100%); } | |
| .search-button { | |
| background: linear-gradient(135deg, var(--amazon-orange) 0%, var(--amazon-orange-dark) 100%); | |
| transition: all 0.3s ease; | |
| } | |
| .search-button:hover { | |
| background: linear-gradient(135deg, var(--amazon-orange-dark) 0%, var(--amazon-orange) 100%); | |
| transform: translateY(-1px); | |
| box-shadow: 0 4px 12px rgba(255, 153, 0, 0.3); | |
| } | |
| .upload-area { border: 2px dashed #D1D5DB; transition: all 0.3s ease; } | |
| .upload-area:hover { | |
| border-color: var(--amazon-orange); | |
| background-color: rgba(255, 153, 0, 0.05); | |
| } | |
| .upload-area.dragover { | |
| border-color: var(--amazon-orange); | |
| background-color: rgba(255, 153, 0, 0.1); | |
| } | |
| @media (max-width: 1024px) { | |
| .three-column-layout { grid-template-columns: 1fr; gap: 1rem; } | |
| .sidebar-panel { order: -1; } | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen flex flex-col"> | |
| <header class="header-bg text-white shadow-lg"> | |
| <div class="container mx-auto px-6 py-4"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-4"> | |
| <img src="amazon-logo.png" onerror="this.style.display='none'" alt="Amazon" class="h-8 w-auto"> | |
| <div> | |
| <h1 class="text-2xl font-bold">Multimodal Assistant</h1> | |
| <p class="text-sm text-gray-300">AI-powered product search with CLIP + GPT-4</p> | |
| </div> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex items-center space-x-2 glass-effect px-3 py-2 rounded-full"> | |
| <div class="w-2 h-2 bg-green-400 rounded-full status-indicator"></div> | |
| <span class="text-xs">Index Ready</span> | |
| </div> | |
| <div class="flex items-center space-x-2 glass-effect px-3 py-2 rounded-full"> | |
| <span class="text-xs">9,509 Products</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <main class="container mx-auto px-6 py-8 flex-grow"> | |
| <div class="three-column-layout grid grid-cols-12 gap-6"> | |
| <div class="col-span-12 lg:col-span-4"> | |
| <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 hover-lift"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Search Query</h2> | |
| <div class="mb-6"> | |
| <label for="search-text" class="block text-sm font-medium text-gray-700 mb-2"> | |
| Describe what you're looking for | |
| </label> | |
| <textarea | |
| id="search-text" | |
| placeholder="e.g., 'Wireless earbuds with noise cancellation under $150' or 'What is this product and how is it used?'" | |
| class="search-input w-full p-4 border border-gray-300 rounded-lg resize-none focus:outline-none transition-all duration-200" | |
| rows="3" | |
| ></textarea> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium text-gray-700 mb-2"> | |
| Upload product image (optional) | |
| </label> | |
| <div id="upload-area" class="upload-area rounded-lg p-8 text-center cursor-pointer"> | |
| <div id="upload-content"> | |
| <svg class="mx-auto h-12 w-12 text-gray-400 mb-4" stroke="currentColor" fill="none" viewBox="0 0 48 48"> | |
| <path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> | |
| </svg> | |
| <p class="text-sm text-gray-600 mb-2"> | |
| <span class="font-medium text-orange-600">Click to upload</span> or drag and drop | |
| </p> | |
| <p class="text-xs text-gray-500">PNG, JPG up to 10MB</p> | |
| </div> | |
| <div id="image-preview" class="hidden relative"> | |
| <img id="preview-img" class="mx-auto max-h-32 rounded-lg shadow-sm" alt="Preview"> | |
| <button id="remove-image" class="mt-2 text-sm text-red-600 hover:text-red-800">Remove image</button> | |
| </div> | |
| </div> | |
| <input type="file" id="image-input" accept="image/*" class="hidden"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium text-gray-700 mb-3">Search Mode</label> | |
| <div class="space-y-2"> | |
| <label class="flex items-center cursor-pointer"> | |
| <input type="radio" name="search-mode" value="text_only" class="text-orange-600 focus:ring-orange-500"> | |
| <span class="ml-2 text-sm text-gray-700">Text Only</span> | |
| </label> | |
| <label class="flex items-center cursor-pointer"> | |
| <input type="radio" name="search-mode" value="image_only" class="text-orange-600 focus:ring-orange-500"> | |
| <span class="ml-2 text-sm text-gray-700">Image Only</span> | |
| </label> | |
| <label class="flex items-center cursor-pointer"> | |
| <input type="radio" name="search-mode" value="multimodal" checked class="text-orange-600 focus:ring-orange-500"> | |
| <span class="ml-2 text-sm text-gray-700">Multimodal (Text + Image)</span> | |
| </label> | |
| </div> | |
| </div> | |
| <button id="search-button" class="search-button w-full text-white font-semibold py-3 px-6 rounded-lg micro-interaction"> | |
| <span class="flex items-center justify-center"> | |
| <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path> | |
| </svg> | |
| <span id="btn-text">Search Products</span> | |
| <div id="loading-state" class="hidden ml-2 w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div> | |
| </span> | |
| </button> | |
| <div class="mt-6 pt-6 border-t border-gray-100"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-sm font-semibold text-gray-800">History</h3> | |
| <button id="clear-history" class="text-xs text-red-600 hover:text-red-800">Clear</button> | |
| </div> | |
| <div id="history-container" class="space-y-2 max-h-48 overflow-y-auto pr-1"> | |
| <div class="text-xs text-gray-400 text-center py-2">No history yet</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-span-12 lg:col-span-5"> | |
| <div id="query-card" class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6 hover-lift hidden fade-in"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-lg font-semibold text-gray-800">Current Query</h3> | |
| <span id="retrieval-method" class="px-3 py-1 bg-orange-100 text-orange-800 text-xs font-medium rounded-full"> | |
| Multimodal Fusion | |
| </span> | |
| </div> | |
| <div id="query-content" class="text-gray-700 text-sm leading-relaxed font-medium"> | |
| </div> | |
| <div id="query-image" class="mt-4 hidden"> | |
| <img class="rounded-lg shadow-sm max-h-32 object-contain border border-gray-100" alt="Query image"> | |
| </div> | |
| </div> | |
| <div id="answer-card" class="answer-card rounded-xl p-6 mb-6 hover-lift hidden fade-in"> | |
| <div class="flex items-center mb-4"> | |
| <div class="w-8 h-8 bg-gradient-to-br from-orange-400 to-orange-600 rounded-full flex items-center justify-center mr-3 shadow-md"> | |
| <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20"> | |
| <path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path> | |
| </svg> | |
| </div> | |
| <h3 class="text-lg font-semibold text-gray-800">AI Assistant Answer</h3> | |
| </div> | |
| <div id="answer-content" class="text-gray-700 leading-relaxed text-sm whitespace-pre-wrap"> | |
| </div> | |
| <div class="mt-4 text-xs text-gray-500 flex items-center"> | |
| <span class="inline-block w-2 h-2 bg-green-500 rounded-full mr-2"></span> | |
| Generated using CLIP retrieval + GPT-4 reasoning | |
| </div> | |
| </div> | |
| <div id="evidence-card" class="evidence-highlight rounded-xl p-6 mb-6 hover-lift hidden fade-in"> | |
| <h4 class="text-md font-semibold text-gray-800 mb-3">🔍 Grounding Evidence</h4> | |
| <div class="flex items-start space-x-4"> | |
| <div class="flex-shrink-0 bg-white p-1 rounded-lg border border-gray-200"> | |
| <img id="evidence-image" class="w-20 h-20 object-contain rounded-md" src="https://via.placeholder.com/150?text=Wait..." onerror="this.src='https://via.placeholder.com/150?text=No+Img'" alt="Evidence product"> | |
| </div> | |
| <div class="flex-1"> | |
| <h5 id="evidence-name" class="font-semibold text-gray-800 mb-1 text-sm line-clamp-2">Product Name</h5> | |
| <p id="evidence-category" class="text-xs text-gray-600 mb-2">Category</p> | |
| <div class="flex items-center space-x-2"> | |
| <span class="text-xs bg-orange-100 text-orange-800 px-2 py-1 rounded font-medium">Top Match</span> | |
| <span id="evidence-similarity" class="text-xs text-green-700 font-bold">95.2% match</span> | |
| </div> | |
| </div> | |
| </div> | |
| <p class="text-xs text-gray-500 mt-4 italic"> | |
| The assistant's answer is primarily based on this product and similar items from the retrieved set. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="col-span-12 lg:col-span-3"> | |
| <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 hover-lift h-full flex flex-col"> | |
| <div class="flex items-center justify-between mb-4 border-b border-gray-100 pb-3"> | |
| <h3 class="text-lg font-semibold text-gray-800">Retrieved Products</h3> | |
| <span id="results-count" class="text-xs font-medium bg-gray-100 text-gray-600 px-2 py-1 rounded-full">0 items</span> | |
| </div> | |
| <div id="results-container" class="space-y-3 flex-1 overflow-y-auto custom-scrollbar" style="max-height: 70vh;"> | |
| <div class="text-sm text-gray-400 text-center py-10"> | |
| Results from ChromaDB will appear here. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <footer class="bg-white border-t border-gray-200 mt-auto"> | |
| <div class="container mx-auto px-6 py-8"> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-8 text-sm"> | |
| <div> | |
| <h4 class="font-semibold text-gray-800 mb-2">System Information</h4> | |
| <div class="space-y-1 text-gray-500"> | |
| <p>Products indexed: 9,509</p> | |
| <p>Index status: <span class="text-green-600 font-medium">Ready</span></p> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-semibold text-gray-800 mb-2">How it Works</h4> | |
| <div class="space-y-1 text-gray-500"> | |
| <p>1. CLIP encodes your query</p> | |
| <p>2. ChromaDB retrieves similar products</p> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-semibold text-gray-800 mb-2">Tips</h4> | |
| <div class="space-y-1 text-gray-500"> | |
| <p>• Combine text + image for best results</p> | |
| <p>• Be specific in your descriptions</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="border-t border-gray-100 mt-8 pt-6 text-center text-xs text-gray-400"> | |
| © 2025 Amazon Multimodal RAG Demo. Powered by FastAPI + ChromaDB. | |
| </div> | |
| </div> | |
| </footer> | |
| <script src="main.js"></script> | |
| </body> | |
| </html> |