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> |