Merge remote-tracking branch 'origin/fix/watsonx_fixes' into all-merges
This commit is contained in:
commit
8a456bc605
2 changed files with 366 additions and 233 deletions
|
|
@ -73,8 +73,6 @@ function ChatPage() {
|
||||||
const lastLoadedConversationRef = useRef<string | null>(null);
|
const lastLoadedConversationRef = useRef<string | null>(null);
|
||||||
const { addTask } = useTask();
|
const { addTask } = useTask();
|
||||||
|
|
||||||
console.log(endpoint, refreshTrigger);
|
|
||||||
|
|
||||||
// Check if chat history is loading
|
// Check if chat history is loading
|
||||||
const { isLoading: isConversationsLoading } = useGetConversationsQuery(
|
const { isLoading: isConversationsLoading } = useGetConversationsQuery(
|
||||||
endpoint,
|
endpoint,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { X } from "lucide-react";
|
||||||
import { AnimatePresence, motion } from "motion/react";
|
import { AnimatePresence, motion } from "motion/react";
|
||||||
import { type ChangeEvent, useEffect, useRef, useState } from "react";
|
import { type ChangeEvent, useEffect, useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
@ -24,6 +25,10 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
const [uploadedTaskId, setUploadedTaskId] = useState<string | null>(null);
|
const [uploadedTaskId, setUploadedTaskId] = useState<string | null>(null);
|
||||||
const [shouldCreateFilter, setShouldCreateFilter] = useState(false);
|
const [shouldCreateFilter, setShouldCreateFilter] = useState(false);
|
||||||
const [isCreatingFilter, setIsCreatingFilter] = useState(false);
|
const [isCreatingFilter, setIsCreatingFilter] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Track which tasks we've already handled to prevent infinite loops
|
||||||
|
const handledFailedTasksRef = useRef<Set<string>>(new Set());
|
||||||
|
|
||||||
const createFilterMutation = useCreateFilter();
|
const createFilterMutation = useCreateFilter();
|
||||||
|
|
||||||
|
|
@ -40,8 +45,6 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
refetchInterval: currentStep !== null ? 1000 : false, // Poll every 1 second during upload
|
refetchInterval: currentStep !== null ? 1000 : false, // Poll every 1 second during upload
|
||||||
});
|
});
|
||||||
|
|
||||||
const { refetch: refetchNudges } = useGetNudgesQuery(null);
|
|
||||||
|
|
||||||
// Monitor tasks and call onComplete when file processing is done
|
// Monitor tasks and call onComplete when file processing is done
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentStep === null || !tasks || !uploadedTaskId) {
|
if (currentStep === null || !tasks || !uploadedTaskId) {
|
||||||
|
|
@ -56,14 +59,109 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip if this task was already handled as a failed task (from a previous failed upload)
|
||||||
|
// This prevents processing old failed tasks when a new upload starts
|
||||||
|
if (handledFailedTasksRef.current.has(matchingTask.task_id)) {
|
||||||
|
// Check if it's a failed task that we've already handled
|
||||||
|
const hasFailedFile =
|
||||||
|
matchingTask.files &&
|
||||||
|
Object.values(matchingTask.files).some(
|
||||||
|
(file) => file.status === "failed" || file.status === "error",
|
||||||
|
);
|
||||||
|
if (hasFailedFile) {
|
||||||
|
// This is an old failed task that we've already handled, ignore it
|
||||||
|
console.log(
|
||||||
|
"Skipping already-handled failed task:",
|
||||||
|
matchingTask.task_id,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If it's not a failed task, remove it from handled list (it might have succeeded on retry)
|
||||||
|
handledFailedTasksRef.current.delete(matchingTask.task_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any file failed in the matching task
|
||||||
|
const hasFailedFile = (() => {
|
||||||
|
// Must have files object
|
||||||
|
if (!matchingTask.files || typeof matchingTask.files !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileEntries = Object.values(matchingTask.files);
|
||||||
|
|
||||||
|
// Must have at least one file
|
||||||
|
if (fileEntries.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any file has failed status
|
||||||
|
return fileEntries.some(
|
||||||
|
(file) => file.status === "failed" || file.status === "error",
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// If any file failed, show error and jump back one step (like onboarding-card.tsx)
|
||||||
|
// Only handle if we haven't already handled this task
|
||||||
|
if (
|
||||||
|
hasFailedFile &&
|
||||||
|
!isCreatingFilter &&
|
||||||
|
!handledFailedTasksRef.current.has(matchingTask.task_id)
|
||||||
|
) {
|
||||||
|
console.error("File failed in task, jumping back one step", matchingTask);
|
||||||
|
|
||||||
|
// Mark this task as handled to prevent infinite loops
|
||||||
|
handledFailedTasksRef.current.add(matchingTask.task_id);
|
||||||
|
|
||||||
|
// Extract error messages from failed files
|
||||||
|
const errorMessages: string[] = [];
|
||||||
|
if (matchingTask.files) {
|
||||||
|
Object.values(matchingTask.files).forEach((file) => {
|
||||||
|
if (
|
||||||
|
(file.status === "failed" || file.status === "error") &&
|
||||||
|
file.error
|
||||||
|
) {
|
||||||
|
errorMessages.push(file.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check task-level error
|
||||||
|
if (matchingTask.error) {
|
||||||
|
errorMessages.push(matchingTask.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the first error message, or a generic message if no errors found
|
||||||
|
const errorMessage =
|
||||||
|
errorMessages.length > 0
|
||||||
|
? errorMessages[0]
|
||||||
|
: "Document failed to ingest. Please try again with a different file.";
|
||||||
|
|
||||||
|
// Set error message and jump back one step
|
||||||
|
setError(errorMessage);
|
||||||
|
setCurrentStep(STEP_LIST.length);
|
||||||
|
|
||||||
|
// Clear filter creation flags since ingestion failed
|
||||||
|
setShouldCreateFilter(false);
|
||||||
|
setUploadedFilename(null);
|
||||||
|
|
||||||
|
// Jump back one step after 1 second (go back to upload step)
|
||||||
|
setTimeout(() => {
|
||||||
|
setCurrentStep(null);
|
||||||
|
}, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the matching task is still active (pending, running, or processing)
|
// Check if the matching task is still active (pending, running, or processing)
|
||||||
const isTaskActive =
|
const isTaskActive =
|
||||||
matchingTask.status === "pending" ||
|
matchingTask.status === "pending" ||
|
||||||
matchingTask.status === "running" ||
|
matchingTask.status === "running" ||
|
||||||
matchingTask.status === "processing";
|
matchingTask.status === "processing";
|
||||||
|
|
||||||
// If task is completed or has processed files, complete the onboarding step
|
// If task is completed successfully (no failures) and has processed files, complete the onboarding step
|
||||||
if (!isTaskActive || (matchingTask.processed_files ?? 0) > 0) {
|
if (
|
||||||
|
(!isTaskActive || (matchingTask.processed_files ?? 0) > 0) &&
|
||||||
|
!hasFailedFile
|
||||||
|
) {
|
||||||
// Set to final step to show "Done"
|
// Set to final step to show "Done"
|
||||||
setCurrentStep(STEP_LIST.length);
|
setCurrentStep(STEP_LIST.length);
|
||||||
|
|
||||||
|
|
@ -157,11 +255,23 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUploadClick = () => {
|
const handleUploadClick = () => {
|
||||||
|
// Clear any previous error when user clicks to upload again
|
||||||
|
setError(null);
|
||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
const performUpload = async (file: File) => {
|
const performUpload = async (file: File) => {
|
||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
|
// Clear any previous error when starting a new upload
|
||||||
|
setError(null);
|
||||||
|
// Clear handled tasks ref to allow retry
|
||||||
|
handledFailedTasksRef.current.clear();
|
||||||
|
// Reset task ID to prevent matching old failed tasks
|
||||||
|
setUploadedTaskId(null);
|
||||||
|
// Clear filter creation flags
|
||||||
|
setShouldCreateFilter(false);
|
||||||
|
setUploadedFilename(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setCurrentStep(0);
|
setCurrentStep(0);
|
||||||
const result = await uploadFile(file, true, true); // Pass createFilter=true
|
const result = await uploadFile(file, true, true); // Pass createFilter=true
|
||||||
|
|
@ -183,7 +293,8 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
setCurrentStep(1);
|
setCurrentStep(1);
|
||||||
}, 1500);
|
}, 1500);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : "Upload failed";
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Upload failed";
|
||||||
console.error("Upload failed", errorMessage);
|
console.error("Upload failed", errorMessage);
|
||||||
|
|
||||||
// Dispatch event that chat context can listen to
|
// Dispatch event that chat context can listen to
|
||||||
|
|
@ -205,6 +316,9 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
// Reset on error
|
// Reset on error
|
||||||
setCurrentStep(null);
|
setCurrentStep(null);
|
||||||
setUploadedTaskId(null);
|
setUploadedTaskId(null);
|
||||||
|
setError(errorMessage);
|
||||||
|
setShouldCreateFilter(false);
|
||||||
|
setUploadedFilename(null);
|
||||||
} finally {
|
} finally {
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -238,6 +352,24 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
exit={{ opacity: 0, y: -24 }}
|
exit={{ opacity: 0, y: -24 }}
|
||||||
transition={{ duration: 0.4, ease: "easeInOut" }}
|
transition={{ duration: 0.4, ease: "easeInOut" }}
|
||||||
>
|
>
|
||||||
|
<div className="w-full flex flex-col gap-4">
|
||||||
|
<AnimatePresence mode="wait">
|
||||||
|
{error && (
|
||||||
|
<motion.div
|
||||||
|
key="error"
|
||||||
|
initial={{ opacity: 1, y: 0, height: "auto" }}
|
||||||
|
exit={{ opacity: 0, y: -10, height: 0 }}
|
||||||
|
>
|
||||||
|
<div className="pb-2 flex items-center gap-4">
|
||||||
|
<X className="w-4 h-4 text-destructive shrink-0" />
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{error}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
<div>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -246,6 +378,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
>
|
>
|
||||||
<div>{isUploading ? "Uploading..." : "Add a document"}</div>
|
<div>{isUploading ? "Uploading..." : "Add a document"}</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -253,6 +386,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
className="hidden"
|
className="hidden"
|
||||||
accept=".pdf,.doc,.docx,.txt,.md,.rtf,.odt"
|
accept=".pdf,.doc,.docx,.txt,.md,.rtf,.odt"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
) : (
|
) : (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -267,6 +401,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
isCompleted={false}
|
isCompleted={false}
|
||||||
steps={STEP_LIST}
|
steps={STEP_LIST}
|
||||||
storageKey={ONBOARDING_UPLOAD_STEPS_KEY}
|
storageKey={ONBOARDING_UPLOAD_STEPS_KEY}
|
||||||
|
hasError={!!error}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue