Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Text2Vector | Text to Vector Conversion</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <style> | |
| .text-input-group:hover .remove-text-btn { | |
| display: block ; | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #6e8efb 0%, #a777e3 100%); | |
| } | |
| .embed-card { | |
| backdrop-filter: blur(10px); | |
| background: rgba(255, 255, 255, 0.1); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| } | |
| .text-area { | |
| min-height: 150px; | |
| } | |
| .vector-display { | |
| font-family: monospace; | |
| white-space: pre-wrap; | |
| overflow-x: auto; | |
| } | |
| #vanta-bg { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen text-gray-100"> | |
| <div id="vanta-bg"></div> | |
| <div class="container mx-auto px-4 py-12"> | |
| <!-- Header --> | |
| <header class="text-center mb-12"> | |
| <h1 class="text-4xl md:text-5xl font-bold mb-4">Text2Vector ⚡</h1> | |
| <p class="text-xl opacity-80">Transform your text into powerful vector embeddings</p> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="max-w-4xl mx-auto"> | |
| <div class="grid md:grid-cols-2 gap-8"> | |
| <!-- Input Section --> | |
| <div class="embed-card rounded-xl p-6 shadow-lg"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="flex items-center"> | |
| <i data-feather="edit-3" class="mr-2"></i> | |
| <h2 class="text-xl font-semibold">Input Texts</h2> | |
| </div> | |
| <button id="add-text-btn" class="px-3 py-1 gradient-bg hover:opacity-90 rounded-lg transition flex items-center text-sm"> | |
| <i data-feather="plus" class="mr-1"></i> | |
| Add Field | |
| </button> | |
| </div> | |
| <div id="text-inputs-container"> | |
| <div class="text-input-group mb-3 relative"> | |
| <textarea class="w-full text-area bg-gray-800 bg-opacity-50 rounded-lg p-4 text-white border border-gray-600 focus:border-purple-400 focus:ring-1 focus:ring-purple-400 transition" placeholder="Enter your text here..."></textarea> | |
| <button class="remove-text-btn absolute top-1 right-1 p-1 bg-gray-700 hover:bg-gray-600 rounded-full transition" style="display: none;"> | |
| <i data-feather="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="flex justify-between mt-4"> | |
| <button id="clear-btn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg transition flex items-center"> | |
| <i data-feather="trash-2" class="mr-2"></i> | |
| Clear All | |
| </button> | |
| <button id="generate-btn" class="px-6 py-2 gradient-bg hover:opacity-90 rounded-lg transition flex items-center"> | |
| <i data-feather="zap" class="mr-2"></i> | |
| Generate Embeddings | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Output Section --> | |
| <div class="embed-card rounded-xl p-6 shadow-lg"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="flex items-center"> | |
| <i data-feather="list" class="mr-2"></i> | |
| <h2 class="text-xl font-semibold">Vector Embeddings</h2> | |
| </div> | |
| <button id="copy-btn" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded-lg transition flex items-center text-sm" disabled> | |
| <i data-feather="copy" class="mr-1"></i> | |
| Copy | |
| </button> | |
| </div> | |
| <div id="output-container" class="vector-display bg-gray-800 bg-opacity-50 rounded-lg p-4 h-64 overflow-auto hidden"> | |
| <pre id="output-vector" class="text-sm"></pre> | |
| </div> | |
| <div id="placeholder" class="bg-gray-800 bg-opacity-30 rounded-lg p-8 text-center h-64 flex items-center justify-center"> | |
| <div class="opacity-60"> | |
| <i data-feather="wind" class="w-12 h-12 mx-auto mb-4"></i> | |
| <p>Your embeddings will appear here</p> | |
| </div> | |
| </div> | |
| <button id="download-btn" class="w-full mt-4 px-4 py-2 gradient-bg hover:opacity-90 rounded-lg transition flex items-center justify-center hidden"> | |
| <i data-feather="download" class="mr-2"></i> | |
| Download as JSON | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Info Section --> | |
| <div class="embed-card rounded-xl p-6 mt-8 shadow-lg"> | |
| <div class="flex items-center mb-4"> | |
| <i data-feather="info" class="mr-2"></i> | |
| <h2 class="text-xl font-semibold">About Text2Vector</h2> | |
| </div> | |
| <p class="mb-4">Text2Vector transforms your text into high-dimensional vector representations using Microsoft's multilingual-e5-large model, capturing semantic meaning across multiple languages.</p> | |
| <div class="grid md:grid-cols-3 gap-4"> | |
| <div class="bg-gray-800 bg-opacity-30 p-4 rounded-lg"> | |
| <div class="flex items-center mb-2"> | |
| <i data-feather="hash" class="mr-2 text-purple-300"></i> | |
| <h3 class="font-medium">Dimensionality</h3> | |
| </div> | |
| <p class="text-sm opacity-80">1024-dimensional vectors</p> | |
| </div> | |
| <div class="bg-gray-800 bg-opacity-30 p-4 rounded-lg"> | |
| <div class="flex items-center mb-2"> | |
| <i data-feather="cpu" class="mr-2 text-purple-300"></i> | |
| <h3 class="font-medium">Model</h3> | |
| </div> | |
| <p class="text-sm opacity-80">Microsoft's multilingual-e5-large</p> | |
| </div> | |
| <div class="bg-gray-800 bg-opacity-30 p-4 rounded-lg"> | |
| <div class="flex items-center mb-2"> | |
| <i data-feather="code" class="mr-2 text-purple-300"></i> | |
| <h3 class="font-medium">API</h3> | |
| </div> | |
| <p class="text-sm opacity-80">Simple REST integration</p> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="text-center py-8 opacity-70 text-sm"> | |
| <p>© 2023 Text2Vector ⚡ | Powered by multilingual-e5-large embeddings</p> | |
| <p class="mt-2" id="api-status">API Status: <span class="text-red-500">Checking...</span></p> | |
| </footer> | |
| <!-- Scripts --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> | |
| <script> | |
| // Initialize Vanta.js background | |
| VANTA.GLOBE({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x6e8efb, | |
| backgroundColor: 0x0, | |
| size: 0.8 | |
| }); | |
| // Initialize Feather Icons | |
| feather.replace(); | |
| // DOM Elements | |
| const textInputsContainer = document.getElementById('text-inputs-container'); | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const clearBtn = document.getElementById('clear-btn'); | |
| const copyBtn = document.getElementById('copy-btn'); | |
| const downloadBtn = document.getElementById('download-btn'); | |
| const outputContainer = document.getElementById('output-container'); | |
| const outputVector = document.getElementById('output-vector'); | |
| const placeholder = document.getElementById('placeholder'); | |
| const addTextBtn = document.getElementById('add-text-btn'); | |
| // Add new text input field | |
| addTextBtn.addEventListener('click', () => { | |
| const newInputGroup = document.createElement('div'); | |
| newInputGroup.className = 'text-input-group mb-3 relative'; | |
| newInputGroup.innerHTML = ` | |
| <textarea class="w-full text-area bg-gray-800 bg-opacity-50 rounded-lg p-4 text-white border border-gray-600 focus:border-purple-400 focus:ring-1 focus:ring-purple-400 transition" placeholder="Enter your text here..."></textarea> | |
| <button class="remove-text-btn absolute top-1 right-1 p-1 bg-gray-700 hover:bg-gray-600 rounded-full transition"> | |
| <i data-feather="x" class="w-4 h-4"></i> | |
| </button> | |
| `; | |
| textInputsContainer.appendChild(newInputGroup); | |
| feather.replace(); | |
| setupRemoveButtons(); | |
| }); | |
| // Setup remove buttons for all input fields | |
| function setupRemoveButtons() { | |
| document.querySelectorAll('.remove-text-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| if (document.querySelectorAll('.text-input-group').length > 1) { | |
| e.target.closest('.text-input-group').remove(); | |
| } | |
| }); | |
| }); | |
| } | |
| // Show remove buttons when hovering over input groups | |
| document.addEventListener('mouseover', (e) => { | |
| if (e.target.closest('.text-input-group')) { | |
| const group = e.target.closest('.text-input-group'); | |
| if (document.querySelectorAll('.text-input-group').length > 1) { | |
| group.querySelector('.remove-text-btn').style.display = 'block'; | |
| } | |
| } | |
| }); | |
| document.addEventListener('mouseout', (e) => { | |
| if (e.target.closest('.text-input-group')) { | |
| const group = e.target.closest('.text-input-group'); | |
| group.querySelector('.remove-text-btn').style.display = 'none'; | |
| } | |
| }); | |
| setupRemoveButtons(); | |
| // API Configuration | |
| const API_URL = '/embed'; | |
| // Check API health | |
| async function checkApiHealth() { | |
| try { | |
| const response = await fetch('/health'); | |
| if (response.ok) { | |
| document.getElementById('api-status').innerHTML = | |
| 'API Status: <span class="text-green-500">Online</span>'; | |
| } else { | |
| throw new Error('API not responding'); | |
| } | |
| } catch (error) { | |
| document.getElementById('api-status').innerHTML = | |
| 'API Status: <span class="text-red-500">Offline</span>'; | |
| console.error('API health check failed:', error); | |
| } | |
| } | |
| // Initial health check | |
| checkApiHealth(); | |
| setInterval(checkApiHealth, 30000); // Check every 30 seconds | |
| // Event Listeners | |
| generateBtn.addEventListener('click', async () => { | |
| const textInputs = Array.from(document.querySelectorAll('.text-input-group textarea')); | |
| const texts = textInputs | |
| .map(input => input.value.trim()) | |
| .filter(text => text.length > 0); | |
| if (texts.length === 0) { | |
| alert('Please enter at least one text input'); | |
| return; | |
| } | |
| // Show loading state | |
| generateBtn.disabled = true; | |
| generateBtn.innerHTML = '<i data-feather="loader" class="animate-spin mr-2"></i> Processing...'; | |
| feather.replace(); | |
| // Make API call to backend | |
| try { | |
| const response = await fetch('/embed', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| texts: texts | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`API Error: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| const embeddings = texts.map((text, index) => ({ | |
| text: text, | |
| vector: data.embeddings[index], | |
| model: "multilingual-e5-large", | |
| timestamp: new Date().toISOString() | |
| })); | |
| // Display the embeddings | |
| outputVector.textContent = JSON.stringify(embeddings.length === 1 ? embeddings[0] : embeddings, null, 2); | |
| placeholder.classList.add('hidden'); | |
| outputContainer.classList.remove('hidden'); | |
| copyBtn.disabled = false; | |
| downloadBtn.classList.remove('hidden'); | |
| } catch (error) { | |
| alert(`Failed to generate embeddings: ${error.message}`); | |
| console.error(error); | |
| } | |
| // Reset button | |
| generateBtn.disabled = false; | |
| generateBtn.innerHTML = '<i data-feather="zap" class="mr-2"></i> Generate Embeddings'; | |
| feather.replace(); | |
| }); | |
| clearBtn.addEventListener('click', () => { | |
| document.querySelectorAll('.text-input-group textarea').forEach(input => { | |
| input.value = ''; | |
| }); | |
| outputVector.textContent = ''; | |
| outputContainer.classList.add('hidden'); | |
| placeholder.classList.remove('hidden'); | |
| copyBtn.disabled = true; | |
| downloadBtn.classList.add('hidden'); | |
| }); | |
| copyBtn.addEventListener('click', () => { | |
| navigator.clipboard.writeText(outputVector.textContent) | |
| .then(() => { | |
| copyBtn.innerHTML = '<i data-feather="check" class="mr-1"></i> Copied!'; | |
| feather.replace(); | |
| setTimeout(() => { | |
| copyBtn.innerHTML = '<i data-feather="copy" class="mr-1"></i> Copy'; | |
| feather.replace(); | |
| }, 2000); | |
| }); | |
| }); | |
| downloadBtn.addEventListener('click', () => { | |
| const blob = new Blob([outputVector.textContent], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `embedding-${new Date().getTime()}.json`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| }); | |
| </script> | |
| </body> | |
| </html> |