Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ML/AI Use Cases Assistant</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#3b82f6', | |
| secondary: '#8b5cf6', | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| </head> | |
| <body class="bg-gradient-to-br from-blue-50 via-white to-purple-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Header --> | |
| <div class="text-center mb-12"> | |
| <div class="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full mb-6"> | |
| <i class="fas fa-brain text-white text-2xl"></i> | |
| </div> | |
| <h1 class="text-4xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent mb-4"> | |
| ML/AI Use Cases Assistant | |
| </h1> | |
| <p class="text-gray-600 text-lg max-w-2xl mx-auto"> | |
| Get AI-powered advice for your business problems based on real implementations from 60+ companies with 400+ use cases | |
| </p> | |
| </div> | |
| <!-- API Key Section --> | |
| <div class="bg-white rounded-2xl shadow-xl p-8 mb-8 border border-gray-100"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-10 h-10 bg-gradient-to-r from-red-400 to-orange-500 rounded-lg flex items-center justify-center mr-4"> | |
| <i class="fas fa-key text-white"></i> | |
| </div> | |
| <h2 class="text-2xl font-bold text-gray-800">API Key Required</h2> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="apiKey" class="block text-sm font-semibold text-gray-700 mb-3"> | |
| <i class="fas fa-shield-alt text-blue-500 mr-2"></i> | |
| Enter your HuggingFace API Key | |
| </label> | |
| <div class="relative"> | |
| <input | |
| type="password" | |
| id="apiKey" | |
| placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | |
| class="w-full px-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-700" | |
| /> | |
| <div id="keyStatus" class="absolute right-3 top-3 hidden"> | |
| <i class="fas fa-check-circle text-green-500" id="keyValid"></i> | |
| <i class="fas fa-times-circle text-red-500" id="keyInvalid"></i> | |
| <i class="fas fa-spinner fa-spin text-blue-500" id="keyValidating"></i> | |
| </div> | |
| </div> | |
| <div id="keyError" class="mt-2 text-sm text-red-600 hidden"></div> | |
| </div> | |
| <div class="bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="text-sm text-blue-800 mb-2"> | |
| <i class="fas fa-info-circle mr-2"></i> | |
| <strong>Don't have an API key?</strong> | |
| </p> | |
| <ol class="text-sm text-blue-700 space-y-1 ml-4"> | |
| <li>1. Go to <a href="https://huggingface.co/settings/tokens" target="_blank" class="underline">HuggingFace Settings</a></li> | |
| <li>2. Click "Create new token"</li> | |
| <li>3. Select "Read" access and create</li> | |
| <li>4. Copy and paste the token above</li> | |
| </ol> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main App (Initially Disabled) --> | |
| <div id="mainApp" class="opacity-50 pointer-events-none"> | |
| <!-- Search Form --> | |
| <div class="bg-white rounded-2xl shadow-xl p-8 mb-8 border border-gray-100"> | |
| <form id="chatForm" class="space-y-6"> | |
| <div class="relative"> | |
| <label for="query" class="block text-sm font-semibold text-gray-700 mb-3"> | |
| <i class="fas fa-question-circle text-blue-500 mr-2"></i> | |
| What business problem would you like to solve? | |
| </label> | |
| <textarea | |
| id="query" | |
| name="query" | |
| rows="4" | |
| class="w-full px-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none text-gray-700 placeholder-gray-400" | |
| placeholder="e.g., I want to reduce customer churn in my SaaS business..." | |
| required | |
| ></textarea> | |
| </div> | |
| <button | |
| type="submit" | |
| id="submitBtn" | |
| class="w-full bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-semibold py-4 px-6 rounded-lg transition-all duration-200 transform hover:scale-[1.02] shadow-lg" | |
| > | |
| <i class="fas fa-magic mr-2"></i> | |
| Get AI-Powered Solution | |
| </button> | |
| </form> | |
| </div> | |
| <!-- Loading State --> | |
| <div id="loading" class="hidden text-center py-12"> | |
| <div class="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mb-4"></div> | |
| <p class="text-gray-600 text-lg">Analyzing your problem and searching through 400+ use cases...</p> | |
| <p class="text-sm text-gray-500 mt-2">This may take a moment while the AI processes your request</p> | |
| </div> | |
| <!-- Processing Logs --> | |
| <div id="processingLogs" class="hidden max-w-4xl mx-auto mb-8"> | |
| <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-100"> | |
| <div class="flex items-center mb-4"> | |
| <div class="w-8 h-8 bg-gradient-to-r from-green-400 to-blue-500 rounded-lg flex items-center justify-center mr-3"> | |
| <i class="fas fa-terminal text-white text-sm"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold text-gray-800">Processing Details</h3> | |
| </div> | |
| <div id="logMessages" class="bg-gray-900 text-green-400 p-4 rounded-lg font-mono text-sm max-h-64 overflow-y-auto"> | |
| <!-- Logs will be inserted here --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results Container --> | |
| <div id="results" class="hidden space-y-8"> | |
| <!-- Solution Approach --> | |
| <div class="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-10 h-10 bg-gradient-to-r from-green-400 to-blue-500 rounded-lg flex items-center justify-center mr-4"> | |
| <i class="fas fa-lightbulb text-white"></i> | |
| </div> | |
| <h2 class="text-2xl font-bold text-gray-800">AI-Powered Solution Approach</h2> | |
| </div> | |
| <div id="solutionContent" class="prose prose-gray max-w-none"> | |
| <!-- Solution content will be inserted here --> | |
| </div> | |
| </div> | |
| <!-- Company Examples --> | |
| <div class="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-10 h-10 bg-gradient-to-r from-purple-400 to-pink-500 rounded-lg flex items-center justify-center mr-4"> | |
| <i class="fas fa-building text-white"></i> | |
| </div> | |
| <h2 class="text-2xl font-bold text-gray-800">Real Company Examples</h2> | |
| </div> | |
| <div id="examplesContent" class="space-y-4"> | |
| <!-- Examples will be inserted here --> | |
| </div> | |
| </div> | |
| <!-- Recommended Models --> | |
| <div class="bg-white rounded-2xl shadow-xl p-8 border border-gray-100"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-10 h-10 bg-gradient-to-r from-orange-400 to-red-500 rounded-lg flex items-center justify-center mr-4"> | |
| <i class="fas fa-cogs text-white"></i> | |
| </div> | |
| <h2 class="text-2xl font-bold text-gray-800">Recommended ML Models</h2> | |
| </div> | |
| <!-- Fine-tuned Models Section --> | |
| <div id="fineTunedSection" class="mb-8"> | |
| <h3 class="text-lg font-semibold text-gray-700 mb-4 flex items-center"> | |
| <i class="fas fa-bullseye text-purple-500 mr-2"></i> | |
| Fine-tuned & Specialized Models | |
| </h3> | |
| <p class="text-sm text-gray-600 mb-4">Ready-to-use models specifically trained for your type of problem</p> | |
| <div id="fineTunedModels" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6"> | |
| <!-- Fine-tuned models will be inserted here --> | |
| </div> | |
| </div> | |
| <!-- General Models Section --> | |
| <div id="generalSection"> | |
| <h3 class="text-lg font-semibold text-gray-700 mb-4 flex items-center"> | |
| <i class="fas fa-tools text-blue-500 mr-2"></i> | |
| General Foundation Models | |
| </h3> | |
| <p class="text-sm text-gray-600 mb-4">Base models you can fine-tune for your specific use case</p> | |
| <div id="generalModels" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | |
| <!-- General models will be inserted here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Error Message --> | |
| <div id="error" class="hidden bg-red-50 border border-red-200 rounded-lg p-4 mb-8"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-exclamation-triangle text-red-500 mr-2"></i> | |
| <p class="text-red-700" id="errorMessage"></p> | |
| </div> | |
| </div> | |
| </div> <!-- Close mainApp --> | |
| </div> | |
| <script> | |
| // Global variables - NO localStorage persistence for security | |
| let userApiKey = ''; | |
| let isApiKeyValid = false; | |
| // DOM elements | |
| const chatForm = document.getElementById('chatForm'); | |
| const loading = document.getElementById('loading'); | |
| const processingLogs = document.getElementById('processingLogs'); | |
| const logMessages = document.getElementById('logMessages'); | |
| const results = document.getElementById('results'); | |
| const error = document.getElementById('error'); | |
| const submitBtn = document.getElementById('submitBtn'); | |
| const apiKeyInput = document.getElementById('apiKey'); | |
| const keyStatus = document.getElementById('keyStatus'); | |
| const keyValid = document.getElementById('keyValid'); | |
| const keyInvalid = document.getElementById('keyInvalid'); | |
| const keyValidating = document.getElementById('keyValidating'); | |
| const keyError = document.getElementById('keyError'); | |
| const mainApp = document.getElementById('mainApp'); | |
| // Initialize on page load - no pre-filling for security | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Always start with empty API key field | |
| apiKeyInput.value = ''; | |
| userApiKey = ''; | |
| isApiKeyValid = false; | |
| disableMainApp(); | |
| }); | |
| // API Key validation | |
| apiKeyInput.addEventListener('input', debounce(async (e) => { | |
| const apiKey = e.target.value.trim(); | |
| if (apiKey.length < 10) { | |
| resetKeyStatus(); | |
| return; | |
| } | |
| await validateApiKey(apiKey); | |
| }, 500)); | |
| async function validateApiKey(apiKey) { | |
| if (!apiKey || apiKey.length < 10) { | |
| resetKeyStatus(); | |
| return false; | |
| } | |
| showKeyStatus('validating'); | |
| try { | |
| const response = await fetch('/validate-key', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ api_key: apiKey }), | |
| }); | |
| const data = await response.json(); | |
| console.log('Validation response:', { status: response.status, ok: response.ok, data }); | |
| if (response.ok && data.valid) { | |
| console.log('✅ API key validated successfully'); | |
| showKeyStatus('valid'); | |
| userApiKey = apiKey; | |
| // NO localStorage.setItem for security - key only stored in memory | |
| isApiKeyValid = true; | |
| enableMainApp(); | |
| return true; | |
| } else { | |
| console.log('❌ API key validation failed:', data); | |
| showKeyStatus('invalid', data.error || 'Invalid API key'); | |
| isApiKeyValid = false; | |
| disableMainApp(); | |
| return false; | |
| } | |
| } catch (err) { | |
| console.error('Key validation error:', err); | |
| showKeyStatus('invalid', 'Failed to validate API key. Please check your internet connection.'); | |
| isApiKeyValid = false; | |
| disableMainApp(); | |
| return false; | |
| } | |
| } | |
| function showKeyStatus(status, errorMessage = '') { | |
| keyStatus.classList.remove('hidden'); | |
| keyValid.classList.add('hidden'); | |
| keyInvalid.classList.add('hidden'); | |
| keyValidating.classList.add('hidden'); | |
| keyError.classList.add('hidden'); | |
| if (status === 'valid') { | |
| keyValid.classList.remove('hidden'); | |
| keyError.classList.add('hidden'); | |
| } else if (status === 'invalid') { | |
| keyInvalid.classList.remove('hidden'); | |
| if (errorMessage) { | |
| keyError.textContent = errorMessage; | |
| keyError.classList.remove('hidden'); | |
| } | |
| } else if (status === 'validating') { | |
| keyValidating.classList.remove('hidden'); | |
| } | |
| } | |
| function resetKeyStatus() { | |
| keyStatus.classList.add('hidden'); | |
| keyError.classList.add('hidden'); | |
| isApiKeyValid = false; | |
| disableMainApp(); | |
| } | |
| function enableMainApp() { | |
| mainApp.classList.remove('opacity-50', 'pointer-events-none'); | |
| } | |
| function disableMainApp() { | |
| mainApp.classList.add('opacity-50', 'pointer-events-none'); | |
| } | |
| function debounce(func, wait) { | |
| let timeout; | |
| return function executedFunction(...args) { | |
| const later = () => { | |
| clearTimeout(timeout); | |
| func(...args); | |
| }; | |
| clearTimeout(timeout); | |
| timeout = setTimeout(later, wait); | |
| }; | |
| } | |
| chatForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| if (!isApiKeyValid) { | |
| showError('Please enter a valid HuggingFace API key first.'); | |
| return; | |
| } | |
| const query = document.getElementById('query').value.trim(); | |
| if (!query) return; | |
| // Show loading state | |
| loading.classList.remove('hidden'); | |
| results.classList.add('hidden'); | |
| error.classList.add('hidden'); | |
| submitBtn.disabled = true; | |
| submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Processing...'; | |
| try { | |
| const response = await fetch('/chat', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-HF-API-Key': userApiKey, | |
| }, | |
| body: JSON.stringify({ query: query }), | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.detail || `HTTP error! status: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| displayResults(data); | |
| } catch (err) { | |
| console.error('Error:', err); | |
| showError(err.message || 'Failed to get response. Please check your API key and try again.'); | |
| } finally { | |
| // Hide loading state | |
| loading.classList.add('hidden'); | |
| submitBtn.disabled = false; | |
| submitBtn.innerHTML = '<i class="fas fa-magic mr-2"></i>Get AI-Powered Solution'; | |
| } | |
| }); | |
| function displayResults(data) { | |
| // Display processing logs first | |
| if (data.logs && data.logs.length > 0) { | |
| logMessages.innerHTML = ''; | |
| data.logs.forEach(log => { | |
| const logDiv = document.createElement('div'); | |
| logDiv.className = 'text-green-400 mb-1'; | |
| logDiv.textContent = log; | |
| logMessages.appendChild(logDiv); | |
| }); | |
| processingLogs.classList.remove('hidden'); | |
| } | |
| // Display solution approach | |
| const solutionContent = document.getElementById('solutionContent'); | |
| solutionContent.innerHTML = formatText(data.solution_approach); | |
| // Display company examples | |
| const examplesContent = document.getElementById('examplesContent'); | |
| examplesContent.innerHTML = ''; | |
| data.company_examples.forEach((example, index) => { | |
| const exampleCard = document.createElement('div'); | |
| exampleCard.className = 'bg-gradient-to-r from-gray-50 to-blue-50 rounded-lg p-6 border border-gray-200'; | |
| exampleCard.innerHTML = ` | |
| <div class="flex items-start justify-between mb-3"> | |
| <h3 class="font-bold text-lg text-gray-800">${example.company}</h3> | |
| <span class="bg-blue-100 text-blue-800 text-sm font-medium px-2.5 py-0.5 rounded">${example.year}</span> | |
| </div> | |
| <p class="text-sm text-purple-600 font-medium mb-2">${example.industry}</p> | |
| <p class="text-gray-700 mb-3">${example.description}</p> | |
| <div class="text-sm text-gray-600"> | |
| <p class="leading-relaxed">${example.summary}</p> | |
| </div> | |
| <div class="mt-3 flex items-center justify-between"> | |
| <span class="text-xs text-gray-500">Similarity: ${(example.similarity_score * 100).toFixed(1)}%</span> | |
| ${example.url ? `<a href="${example.url}" target="_blank" class="inline-flex items-center text-xs text-blue-600 hover:text-blue-800 font-medium transition-colors"> | |
| <i class="fas fa-external-link-alt mr-1"></i> | |
| Read Article | |
| </a>` : ''} | |
| </div> | |
| `; | |
| examplesContent.appendChild(exampleCard); | |
| }); | |
| // Display recommended models | |
| const fineTunedModels = document.getElementById('fineTunedModels'); | |
| const generalModels = document.getElementById('generalModels'); | |
| fineTunedModels.innerHTML = ''; | |
| generalModels.innerHTML = ''; | |
| if (data.recommended_models) { | |
| // Display fine-tuned models | |
| if (data.recommended_models.fine_tuned && data.recommended_models.fine_tuned.length > 0) { | |
| data.recommended_models.fine_tuned.forEach(model => { | |
| const modelCard = createModelCard(model, 'purple'); | |
| fineTunedModels.appendChild(modelCard); | |
| }); | |
| } else { | |
| fineTunedModels.innerHTML = '<p class="text-gray-500 col-span-full text-center py-4">No specialized models found</p>'; | |
| } | |
| // Display general models | |
| if (data.recommended_models.general && data.recommended_models.general.length > 0) { | |
| data.recommended_models.general.forEach(model => { | |
| const modelCard = createModelCard(model, 'blue'); | |
| generalModels.appendChild(modelCard); | |
| }); | |
| } else { | |
| generalModels.innerHTML = '<p class="text-gray-500 col-span-full text-center py-4">No general models found</p>'; | |
| } | |
| } | |
| results.classList.remove('hidden'); | |
| } | |
| function createModelCard(model, colorTheme) { | |
| const modelCard = document.createElement('div'); | |
| const colorClasses = { | |
| 'purple': 'from-purple-50 to-indigo-50 border-purple-200 text-purple-600', | |
| 'blue': 'from-blue-50 to-cyan-50 border-blue-200 text-blue-600' | |
| }; | |
| modelCard.className = `bg-gradient-to-br ${colorClasses[colorTheme]} rounded-lg p-4 border hover:shadow-md transition-shadow`; | |
| modelCard.innerHTML = ` | |
| <div class="flex items-start justify-between mb-2"> | |
| <h4 class="font-semibold text-gray-800 text-sm">${model.name.split('/').pop()}</h4> | |
| <span class="text-xs bg-white px-2 py-1 rounded ${colorTheme === 'purple' ? 'text-purple-600' : 'text-blue-600'}">${model.type}</span> | |
| </div> | |
| <p class="text-xs text-gray-600 mb-2">${model.description}</p> | |
| <p class="text-xs ${colorTheme === 'purple' ? 'text-purple-600' : 'text-blue-600'} mb-2 capitalize">${model.task.replace('-', ' ')}</p> | |
| <p class="text-xs text-gray-500 mb-3">Downloads: ${model.downloads.toLocaleString()}</p> | |
| <a href="${model.url}" target="_blank" | |
| class="inline-flex items-center text-xs ${colorTheme === 'purple' ? 'text-purple-600 hover:text-purple-800' : 'text-blue-600 hover:text-blue-800'} font-medium"> | |
| View Model <i class="fas fa-external-link-alt ml-1"></i> | |
| </a> | |
| `; | |
| return modelCard; | |
| } | |
| function formatText(text) { | |
| // Simple text formatting - convert newlines to paragraphs | |
| return text.split('\n\n').map(paragraph => | |
| `<p class="mb-4 text-gray-700 leading-relaxed">${paragraph.trim()}</p>` | |
| ).join(''); | |
| } | |
| function showError(message) { | |
| document.getElementById('errorMessage').textContent = message; | |
| error.classList.remove('hidden'); | |
| } | |
| </script> | |
| </body> | |
| </html> |