llm-chat / index.html
rajkumar218's picture
Add 3 files
a6cbede verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Chat Interface</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.chat-container {
height: calc(100vh - 200px);
}
.message-animation {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.slide-in {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.pulse {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="container mx-auto max-w-6xl p-4">
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Header -->
<div class="bg-indigo-600 text-white p-4 flex justify-between items-center">
<h1 class="text-xl font-bold">LLM Chat Interface</h1>
<button id="config-btn" class="bg-indigo-700 hover:bg-indigo-800 px-4 py-2 rounded-lg transition">
<i class="fas fa-cog mr-2"></i>Configure
</button>
</div>
<!-- Configuration Panel -->
<div id="config-panel" class="hidden bg-gray-50 p-4 border-b border-gray-200 slide-in">
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2">LLM Provider</label>
<select id="provider-select" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="">Select a provider</option>
<option value="azure-openai">Azure OpenAI</option>
<option value="groq">Groq</option>
<option value="openai">OpenAI</option>
<option value="anthropic">Anthropic</option>
<option value="cohere">Cohere</option>
</select>
</div>
<!-- Dynamic Credential Form -->
<div id="credential-form" class="space-y-4">
<!-- Form will be dynamically populated based on provider -->
</div>
<div class="flex justify-end mt-4">
<button id="save-config" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg transition">
Save Configuration
</button>
</div>
</div>
<!-- Chat Area -->
<div class="flex flex-col">
<div id="chat-messages" class="chat-container p-4 overflow-y-auto space-y-4">
<div class="text-center text-gray-500 py-10">
<i class="fas fa-comments text-4xl mb-2"></i>
<p>Configure your LLM provider to start chatting</p>
</div>
</div>
<!-- Input Area -->
<div class="p-4 border-t border-gray-200 bg-white">
<div id="connection-status" class="hidden mb-2 flex items-center">
<span class="w-3 h-3 rounded-full mr-2 bg-gray-400"></span>
<span class="text-sm text-gray-600">Disconnected</span>
</div>
<div class="flex space-x-2">
<input id="message-input" type="text" placeholder="Type your message..."
class="flex-1 p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 disabled:opacity-50" disabled>
<button id="send-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg transition disabled:opacity-50" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const configBtn = document.getElementById('config-btn');
const configPanel = document.getElementById('config-panel');
const providerSelect = document.getElementById('provider-select');
const credentialForm = document.getElementById('credential-form');
const saveConfigBtn = document.getElementById('save-config');
const chatMessages = document.getElementById('chat-messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const connectionStatus = document.getElementById('connection-status');
// Current configuration
let currentConfig = {
provider: null,
credentials: null,
connected: false
};
// Provider configuration templates
const providerConfigs = {
'azure-openai': {
name: 'Azure OpenAI',
fields: [
{ id: 'api-key', label: 'API Key', type: 'password', required: true },
{ id: 'endpoint', label: 'Endpoint URL', type: 'text', placeholder: 'https://your-resource.openai.azure.com/', required: true },
{ id: 'deployment-name', label: 'Deployment Name', type: 'text', required: true },
{ id: 'api-version', label: 'API Version', type: 'text', value: '2023-05-15', required: true }
]
},
'groq': {
name: 'Groq',
fields: [
{ id: 'api-key', label: 'API Key', type: 'password', required: true },
{ id: 'model', label: 'Model', type: 'text', value: 'mixtral-8x7b-32768', required: true }
]
},
'openai': {
name: 'OpenAI',
fields: [
{ id: 'api-key', label: 'API Key', type: 'password', required: true },
{ id: 'model', label: 'Model', type: 'text', value: 'gpt-4', required: true }
]
},
'anthropic': {
name: 'Anthropic',
fields: [
{ id: 'api-key', label: 'API Key', type: 'password', required: true },
{ id: 'model', label: 'Model', type: 'text', value: 'claude-2', required: true }
]
},
'cohere': {
name: 'Cohere',
fields: [
{ id: 'api-key', label: 'API Key', type: 'password', required: true },
{ id: 'model', label: 'Model', type: 'text', value: 'command', required: true }
]
}
};
// Toggle configuration panel
configBtn.addEventListener('click', function() {
configPanel.classList.toggle('hidden');
});
// Handle provider selection change
providerSelect.addEventListener('change', function() {
const selectedProvider = this.value;
if (!selectedProvider) {
credentialForm.innerHTML = '';
return;
}
const provider = providerConfigs[selectedProvider];
let formHTML = `<h3 class="font-medium text-gray-700 mb-3">${provider.name} Configuration</h3>`;
provider.fields.forEach(field => {
formHTML += `
<div class="mb-3">
<label for="${field.id}" class="block text-sm font-medium text-gray-700 mb-1">${field.label}</label>
<input type="${field.type}" id="${field.id}"
class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
${field.placeholder ? `placeholder="${field.placeholder}"` : ''}
${field.value ? `value="${field.value}"` : ''}
${field.required ? 'required' : ''}>
</div>
`;
});
credentialForm.innerHTML = formHTML;
});
// Save configuration
saveConfigBtn.addEventListener('click', function() {
const selectedProvider = providerSelect.value;
if (!selectedProvider) {
alert('Please select a provider');
return;
}
const provider = providerConfigs[selectedProvider];
const credentials = {};
let isValid = true;
provider.fields.forEach(field => {
const input = document.getElementById(field.id);
if (field.required && !input.value.trim()) {
input.classList.add('border-red-500');
isValid = false;
} else {
input.classList.remove('border-red-500');
credentials[field.id] = input.value.trim();
}
});
if (!isValid) {
alert('Please fill in all required fields');
return;
}
// Save configuration
currentConfig.provider = selectedProvider;
currentConfig.credentials = credentials;
// Test connection (simulated)
testConnection();
// Close config panel
configPanel.classList.add('hidden');
// Add system message
addMessage('system', `Configuration saved for ${provider.name}. You can now start chatting.`);
});
// Simulate connection test
function testConnection() {
connectionStatus.classList.remove('hidden');
const statusDot = connectionStatus.querySelector('span:first-child');
const statusText = connectionStatus.querySelector('span:last-child');
statusDot.classList.remove('bg-green-500', 'bg-red-500');
statusDot.classList.add('bg-yellow-500', 'pulse');
statusText.textContent = 'Connecting...';
// Simulate API call
setTimeout(() => {
statusDot.classList.remove('bg-yellow-500', 'pulse');
// For demo purposes, we'll assume connection is successful
currentConfig.connected = true;
statusDot.classList.add('bg-green-500');
statusText.textContent = 'Connected';
// Enable chat
messageInput.disabled = false;
sendBtn.disabled = false;
}, 1500);
}
// Add message to chat
function addMessage(role, content) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message-animation');
if (role === 'user') {
messageDiv.innerHTML = `
<div class="flex justify-end">
<div class="bg-indigo-600 text-white rounded-lg p-3 max-w-3/4">
<p>${content}</p>
</div>
</div>
`;
} else if (role === 'assistant') {
messageDiv.innerHTML = `
<div class="flex justify-start">
<div class="bg-gray-200 text-gray-800 rounded-lg p-3 max-w-3/4">
<p>${content}</p>
</div>
</div>
`;
} else { // system
messageDiv.innerHTML = `
<div class="flex justify-center">
<div class="bg-gray-100 text-gray-600 text-sm rounded-lg p-2 max-w-3/4">
<p>${content}</p>
</div>
</div>
`;
}
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Handle send message
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
function sendMessage() {
const message = messageInput.value.trim();
if (!message || !currentConfig.connected) return;
// Add user message
addMessage('user', message);
messageInput.value = '';
// Show typing indicator
const typingIndicator = document.createElement('div');
typingIndicator.id = 'typing-indicator';
typingIndicator.innerHTML = `
<div class="flex justify-start">
<div class="bg-gray-200 text-gray-800 rounded-lg p-3 max-w-3/4">
<div class="flex space-x-2">
<div class="w-2 h-2 rounded-full bg-gray-500 animate-bounce" style="animation-delay: 0s"></div>
<div class="w-2 h-2 rounded-full bg-gray-500 animate-bounce" style="animation-delay: 0.2s"></div>
<div class="w-2 h-2 rounded-full bg-gray-500 animate-bounce" style="animation-delay: 0.4s"></div>
</div>
</div>
</div>
`;
chatMessages.appendChild(typingIndicator);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Simulate API call to backend
const apiEndpoint = '/api/chat'; // Your FastAPI endpoint
const requestData = {
provider: currentConfig.provider,
credentials: currentConfig.credentials,
message: message
};
// In a real implementation, you would use fetch or axios
console.log('Sending to backend:', requestData);
// Simulate API response after delay
setTimeout(() => {
// Remove typing indicator
const indicator = document.getElementById('typing-indicator');
if (indicator) chatMessages.removeChild(indicator);
// For demo purposes, we'll use a mock response
const mockResponses = {
'azure-openai': `I'm your Azure OpenAI assistant. How can I help you today?`,
'groq': `I'm powered by Groq's ultra-fast LLMs. What would you like to know?`,
'openai': `Hello! I'm an OpenAI model. How can I assist you?`,
'anthropic': `Greetings! I'm Claude from Anthropic. How may I help?`,
'cohere': `I'm using Cohere's language model. What's on your mind?`
};
const response = mockResponses[currentConfig.provider] ||
"I'm your AI assistant. How can I help you today?";
addMessage('assistant', response);
}, 1500);
}
// Initial setup
if (localStorage.getItem('llmConfig')) {
try {
currentConfig = JSON.parse(localStorage.getItem('llmConfig'));
if (currentConfig.provider) {
providerSelect.value = currentConfig.provider;
const event = new Event('change');
providerSelect.dispatchEvent(event);
// Populate fields with saved values
if (currentConfig.credentials) {
for (const [key, value] of Object.entries(currentConfig.credentials)) {
const input = document.getElementById(key);
if (input) input.value = value;
}
}
// Test connection
testConnection();
}
} catch (e) {
console.error('Failed to load config:', e);
}
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=rajkumar218/llm-chat" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>