|
|
@@ -55,9 +55,20 @@ export async function sendChatStream(message, { conversationId, mode, onChunk, o
|
|
|
const decoder = new TextDecoder();
|
|
|
let buffer = "";
|
|
|
|
|
|
+ const STREAM_INACTIVITY_TIMEOUT_MS = 45_000;
|
|
|
+ function readWithTimeout() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const timer = setTimeout(() => reject(new Error("stream_inactivity_timeout")), STREAM_INACTIVITY_TIMEOUT_MS);
|
|
|
+ reader.read().then(
|
|
|
+ (result) => { clearTimeout(timer); resolve(result); },
|
|
|
+ (err) => { clearTimeout(timer); reject(err); }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
while (true) {
|
|
|
- const { done, value } = await reader.read();
|
|
|
+ const { done, value } = await readWithTimeout();
|
|
|
if (done) break;
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
|
const lines = buffer.split("\n");
|
|
|
@@ -77,6 +88,11 @@ export async function sendChatStream(message, { conversationId, mode, onChunk, o
|
|
|
}
|
|
|
} catch (e) {
|
|
|
if (e?.name === "AbortError") return;
|
|
|
+ if (e?.message === "stream_inactivity_timeout") {
|
|
|
+ reader.cancel().catch(() => {});
|
|
|
+ onError?.(new Error("A resposta demorou demais e foi interrompida."));
|
|
|
+ return;
|
|
|
+ }
|
|
|
onError?.(e);
|
|
|
}
|
|
|
|