|
const chatbox = document.getElementById('chatbox'); |
|
const messageInput = document.getElementById('message-input'); |
|
const sendButton = document.getElementById('send-button'); |
|
|
|
|
|
function addMessage(role, text) { |
|
const messageDiv = document.createElement('div'); |
|
messageDiv.classList.add('message', role === 'user' ? 'user-message' : 'assistant-message'); |
|
messageDiv.innerHTML = text.replace(/\n/g, '<br>'); |
|
chatbox.appendChild(messageDiv); |
|
chatbox.scrollTop = chatbox.scrollHeight; |
|
} |
|
|
|
|
|
function processSSEBuffer(buffer, targetDiv, currentResponseAccumulated) { |
|
console.log("Processing buffer:", buffer); |
|
let messages = buffer.split('\n\n'); |
|
let incomplete = buffer.endsWith('\n\n') ? '' : messages.pop(); |
|
let newResponsePart = ''; |
|
|
|
messages.forEach(message => { |
|
if (!message.trim()) return; |
|
let eventType = 'message'; |
|
let dataLines = []; |
|
message.split('\n').forEach(line => { |
|
if (line.startsWith('event: ')) { |
|
eventType = line.substring(7).trim(); |
|
} else if (line.startsWith('data: ')) { |
|
dataLines.push(line.substring(6)); |
|
} |
|
}); |
|
let data = dataLines.join('\n'); |
|
console.log(`--> SSE Event Parsed: ${eventType}, Data: ${data.substring(0, 100)}...`); |
|
|
|
if (eventType === 'message') { |
|
if (targetDiv.querySelector('.thinking')) { |
|
console.log("Replacing 'thinking' indicator"); |
|
targetDiv.innerHTML = ''; |
|
} |
|
newResponsePart += data; |
|
} else if (eventType === 'end') { |
|
console.log("Fin de stream détectée (event: end)"); |
|
sendButton.disabled = false; |
|
} else if (eventType === 'error') { |
|
console.error("Erreur SSE du serveur:", data); |
|
currentResponseAccumulated += `<br><strong style="color: #ffaaaa;">خطأ من الخادم: ${data}</strong>`; |
|
sendButton.disabled = false; |
|
} |
|
}); |
|
|
|
const updatedResponse = currentResponseAccumulated + newResponsePart; |
|
|
|
targetDiv.innerHTML = updatedResponse.replace(/\n/g, '<br>'); |
|
chatbox.scrollTop = chatbox.scrollHeight; |
|
|
|
|
|
return { incomplete: incomplete, updatedResponse: updatedResponse }; |
|
} |
|
|
|
|
|
function adjustTextareaHeight() { } |
|
|
|
|
|
|
|
async function askQuestion() { |
|
console.log("askQuestion called"); |
|
const question = messageInput.value.trim(); |
|
if (!question) { |
|
console.log("Question is empty, aborting."); |
|
return; |
|
} |
|
|
|
addMessage('user', question); |
|
messageInput.value = ''; |
|
sendButton.disabled = true; |
|
adjustTextareaHeight(); |
|
console.log("User message added, button disabled."); |
|
|
|
|
|
const assistantMessageDiv = document.createElement('div'); |
|
assistantMessageDiv.classList.add('message', 'assistant-message'); |
|
assistantMessageDiv.innerHTML = '<span class="thinking">...</span>'; |
|
chatbox.appendChild(assistantMessageDiv); |
|
chatbox.scrollTop = chatbox.scrollHeight; |
|
console.log("Assistant placeholder added."); |
|
|
|
let currentResponse = ""; |
|
let buffer = ''; |
|
const decoder = new TextDecoder(); |
|
|
|
try { |
|
console.log("Initiating fetch to /ask"); |
|
const response = await fetch('/ask', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Accept': 'text/event-stream' |
|
}, |
|
body: JSON.stringify({ question: question }) |
|
}); |
|
|
|
console.log(`Fetch response received. Status: ${response.status}, OK: ${response.ok}`); |
|
|
|
if (!response.ok || !response.body) { |
|
|
|
let errorBody = "N/A"; |
|
try { |
|
errorBody = await response.text(); |
|
} catch (e) {} |
|
throw new Error(`Erreur serveur: ${response.status} ${response.statusText}. Body: ${errorBody}`); |
|
} |
|
|
|
const reader = response.body.getReader(); |
|
console.log("ReadableStream reader obtained. Starting read loop."); |
|
|
|
while (true) { |
|
console.log("Calling reader.read()..."); |
|
const { done, value } = await reader.read(); |
|
|
|
if (done) { |
|
console.log("Reader finished (done=true). Processing remaining buffer."); |
|
|
|
const result = processSSEBuffer(buffer, assistantMessageDiv, currentResponse); |
|
currentResponse = result.updatedResponse; |
|
console.log("Final response after stream ended:", currentResponse.substring(0,100)+"..."); |
|
break; |
|
} |
|
|
|
|
|
const chunk = decoder.decode(value, { stream: true }); |
|
console.log("Received chunk:", chunk); |
|
buffer += chunk; |
|
|
|
|
|
console.log("Processing buffer with new chunk..."); |
|
const result = processSSEBuffer(buffer, assistantMessageDiv, currentResponse); |
|
currentResponse = result.updatedResponse; |
|
buffer = result.incomplete; |
|
console.log("Buffer remaining:", buffer); |
|
|
|
} |
|
|
|
} catch (error) { |
|
console.error('Erreur dans askQuestion (fetch ou stream):', error); |
|
|
|
if (assistantMessageDiv) { |
|
|
|
assistantMessageDiv.innerHTML += `<br><br><strong style="color: #ffaaaa;">حدث خطأ: ${error.message}</strong>`; |
|
chatbox.scrollTop = chatbox.scrollHeight; |
|
} else { |
|
|
|
addMessage('assistant', `<strong style="color: #ffaaaa;">حدث خطأ: ${error.message}</strong>`); |
|
} |
|
} finally { |
|
console.log("Executing finally block: Enabling button."); |
|
sendButton.disabled = false; |
|
} |
|
} |
|
|
|
|
|
|
|
sendButton.addEventListener('click', askQuestion); |
|
messageInput.addEventListener('keypress', function(e) { |
|
if (e.key === 'Enter' && !e.shiftKey) { |
|
e.preventDefault(); |
|
askQuestion(); |
|
} |
|
}); |
|
messageInput.addEventListener('input', adjustTextareaHeight); |
|
adjustTextareaHeight(); |
|
|
|
console.log("Chat script loaded and listeners attached."); |