try to fix streaming responses

This commit is contained in:
Dobromir Popov
2026-07-07 22:19:22 +03:00
parent a0b37ad1b9
commit f0dc3bd93f
4 changed files with 140 additions and 26 deletions

View File

@@ -1241,7 +1241,7 @@ async function sendChat() {
.map(msg => ({ role: msg.role, content: msg.content })),
{ role: "user", content: prompt },
],
stream: false,
stream: true,
max_tokens: 15120,
};
chatBusy = true;
@@ -1249,36 +1249,83 @@ async function sendChat() {
promptEl.value = "";
promptEl.style.height = "auto";
chatHistory.push({ role: "user", content: prompt, model: selectedChatModel });
const assistantMessage = { role: "assistant", content: "", model: selectedChatModel };
chatHistory.push(assistantMessage);
renderChatHistory();
persistActiveChatSession();
renderChatStatus("sending request…");
const r = await apiCall("/v1/chat/completions", "POST", body, bearerToken);
chatBusy = false;
$("chat-send").disabled = false;
if (!r.ok) {
const error = r.data && r.data.error
? (typeof r.data.error === "string" ? r.data.error : r.data.error.message || "request failed")
: "request failed";
chatHistory.push({ role: "error", content: error, model: selectedChatModel });
renderChatStatus("streaming response…");
try {
const headers = { "Content-Type": "application/json" };
if (bearerToken) headers["Authorization"] = "Bearer " + bearerToken;
const response = await fetch("/v1/chat/completions", {
method: "POST",
headers,
credentials: "same-origin",
body: JSON.stringify(body),
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
const error = data && data.error
? (typeof data.error === "string" ? data.error : data.error.message || "request failed")
: "request failed";
assistantMessage.role = "error";
assistantMessage.content = error;
renderChatHistory();
persistActiveChatSession();
renderChatStatus(error);
return;
}
if (!response.body) throw new Error("stream response body is missing");
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let receivedAny = false;
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split("\n\n");
buffer = events.pop() || "";
for (const eventText of events) {
for (const line of eventText.split("\n")) {
if (!line.startsWith("data: ")) continue;
const data = line.slice(6).trim();
if (data === "[DONE]") {
buffer = "";
break;
}
try {
const chunk = JSON.parse(data);
const delta = chunk.choices && chunk.choices[0] && chunk.choices[0].delta;
if (delta && delta.content) {
assistantMessage.content += delta.content;
receivedAny = true;
renderChatHistory();
persistActiveChatSession();
}
} catch {
// Ignore malformed SSE keepalive/event lines.
}
}
}
}
if (!receivedAny) assistantMessage.content = "(empty response)";
renderChatHistory();
persistActiveChatSession();
renderChatStatus(error);
renderChatStatus("done");
} catch (err) {
assistantMessage.role = "error";
assistantMessage.content = err && err.message ? err.message : "request failed";
renderChatHistory();
persistActiveChatSession();
renderChatStatus(assistantMessage.content);
} finally {
chatBusy = false;
$("chat-send").disabled = false;
promptEl.focus();
return;
}
const reply = (r.data && r.data.choices && r.data.choices[0] && r.data.choices[0].message && r.data.choices[0].message.content) || "";
const usage = r.data && r.data.usage;
chatHistory.push({
role: "assistant",
content: reply || "(empty response)",
model: selectedChatModel,
});
renderChatHistory();
persistActiveChatSession();
renderChatStatus(usage
? `done: ${usage.total_tokens ?? "?"} tokens`
: "done");
promptEl.focus();
}
function bindChatPromptShortcuts() {