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:
pushkala-datastax 2025-11-19 14:22:04 -08:00 committed by GitHub
commit b322404814
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 93 additions and 8 deletions

View file

@ -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 && (

View file

@ -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);
} }
}; };