pdf_chat_interface.html•18.8 kB
<!DOCTYPE html>
<html>
<head>
<title>PDF Chat Interface - MCP System</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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%);
color: white;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
background: rgba(255,255,255,0.1);
padding: 30px;
border-radius: 15px;
backdrop-filter: blur(10px);
}
.upload-section, .chat-section {
background: rgba(255,255,255,0.1);
padding: 30px;
border-radius: 15px;
margin-bottom: 30px;
backdrop-filter: blur(10px);
}
.file-drop-zone {
border: 3px dashed rgba(255,255,255,0.5);
border-radius: 15px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
margin-bottom: 20px;
}
.file-drop-zone:hover {
border-color: #4CAF50;
background: rgba(76, 175, 80, 0.1);
}
.file-drop-zone.dragover {
border-color: #4CAF50;
background: rgba(76, 175, 80, 0.2);
}
.file-input {
display: none;
}
.btn {
background: #4CAF50;
color: white;
padding: 15px 30px;
border: none;
border-radius: 10px;
font-size: 16px;
cursor: pointer;
margin: 5px;
transition: all 0.3s;
}
.btn:hover {
background: #45a049;
transform: translateY(-2px);
}
.btn:disabled {
background: #666;
cursor: not-allowed;
transform: none;
}
.chat-input {
width: 100%;
padding: 15px;
font-size: 16px;
border: none;
border-radius: 10px;
margin-bottom: 15px;
background: rgba(255,255,255,0.9);
color: #333;
outline: none;
}
.chat-messages {
max-height: 400px;
overflow-y: auto;
background: rgba(255,255,255,0.05);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.message {
margin-bottom: 15px;
padding: 15px;
border-radius: 10px;
animation: fadeIn 0.5s ease-in;
}
.message.user {
background: rgba(76, 175, 80, 0.3);
margin-left: 20px;
}
.message.assistant {
background: rgba(33, 150, 243, 0.3);
margin-right: 20px;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.document-info {
background: rgba(255,255,255,0.1);
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
}
.loading {
text-align: center;
padding: 20px;
}
.spinner {
border: 4px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top: 4px solid #4CAF50;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 10px;
color: white;
font-weight: bold;
z-index: 1000;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.notification.success { background: #4CAF50; }
.notification.error { background: #f44336; }
.back-btn {
background: #2196F3;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📄 PDF Chat Interface</h1>
<p>Upload PDF documents and chat with them using AI</p>
<button class="btn back-btn" onclick="window.location.href='/'">🏠 Back to Main Interface</button>
</div>
<div class="upload-section">
<h2>📁 Upload PDF Document</h2>
<div class="file-drop-zone" id="fileDropZone" onclick="document.getElementById('fileInput').click()">
<div>
<p style="font-size: 24px; margin-bottom: 10px;">📄</p>
<p style="font-size: 18px; margin-bottom: 10px;">Drop PDF here or click to browse</p>
<p style="font-size: 14px; opacity: 0.8;">Supports PDF files up to 50MB</p>
</div>
</div>
<input type="file" id="fileInput" class="file-input" accept=".pdf">
<div id="uploadStatus" style="display: none;">
<div class="loading">
<div class="spinner"></div>
<p>Uploading and processing PDF...</p>
</div>
</div>
<div id="documentInfo" class="document-info" style="display: none;">
<h3>📄 Uploaded Document</h3>
<p><strong>Filename:</strong> <span id="docFilename"></span></p>
<p><strong>File Size:</strong> <span id="docSize"></span></p>
<p><strong>Text Length:</strong> <span id="docTextLength"></span> characters</p>
<p><strong>Upload Time:</strong> <span id="docUploadTime"></span></p>
</div>
</div>
<div class="chat-section" id="chatSection" style="display: none;">
<h2>💬 Chat with Your PDF</h2>
<div class="chat-messages" id="chatMessages">
<div class="message assistant">
<strong>🤖 Assistant:</strong> Hello! I've processed your PDF document. Ask me any questions about its content!
</div>
</div>
<div>
<input type="text" id="chatInput" class="chat-input" placeholder="Ask a question about your PDF document..." />
<button id="sendBtn" class="btn">🚀 Send Question</button>
<button id="clearBtn" class="btn" style="background: #ff9800;">🗑️ Clear Chat</button>
</div>
<h3 style="margin-top: 20px;">💡 Example Questions:</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; margin-top: 15px;">
<button class="btn" onclick="askQuestion('What is the main topic of this document?')" style="background: #9c27b0;">📋 Main Topic</button>
<button class="btn" onclick="askQuestion('Summarize the key points')" style="background: #9c27b0;">📝 Summary</button>
<button class="btn" onclick="askQuestion('What are the important dates mentioned?')" style="background: #9c27b0;">📅 Dates</button>
<button class="btn" onclick="askQuestion('List any numbers or statistics')" style="background: #9c27b0;">📊 Statistics</button>
</div>
</div>
</div>
<script>
let currentPdfId = null;
let sessionId = 'session_' + Date.now();
let isProcessing = false;
// Initialize
document.addEventListener('DOMContentLoaded', function() {
setupEventListeners();
});
function setupEventListeners() {
// File input change
document.getElementById('fileInput').addEventListener('change', handleFileSelect);
// Drag and drop
const dropZone = document.getElementById('fileDropZone');
dropZone.addEventListener('dragover', handleDragOver);
dropZone.addEventListener('drop', handleDrop);
dropZone.addEventListener('dragleave', handleDragLeave);
// Chat input
document.getElementById('chatInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !isProcessing) {
sendQuestion();
}
});
// Send button
document.getElementById('sendBtn').addEventListener('click', sendQuestion);
// Clear button
document.getElementById('clearBtn').addEventListener('click', clearChat);
}
function handleDragOver(e) {
e.preventDefault();
e.currentTarget.classList.add('dragover');
}
function handleDragLeave(e) {
e.currentTarget.classList.remove('dragover');
}
function handleDrop(e) {
e.preventDefault();
e.currentTarget.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFile(files[0]);
}
}
function handleFileSelect(e) {
const files = e.target.files;
if (files.length > 0) {
handleFile(files[0]);
}
}
async function handleFile(file) {
if (!file.name.toLowerCase().endsWith('.pdf')) {
showNotification('Please select a PDF file', 'error');
return;
}
if (file.size > 50 * 1024 * 1024) { // 50MB limit
showNotification('File too large. Please select a PDF under 50MB', 'error');
return;
}
// Show upload status
document.getElementById('uploadStatus').style.display = 'block';
document.getElementById('documentInfo').style.display = 'none';
document.getElementById('chatSection').style.display = 'none';
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload/pdf', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status === 'success') {
currentPdfId = result.file_id;
// Show document info
document.getElementById('docFilename').textContent = result.filename;
document.getElementById('docSize').textContent = formatFileSize(file.size);
document.getElementById('docTextLength').textContent = result.text_length;
document.getElementById('docUploadTime').textContent = new Date().toLocaleString();
document.getElementById('uploadStatus').style.display = 'none';
document.getElementById('documentInfo').style.display = 'block';
document.getElementById('chatSection').style.display = 'block';
showNotification('PDF uploaded and processed successfully!', 'success');
// Focus chat input
document.getElementById('chatInput').focus();
} else {
throw new Error(result.message || 'Upload failed');
}
} catch (error) {
document.getElementById('uploadStatus').style.display = 'none';
showNotification('Upload failed: ' + error.message, 'error');
}
}
function askQuestion(question) {
document.getElementById('chatInput').value = question;
sendQuestion();
}
async function sendQuestion() {
if (isProcessing) {
showNotification('Please wait for the current question to be processed', 'error');
return;
}
const question = document.getElementById('chatInput').value.trim();
if (!question) {
showNotification('Please enter a question', 'error');
return;
}
if (!currentPdfId) {
showNotification('Please upload a PDF first', 'error');
return;
}
isProcessing = true;
document.getElementById('sendBtn').disabled = true;
document.getElementById('sendBtn').innerHTML = '⏳ Processing...';
// Add user message
addMessage('user', question);
// Clear input
document.getElementById('chatInput').value = '';
try {
const response = await fetch('/api/chat/pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
question: question,
pdf_id: currentPdfId,
session_id: sessionId
})
});
const result = await response.json();
if (result.status === 'success') {
let answer = 'I processed your question about the PDF document.';
let answerDetails = '';
// Check if LangChain RAG was used
if (result.rag_enabled && result.llm_powered) {
answerDetails = '🧠 <strong>LangChain RAG Powered</strong><br>';
} else if (result.fallback_reason) {
answerDetails = `⚠️ <strong>Fallback Mode:</strong> ${result.fallback_reason}<br>`;
}
// Check if content was chunked
if (result.content_chunked) {
answerDetails += '📄 <strong>Smart Chunking:</strong> Large document processed with intelligent content selection<br>';
}
// Get the main answer
if (result.answer) {
answer = result.answer;
} else if (result.message) {
answer = result.message;
} else if (result.result) {
answer = `Answer: ${result.result}`;
} else if (result.total_documents) {
answer = `I analyzed ${result.total_documents} document(s) to answer your question.`;
}
// Add document summary if available
if (result.document_summary) {
answerDetails += `📄 <strong>Document Summary:</strong> ${result.document_summary}<br><br>`;
}
// Combine details and answer
const fullAnswer = answerDetails + answer;
addMessage('assistant', fullAnswer);
// Show appropriate notification
if (result.rag_enabled) {
showNotification('Question answered using LangChain RAG!', 'success');
} else {
showNotification('Question answered successfully!', 'success');
}
} else {
addMessage('assistant', `Sorry, I couldn't process your question: ${result.message}`);
showNotification('Failed to process question', 'error');
}
} catch (error) {
addMessage('assistant', `Sorry, there was an error processing your question: ${error.message}`);
showNotification('Error processing question', 'error');
} finally {
isProcessing = false;
document.getElementById('sendBtn').disabled = false;
document.getElementById('sendBtn').innerHTML = '🚀 Send Question';
document.getElementById('chatInput').focus();
}
}
function addMessage(type, content) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
const icon = type === 'user' ? '👤' : '🤖';
const label = type === 'user' ? 'You' : 'Assistant';
messageDiv.innerHTML = `<strong>${icon} ${label}:</strong> ${content}`;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
function clearChat() {
const messagesContainer = document.getElementById('chatMessages');
messagesContainer.innerHTML = `
<div class="message assistant">
<strong>🤖 Assistant:</strong> Chat cleared! Ask me any questions about your PDF document.
</div>
`;
}
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 showNotification(message, type = 'success') {
// Remove existing notifications
const existing = document.querySelector('.notification');
if (existing) {
existing.remove();
}
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
// Auto-remove after 3 seconds
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 3000);
}
</script>
</body>
</html>