mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-27 17:35:07 +01:00
Use async/await in web client chat stream instead of promises
Align streaming logic across web, desktop and obsidian clients
This commit is contained in:
parent
fafc467173
commit
e439a6ddac
1 changed files with 62 additions and 68 deletions
|
@ -598,8 +598,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
|||
}
|
||||
|
||||
async function chat(isVoice=false) {
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
|
||||
// Extract chat message from chat input form
|
||||
var query = document.getElementById("chat-input").value.trim();
|
||||
console.log(`Query: ${query}`);
|
||||
|
||||
|
@ -620,6 +619,16 @@ To get started, just start typing below. You can also type / to see a list of co
|
|||
autoResize();
|
||||
document.getElementById("chat-input").setAttribute("disabled", "disabled");
|
||||
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
let conversationID = chatBody.dataset.conversationId;
|
||||
if (!conversationID) {
|
||||
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST" });
|
||||
let data = await response.json();
|
||||
conversationID = data.conversation_id;
|
||||
chatBody.dataset.conversationId = conversationID;
|
||||
await refreshChatSessionsPanel();
|
||||
}
|
||||
|
||||
let newResponseEl = document.createElement("div");
|
||||
newResponseEl.classList.add("chat-message", "khoj");
|
||||
newResponseEl.attributes["data-meta"] = "🏮 Khoj at " + formatDate(new Date());
|
||||
|
@ -641,20 +650,37 @@ To get started, just start typing below. You can also type / to see a list of co
|
|||
let chatInput = document.getElementById("chat-input");
|
||||
chatInput.classList.remove("option-enabled");
|
||||
|
||||
// Call specified Khoj API
|
||||
await sendMessageStream(query);
|
||||
let rawResponse = "";
|
||||
let references = {};
|
||||
|
||||
// Setup chat message state
|
||||
chatMessageState = {
|
||||
newResponseTextEl,
|
||||
newResponseEl,
|
||||
loadingEllipsis,
|
||||
references,
|
||||
rawResponse,
|
||||
references: {},
|
||||
rawResponse: "",
|
||||
rawQuery: query,
|
||||
isVoice: isVoice,
|
||||
}
|
||||
|
||||
// Call Khoj chat API
|
||||
let chatApi = `/api/chat?q=${encodeURIComponent(query)}&conversation_id=${conversationID}&stream=true&client=web`;
|
||||
chatApi += (!!region && !!city && !!countryName && !!timezone)
|
||||
? `®ion=${region}&city=${city}&country=${countryName}&timezone=${timezone}`
|
||||
: '';
|
||||
|
||||
const response = await fetch(chatApi);
|
||||
|
||||
try {
|
||||
if (!response.ok) throw new Error(response.statusText);
|
||||
if (!response.body) throw new Error("Response body is empty");
|
||||
// Stream and render chat response
|
||||
await readChatStream(response);
|
||||
} catch (err) {
|
||||
console.error(`Khoj chat response failed with\n${err}`);
|
||||
if (chatMessageState.newResponseEl.getElementsByClassName("lds-ellipsis").length > 0 && chatMessageState.loadingEllipsis)
|
||||
chatMessageState.newResponseTextEl.removeChild(chatMessageState.loadingEllipsis);
|
||||
let errorMsg = "Sorry, unable to get response from Khoj backend ❤️🩹. Retry or contact developers for help at <a href=mailto:'team@khoj.dev'>team@khoj.dev</a> or <a href='https://discord.gg/BDgyabRM6e'>on Discord</a>";
|
||||
newResponseTextEl.innerHTML = errorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
function createLoadingEllipse() {
|
||||
|
@ -843,67 +869,35 @@ To get started, just start typing below. You can also type / to see a list of co
|
|||
}
|
||||
}
|
||||
|
||||
async function sendMessageStream(query) {
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
let conversationId = chatBody.dataset.conversationId;
|
||||
async function readChatStream(response) {
|
||||
if (!response.body) return;
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let netBracketCount = 0;
|
||||
|
||||
if (!conversationId) {
|
||||
let response = await fetch('/api/chat/sessions', { method: "POST" });
|
||||
let data = await response.json();
|
||||
conversationId = data.conversation_id;
|
||||
chatBody.dataset.conversationId = conversationId;
|
||||
refreshChatSessionsPanel();
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
// If the stream is done
|
||||
if (done) {
|
||||
// Process the last chunk
|
||||
processMessageChunk(buffer);
|
||||
buffer = '';
|
||||
break;
|
||||
}
|
||||
|
||||
// Read chunk from stream and append it to the buffer
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
buffer += chunk;
|
||||
|
||||
// Check if the buffer contains (0 or more) complete JSON objects
|
||||
netBracketCount += (chunk.match(/{/g) || []).length - (chunk.match(/}/g) || []).length;
|
||||
if (netBracketCount === 0) {
|
||||
let chunks = collectJsonsInBufferedMessageChunk(buffer);
|
||||
chunks.objects.forEach((chunk) => processMessageChunk(chunk));
|
||||
buffer = chunks.remainder;
|
||||
}
|
||||
}
|
||||
|
||||
let chatStreamUrl = `/api/chat?q=${encodeURIComponent(query)}&conversation_id=${conversationId}&stream=true&client=web`;
|
||||
chatStreamUrl += (!!region && !!city && !!countryName && !!timezone)
|
||||
? `®ion=${region}&city=${city}&country=${countryName}&timezone=${timezone}`
|
||||
: '';
|
||||
|
||||
fetch(chatStreamUrl)
|
||||
.then(response => {
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let netBracketCount = 0;
|
||||
|
||||
function readStream() {
|
||||
reader.read().then(({ done, value }) => {
|
||||
// If the stream is done
|
||||
if (done) {
|
||||
// Process the last chunk
|
||||
processMessageChunk(buffer);
|
||||
buffer = '';
|
||||
console.log("Stream complete");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read chunk from stream and append it to the buffer
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
buffer += chunk;
|
||||
|
||||
// Check if the buffer contains (0 or more) complete JSON objects
|
||||
netBracketCount += (chunk.match(/{/g) || []).length - (chunk.match(/}/g) || []).length;
|
||||
if (netBracketCount === 0) {
|
||||
let chunks = collectJsonsInBufferedMessageChunk(buffer);
|
||||
chunks.objects.forEach(processMessageChunk);
|
||||
buffer = chunks.remainder;
|
||||
}
|
||||
|
||||
// Continue reading the stream
|
||||
readStream();
|
||||
});
|
||||
}
|
||||
|
||||
readStream();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
if (chatMessageState.newResponseEl.getElementsByClassName("lds-ellipsis").length > 0 && chatMessageState.loadingEllipsis) {
|
||||
chatMessageState.newResponseTextEl.removeChild(chatMessageState.loadingEllipsis);
|
||||
}
|
||||
chatMessageState.newResponseTextEl.textContent += "Failed to get response! Try again or contact developers at team@khoj.dev"
|
||||
});
|
||||
}
|
||||
|
||||
function incrementalChat(event) {
|
||||
|
|
Loading…
Reference in a new issue