Merge pull request #435 from langflow-ai/fix-nudges-errors
fix: Error handling for chats that are slow to respond
This commit is contained in:
commit
b322404814
2 changed files with 93 additions and 8 deletions
|
|
@ -71,6 +71,7 @@ function ChatPage() {
|
||||||
y: number;
|
y: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
||||||
|
const [waitingTooLong, setWaitingTooLong] = useState(false);
|
||||||
|
|
||||||
const chatInputRef = useRef<ChatInputHandle>(null);
|
const chatInputRef = useRef<ChatInputHandle>(null);
|
||||||
|
|
||||||
|
|
@ -87,11 +88,13 @@ function ChatPage() {
|
||||||
streamingMessage,
|
streamingMessage,
|
||||||
sendMessage: sendStreamingMessage,
|
sendMessage: sendStreamingMessage,
|
||||||
abortStream,
|
abortStream,
|
||||||
|
isLoading: isStreamLoading,
|
||||||
} = useChatStreaming({
|
} = useChatStreaming({
|
||||||
endpoint: apiEndpoint,
|
endpoint: apiEndpoint,
|
||||||
onComplete: (message, responseId) => {
|
onComplete: (message, responseId) => {
|
||||||
setMessages((prev) => [...prev, message]);
|
setMessages((prev) => [...prev, message]);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
setWaitingTooLong(false);
|
||||||
|
|
||||||
if (responseId) {
|
if (responseId) {
|
||||||
cancelNudges();
|
cancelNudges();
|
||||||
|
|
@ -111,6 +114,7 @@ function ChatPage() {
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("Streaming error:", error);
|
console.error("Streaming error:", error);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
setWaitingTooLong(false);
|
||||||
const errorMessage: Message = {
|
const errorMessage: Message = {
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content:
|
content:
|
||||||
|
|
@ -121,6 +125,23 @@ function ChatPage() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show warning if waiting too long (20 seconds)
|
||||||
|
useEffect(() => {
|
||||||
|
let timeoutId: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
if (isStreamLoading && !streamingMessage) {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
setWaitingTooLong(true);
|
||||||
|
}, 20000); // 20 seconds
|
||||||
|
} else {
|
||||||
|
setWaitingTooLong(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
};
|
||||||
|
}, [isStreamLoading, streamingMessage]);
|
||||||
|
|
||||||
const getCursorPosition = (textarea: HTMLTextAreaElement) => {
|
const getCursorPosition = (textarea: HTMLTextAreaElement) => {
|
||||||
// Create a hidden div with the same styles as the textarea
|
// Create a hidden div with the same styles as the textarea
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
|
|
@ -1310,6 +1331,19 @@ function ChatPage() {
|
||||||
isCompleted={false}
|
isCompleted={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Waiting too long indicator */}
|
||||||
|
{waitingTooLong && !streamingMessage && loading && (
|
||||||
|
<div className="pl-10 space-y-2">
|
||||||
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
<span>The server is taking longer than expected...</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
This may be due to high server load. The request will timeout after 60 seconds.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!streamingMessage && (
|
{!streamingMessage && (
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,10 @@ export function useChatStreaming({
|
||||||
limit = 10,
|
limit = 10,
|
||||||
scoreThreshold = 0,
|
scoreThreshold = 0,
|
||||||
}: SendMessageOptions) => {
|
}: SendMessageOptions) => {
|
||||||
|
// Set up timeout to detect stuck/hanging requests
|
||||||
|
let timeoutId: NodeJS.Timeout | null = null;
|
||||||
|
let hasReceivedData = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
|
|
@ -50,6 +54,20 @@ export function useChatStreaming({
|
||||||
streamAbortRef.current = controller;
|
streamAbortRef.current = controller;
|
||||||
const thisStreamId = ++streamIdRef.current;
|
const thisStreamId = ++streamIdRef.current;
|
||||||
|
|
||||||
|
// Set up timeout (60 seconds for initial response, then extended as data comes in)
|
||||||
|
const startTimeout = () => {
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
if (!hasReceivedData) {
|
||||||
|
console.error("Chat request timed out - no response received");
|
||||||
|
controller.abort();
|
||||||
|
throw new Error("Request timed out. The server is not responding.");
|
||||||
|
}
|
||||||
|
}, 60000); // 60 second timeout
|
||||||
|
};
|
||||||
|
|
||||||
|
startTimeout();
|
||||||
|
|
||||||
const requestBody: {
|
const requestBody: {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
stream: boolean;
|
stream: boolean;
|
||||||
|
|
@ -81,8 +99,13 @@ export function useChatStreaming({
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clear timeout once we get initial response
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
hasReceivedData = true;
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
const errorText = await response.text().catch(() => "Unknown error");
|
||||||
|
throw new Error(`Server error (${response.status}): ${errorText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = response.body?.getReader();
|
const reader = response.body?.getReader();
|
||||||
|
|
@ -113,6 +136,10 @@ export function useChatStreaming({
|
||||||
break;
|
break;
|
||||||
if (done) break;
|
if (done) break;
|
||||||
|
|
||||||
|
// Reset timeout on each chunk received
|
||||||
|
hasReceivedData = true;
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
|
||||||
buffer += decoder.decode(value, { stream: true });
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
|
||||||
// Process complete lines (JSON objects)
|
// Process complete lines (JSON objects)
|
||||||
|
|
@ -435,6 +462,12 @@ export function useChatStreaming({
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
reader.releaseLock();
|
reader.releaseLock();
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we got any content at all
|
||||||
|
if (!hasReceivedData || (!currentContent && currentFunctionCalls.length === 0)) {
|
||||||
|
throw new Error("No response received from the server. Please try again.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the message
|
// Finalize the message
|
||||||
|
|
@ -456,25 +489,43 @@ export function useChatStreaming({
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If stream was aborted, don't handle as error
|
// Clean up timeout
|
||||||
if (streamAbortRef.current?.signal.aborted) {
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
// If stream was aborted by user, don't handle as error
|
||||||
|
if (streamAbortRef.current?.signal.aborted && !(error as Error).message?.includes("timed out")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error("SSE Stream error:", error);
|
console.error("Chat stream error:", error);
|
||||||
setStreamingMessage(null);
|
setStreamingMessage(null);
|
||||||
|
|
||||||
|
// Create user-friendly error message
|
||||||
|
let errorContent = "Sorry, I couldn't connect to the chat service. Please try again.";
|
||||||
|
|
||||||
|
const errorMessage = (error as Error).message;
|
||||||
|
if (errorMessage?.includes("timed out")) {
|
||||||
|
errorContent = "The request timed out. The server took too long to respond. Please try again.";
|
||||||
|
} else if (errorMessage?.includes("No response")) {
|
||||||
|
errorContent = "The server didn't return a response. Please try again.";
|
||||||
|
} else if (errorMessage?.includes("NetworkError") || errorMessage?.includes("Failed to fetch")) {
|
||||||
|
errorContent = "Network error. Please check your connection and try again.";
|
||||||
|
} else if (errorMessage?.includes("Server error")) {
|
||||||
|
errorContent = errorMessage; // Use the detailed server error message
|
||||||
|
}
|
||||||
|
|
||||||
onError?.(error as Error);
|
onError?.(error as Error);
|
||||||
|
|
||||||
const errorMessage: Message = {
|
const errorMessageObj: Message = {
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content:
|
content: errorContent,
|
||||||
"Sorry, I couldn't connect to the chat service. Please try again.",
|
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
isStreaming: false,
|
isStreaming: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return errorMessage;
|
return errorMessageObj;
|
||||||
} finally {
|
} finally {
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue