ERI-LLM / static /index.html
avakanski's picture
Upload 6 files
40f6083 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>US Army RAG System</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap" rel="stylesheet">
<style>
:root {
--primary: #4F46E5;
--primary-hover: #4338CA;
--bg-dark: #0F172A;
--bg-card: #1E293B;
--text-main: #F8FAFC;
--text-muted: #94A3B8;
--accent: #10B981;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Outfit', sans-serif;
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
background-image: radial-gradient(circle at top right, #1e1b4b 0%, transparent 40%),
radial-gradient(circle at bottom left, #064e3b 0%, transparent 40%);
}
.container {
width: 100%;
max-width: 800px;
margin-top: 4rem;
}
header {
text-align: center;
margin-bottom: 3rem;
animation: fadeIn 0.8s ease-out;
}
h1 {
font-size: 3rem;
font-weight: 600;
background: linear-gradient(to right, #818cf8, #34d399);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 0.5rem;
}
.subtitle {
color: var(--text-muted);
font-size: 1.1rem;
}
.search-box {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 1.5rem;
border-radius: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
margin-bottom: 2rem;
position: relative;
animation: slideUp 0.8s ease-out;
}
.input-group {
display: flex;
gap: 1rem;
position: relative;
}
input[type="text"] {
width: 100%;
padding: 1rem 1.5rem;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 1rem;
color: white;
font-size: 1.1rem;
transition: all 0.3s ease;
}
input[type="text"]:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2);
}
button {
padding: 1rem 2rem;
background: var(--primary);
color: white;
border: none;
border-radius: 1rem;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
}
button:hover {
background: var(--primary-hover);
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(79, 70, 229, 0.3);
}
button:active {
transform: translateY(0);
}
button:disabled {
background: var(--text-muted);
cursor: not-allowed;
transform: none;
}
#loading {
display: none;
text-align: center;
margin: 2rem 0;
color: var(--primary);
}
.spinner {
width: 30px;
height: 30px;
border: 3px solid rgba(255, 255, 255, 0.1);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
#results {
display: none;
animation: fadeIn 0.5s ease-out;
}
.answer-card {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 1.5rem;
padding: 2rem;
margin-bottom: 1.5rem;
}
.section-title {
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.answer-text {
line-height: 1.7;
font-size: 1.1rem;
color: #E2E8F0;
white-space: pre-wrap;
}
.sources-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
margin-top: 2rem;
}
.source-card {
background: rgba(15, 23, 42, 0.5);
padding: 1.5rem;
border-radius: 1rem;
border: 1px solid rgba(255, 255, 255, 0.05);
transition: transform 0.2s ease;
}
.source-card:hover {
transform: translateY(-2px);
background: rgba(15, 23, 42, 0.8);
}
.source-title {
font-weight: 600;
margin-bottom: 0.5rem;
color: #93C5FD;
font-size: 0.95rem;
}
.source-meta {
font-size: 0.85rem;
color: var(--text-muted);
margin-bottom: 0.8rem;
}
.source-excerpt {
font-size: 0.9rem;
color: #CBD5E1;
line-height: 1.5;
font-style: italic;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: var(--bg-card);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Medical Research RAG</h1>
<p class="subtitle">AI-Powered Question Answering System</p>
</header>
<div class="search-box">
<div class="input-group">
<input type="text" id="questionInput" placeholder="Ask a question about medical research..." autocomplete="off">
<button id="searchBtn" onclick="askQuestion()">
Ask AI
</button>
</div>
</div>
<div id="loading">
<div class="spinner"></div>
<p>Analyzing medical papers...</p>
</div>
<div id="results">
<div class="answer-card">
<div class="section-title">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>
AI Consolidated Answer
</div>
<div id="answerText" class="answer-text"></div>
</div>
<div class="sources-list">
<div class="section-title">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"/></svg>
Reference Sources
</div>
<div id="sourcesGrid" class="sources-grid"></div>
</div>
</div>
</div>
<script>
const input = document.getElementById('questionInput');
const searchBtn = document.getElementById('searchBtn');
const loading = document.getElementById('loading');
const results = document.getElementById('results');
const answerText = document.getElementById('answerText');
const sourcesGrid = document.getElementById('sourcesGrid');
// Allow Enter key to submit
input.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
askQuestion();
}
});
async function askQuestion() {
const question = input.value.trim();
if (!question) return;
// UI State updates
searchBtn.disabled = true;
input.disabled = true;
results.style.display = 'none';
loading.style.display = 'block';
try {
const response = await fetch('/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
question: question,
top_k: 5
})
});
if (!response.ok) {
throw new Error('Failed to get response');
}
const data = await response.json();
// Display results
answerText.textContent = data.answer;
// Clear and populate sources
sourcesGrid.innerHTML = '';
data.sources.forEach(source => {
const card = document.createElement('div');
card.className = 'source-card';
card.innerHTML = `
<div class="source-title">${source.metadata.title || 'Untitled Document'}</div>
<div class="source-meta">
${source.metadata.journal || 'Unknown Journal'}${source.metadata.year || 'N/A'}
</div>
<div class="source-excerpt">"...${source.text}..."</div>
`;
sourcesGrid.appendChild(card);
});
results.style.display = 'block';
} catch (error) {
alert('Error querying the system. Please try again.');
console.error(error);
} finally {
loading.style.display = 'none';
searchBtn.disabled = false;
input.disabled = false;
input.focus();
}
}
</script>
</body>
</html>