Abeshith's picture
RAG Chatbot with LangChain, FastAPI, and service layer architecture
64d7fdf
const chatMessages = document.getElementById('chatMessages');
const messageInput = document.getElementById('messageInput');
const sendBtn = document.getElementById('sendBtn');
const uploadBtn = document.getElementById('uploadBtn');
const fileInput = document.getElementById('fileInput');
const documentList = document.getElementById('documentList');
const ragToggle = document.getElementById('ragToggle');
let sessionId = generateSessionId();
function generateSessionId() {
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
function addMessage(content, isUser = false) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${isUser ? 'user' : 'assistant'}`;
messageDiv.textContent = content;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
return messageDiv;
}
function addLoadingMessage() {
const messageDiv = document.createElement('div');
messageDiv.className = 'message loading';
messageDiv.textContent = 'Thinking...';
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
return messageDiv;
}
async function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
addMessage(message, true);
messageInput.value = '';
sendBtn.disabled = true;
const loadingMsg = addLoadingMessage();
try {
const response = await fetch('/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: message,
session_id: sessionId,
use_rag: ragToggle.checked
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
loadingMsg.remove();
const assistantMsg = addMessage('', false);
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.chunk) {
fullResponse += data.chunk;
assistantMsg.textContent = fullResponse;
chatMessages.scrollTop = chatMessages.scrollHeight;
}
if (data.error) {
assistantMsg.textContent = 'Error: ' + data.error;
assistantMsg.style.color = '#dc3545';
}
}
}
}
} catch (error) {
loadingMsg.remove();
addMessage('Error: ' + error.message, false);
} finally {
sendBtn.disabled = false;
messageInput.focus();
}
}
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
sendBtn.addEventListener('click', sendMessage);
uploadBtn.addEventListener('click', () => {
fileInput.click();
});
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
uploadBtn.disabled = true;
uploadBtn.textContent = 'Uploading...';
try {
const response = await fetch('/documents/upload', {
method: 'POST',
body: formData
});
if (response.ok) {
const data = await response.json();
addMessage(`Document "${data.filename}" uploaded successfully! (${data.chunk_count} chunks)`, false);
loadDocuments();
} else {
const error = await response.json();
addMessage('Upload failed: ' + error.detail, false);
}
} catch (error) {
addMessage('Upload error: ' + error.message, false);
} finally {
uploadBtn.disabled = false;
uploadBtn.textContent = 'Upload Document';
fileInput.value = '';
}
});
async function loadDocuments() {
try {
const response = await fetch('/documents/');
const documents = await response.json();
documentList.innerHTML = '';
documents.forEach(doc => {
const docDiv = document.createElement('div');
docDiv.className = 'document-item';
const nameSpan = document.createElement('span');
nameSpan.className = 'document-name';
nameSpan.textContent = doc.filename;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn-delete';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => deleteDocument(doc.id);
docDiv.appendChild(nameSpan);
docDiv.appendChild(deleteBtn);
documentList.appendChild(docDiv);
});
} catch (error) {
console.error('Failed to load documents:', error);
}
}
async function deleteDocument(docId) {
if (!confirm('Are you sure you want to delete this document?')) return;
try {
const response = await fetch(`/documents/${docId}`, {
method: 'DELETE'
});
if (response.ok) {
loadDocuments();
addMessage('Document deleted successfully', false);
}
} catch (error) {
addMessage('Delete failed: ' + error.message, false);
}
}
loadDocuments();