RAG-System / index.html
hardik-0212's picture
Upload index.html
55149b6 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG System - Document Search & Query</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
color: white;
margin-bottom: 30px;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
.main-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.card {
background: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
backdrop-filter: blur(10px);
}
.card h2 {
color: #333;
margin-bottom: 20px;
font-size: 1.5rem;
display: flex;
align-items: center;
gap: 10px;
}
.upload-area {
border: 3px dashed #667eea;
border-radius: 10px;
padding: 40px;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
}
.upload-area:hover {
border-color: #764ba2;
background: rgba(102, 126, 234, 0.05);
}
.upload-area.dragover {
border-color: #4CAF50;
background: rgba(76, 175, 80, 0.1);
}
.upload-icon {
font-size: 3rem;
color: #667eea;
margin-bottom: 15px;
}
.file-input {
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.upload-btn {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 30px;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
margin-top: 15px;
transition: transform 0.3s ease;
}
.upload-btn:hover {
transform: translateY(-2px);
}
.upload-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.query-section {
display: flex;
flex-direction: column;
gap: 15px;
}
.query-input {
width: 100%;
padding: 15px;
border: 2px solid #e1e5e9;
border-radius: 10px;
font-size: 1rem;
resize: vertical;
min-height: 100px;
transition: border-color 0.3s ease;
}
.query-input:focus {
outline: none;
border-color: #667eea;
}
.query-controls {
display: flex;
gap: 15px;
align-items: center;
}
.top-k-input {
padding: 8px 12px;
border: 2px solid #e1e5e9;
border-radius: 8px;
width: 80px;
}
.query-btn {
background: linear-gradient(45deg, #4CAF50, #45a049);
color: white;
border: none;
padding: 12px 25px;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
flex: 1;
transition: transform 0.3s ease;
}
.query-btn:hover {
transform: translateY(-2px);
}
.query-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.results-section {
background: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
display: none;
}
.results-section.visible {
display: block;
}
.answer-section {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.answer-text {
font-size: 1.1rem;
line-height: 1.6;
color: #333;
}
.confidence-score {
background: #4CAF50;
color: white;
padding: 5px 15px;
border-radius: 20px;
font-size: 0.9rem;
display: inline-block;
margin-top: 10px;
}
.sources-section {
margin-bottom: 20px;
}
.source-item {
background: #f8f9fa;
padding: 10px 15px;
border-left: 4px solid #667eea;
margin-bottom: 10px;
border-radius: 5px;
}
.chunks-section {
margin-top: 20px;
}
.chunk-item {
background: #ffffff;
border: 1px solid #e1e5e9;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
position: relative;
}
.chunk-score {
position: absolute;
top: 10px;
right: 10px;
background: #667eea;
color: white;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
}
.chunk-source {
font-weight: bold;
color: #764ba2;
margin-bottom: 8px;
}
.chunk-content {
color: #555;
line-height: 1.5;
}
.status-message {
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
font-weight: 500;
}
.status-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 15px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.file-list {
margin-top: 15px;
max-height: 150px;
overflow-y: auto;
}
.file-item {
background: #f8f9fa;
padding: 8px 12px;
border-radius: 5px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-name {
font-size: 0.9rem;
color: #333;
}
.file-size {
font-size: 0.8rem;
color: #666;
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
gap: 20px;
}
.header h1 {
font-size: 2rem;
}
.card {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>πŸ” RAG System</h1>
<p>Upload documents and query your knowledge base with AI-powered search</p>
</div>
<div class="main-content">
<div class="card">
<h2>πŸ“ Upload Documents</h2>
<div class="upload-area" id="uploadArea">
<div class="upload-icon">πŸ“„</div>
<h3>Drop files here or click to browse</h3>
<p>Supported: CSV, TXT, PDF, DOCX, XLSX</p>
<input type="file" id="fileInput" class="file-input" multiple accept=".csv,.txt,.pdf,.docx,.xlsx,.xls">
</div>
<button class="upload-btn" id="uploadBtn" disabled>Upload Files</button>
<div class="file-list" id="fileList"></div>
</div>
<div class="card">
<h2>❓ Ask Questions</h2>
<div class="query-section">
<textarea
class="query-input"
id="queryInput"
placeholder="Enter your question here... For example: 'What are the main findings in the uploaded documents?'"
></textarea>
<div class="query-controls">
<label for="topK">Results:</label>
<input type="number" id="topK" class="top-k-input" value="3" min="1" max="10">
<button class="query-btn" id="queryBtn">Search & Ask</button>
</div>
</div>
</div>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Processing...</p>
</div>
<div class="results-section" id="resultsSection">
<h2>πŸ“Š Results</h2>
<div id="resultsContent"></div>
</div>
</div>
<script>
const API_BASE = 'http://localhost:8000';
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
const fileList = document.getElementById('fileList');
const queryInput = document.getElementById('queryInput');
const queryBtn = document.getElementById('queryBtn');
const topKInput = document.getElementById('topK');
const loading = document.getElementById('loading');
const resultsSection = document.getElementById('resultsSection');
const resultsContent = document.getElementById('resultsContent');
let selectedFiles = [];
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
function handleFiles(files) {
selectedFiles = Array.from(files);
displayFileList();
uploadBtn.disabled = selectedFiles.length === 0;
}
function displayFileList() {
fileList.innerHTML = '';
selectedFiles.forEach((file, index) => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = `
<span class="file-name">${file.name}</span>
<span class="file-size">${formatFileSize(file.size)}</span>
`;
fileList.appendChild(fileItem);
});
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function showStatus(message, isError = false) {
const existingStatus = document.querySelector('.status-message');
if (existingStatus) existingStatus.remove();
const statusDiv = document.createElement('div');
statusDiv.className = `status-message ${isError ? 'status-error' : 'status-success'}`;
statusDiv.textContent = message;
document.querySelector('.container').insertBefore(statusDiv, document.querySelector('.main-content'));
setTimeout(() => statusDiv.remove(), 5000);
}
uploadBtn.addEventListener('click', async () => {
if (selectedFiles.length === 0) return;
const formData = new FormData();
selectedFiles.forEach(file => {
formData.append('files', file);
});
try {
loading.style.display = 'block';
uploadBtn.disabled = true;
const response = await fetch(`${API_BASE}/upload`, {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
showStatus(`Successfully uploaded and indexed ${selectedFiles.length} file(s)`);
selectedFiles = [];
fileList.innerHTML = '';
fileInput.value = '';
uploadBtn.disabled = true;
} else {
showStatus('Upload failed. Please try again.', true);
}
} catch (error) {
console.error('Upload error:', error);
showStatus('Upload failed. Check if the server is running.', true);
} finally {
loading.style.display = 'none';
uploadBtn.disabled = false;
}
});
queryBtn.addEventListener('click', async () => {
const question = queryInput.value.trim();
if (!question) {
showStatus('Please enter a question', true);
return;
}
try {
loading.style.display = 'block';
queryBtn.disabled = true;
resultsSection.classList.remove('visible');
const response = await fetch(`${API_BASE}/query`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
question: question,
top_k: parseInt(topKInput.value)
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
displayResults(result);
resultsSection.classList.add('visible');
} catch (error) {
console.error('Query error:', error);
showStatus('Query failed. Check if documents are uploaded and server is running.', true);
} finally {
loading.style.display = 'none';
queryBtn.disabled = false;
}
});
function displayResults(result) {
let html = '';
html += `
<div class="answer-section">
<h3>πŸ’‘ Answer</h3>
<div class="answer-text">${result.answer}</div>
<div class="confidence-score">Confidence: ${(result.confidence * 100).toFixed(1)}%</div>
</div>
`;
if (result.sources.length > 0) {
html += `
<div class="sources-section">
<h3>πŸ“š Sources</h3>
${result.sources.map(source => `
<div class="source-item">${source}</div>
`).join('')}
</div>
`;
}
if (result.chunks.length > 0) {
html += `
<div class="chunks-section">
<h3>πŸ“ Relevant Content</h3>
${result.chunks.map(chunk => `
<div class="chunk-item">
<div class="chunk-score">${(chunk.score * 100).toFixed(1)}%</div>
<div class="chunk-source">${chunk.source}</div>
<div class="chunk-content">${chunk.content}</div>
</div>
`).join('')}
</div>
`;
}
resultsContent.innerHTML = html;
}
queryInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && e.ctrlKey) {
queryBtn.click();
}
});
window.addEventListener('load', async () => {
try {
const response = await fetch(`${API_BASE}/`);
if (response.ok) {
showStatus('βœ… Connected to RAG server');
}
} catch (error) {
showStatus('⚠️ Cannot connect to server. Make sure FastAPI is running on port 8000.', true);
}
});
</script>
</body>
</html>