ClaCe's picture
Upload index.html
3adba6f verified
<!DOCTYPE html>
<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>