fix: added empty message on file upload handling, fixed time calculation on onboarding (#316)
* fixed miscalculation of provider steps * Handled empty message with file * fixed same key
This commit is contained in:
parent
31681bb0d9
commit
a7af519d01
6 changed files with 90 additions and 53 deletions
|
|
@ -594,8 +594,8 @@ export function Navigation({
|
||||||
No documents yet
|
No documents yet
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
newConversationFiles?.map((file) => (
|
newConversationFiles?.map((file, index) => (
|
||||||
<div key={`${file}`} className="flex-1 min-w-0 px-3">
|
<div key={`${file}-${index}`} className="flex-1 min-w-0 px-3">
|
||||||
<div className="text-mmd font-medium text-foreground truncate">
|
<div className="text-mmd font-medium text-foreground truncate">
|
||||||
{file}
|
{file}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { cn } from "@/lib/utils";
|
||||||
import { Message } from "./message";
|
import { Message } from "./message";
|
||||||
|
|
||||||
interface UserMessageProps {
|
interface UserMessageProps {
|
||||||
content: string;
|
content: string | undefined;
|
||||||
isCompleted?: boolean;
|
isCompleted?: boolean;
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
files?: string;
|
files?: string;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { type EndpointType, useChat } from "@/contexts/chat-context";
|
||||||
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
||||||
import { useTask } from "@/contexts/task-context";
|
import { useTask } from "@/contexts/task-context";
|
||||||
import { useChatStreaming } from "@/hooks/useChatStreaming";
|
import { useChatStreaming } from "@/hooks/useChatStreaming";
|
||||||
import { FILES_REGEX } from "@/lib/constants";
|
import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants";
|
||||||
import { useLoadingStore } from "@/stores/loadingStore";
|
import { useLoadingStore } from "@/stores/loadingStore";
|
||||||
import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery";
|
import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery";
|
||||||
import { AssistantMessage } from "./components/assistant-message";
|
import { AssistantMessage } from "./components/assistant-message";
|
||||||
|
|
@ -911,9 +911,9 @@ function ChatPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only send message if there's input text
|
// Only send message if there's input text
|
||||||
if (input.trim()) {
|
if (input.trim() || uploadedFile) {
|
||||||
// Pass the responseId from upload (if any) to handleSendMessage
|
// Pass the responseId from upload (if any) to handleSendMessage
|
||||||
handleSendMessage(input, uploadedResponseId || undefined);
|
handleSendMessage(!input.trim() ? FILE_CONFIRMATION : input, uploadedResponseId || undefined);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1154,6 +1154,8 @@ function ChatPage() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(messages)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Debug header - only show in debug mode */}
|
{/* Debug header - only show in debug mode */}
|
||||||
|
|
@ -1236,7 +1238,10 @@ function ChatPage() {
|
||||||
? message.source !== "langflow"
|
? message.source !== "langflow"
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
content={message.content}
|
content={index >= 2
|
||||||
|
&& (messages[index - 2]?.content.match(
|
||||||
|
FILES_REGEX,
|
||||||
|
)?.[0] ?? undefined) && message.content === FILE_CONFIRMATION ? undefined : message.content}
|
||||||
files={
|
files={
|
||||||
index >= 2
|
index >= 2
|
||||||
? messages[index - 2]?.content.match(
|
? messages[index - 2]?.content.match(
|
||||||
|
|
|
||||||
|
|
@ -19,34 +19,30 @@ export function AnimatedProviderSteps({
|
||||||
setCurrentStep,
|
setCurrentStep,
|
||||||
steps,
|
steps,
|
||||||
storageKey = "provider-steps",
|
storageKey = "provider-steps",
|
||||||
|
processingStartTime,
|
||||||
}: {
|
}: {
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
isCompleted: boolean;
|
isCompleted: boolean;
|
||||||
setCurrentStep: (step: number) => void;
|
setCurrentStep: (step: number) => void;
|
||||||
steps: string[];
|
steps: string[];
|
||||||
storageKey?: string;
|
storageKey?: string;
|
||||||
|
processingStartTime?: number | null;
|
||||||
}) {
|
}) {
|
||||||
const [startTime, setStartTime] = useState<number | null>(null);
|
const [startTime, setStartTime] = useState<number | null>(null);
|
||||||
const [elapsedTime, setElapsedTime] = useState<number>(0);
|
const [elapsedTime, setElapsedTime] = useState<number>(0);
|
||||||
|
|
||||||
// Initialize start time from local storage or set new one
|
// Initialize start time from prop or local storage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const storedStartTime = localStorage.getItem(`${storageKey}-start`);
|
|
||||||
const storedElapsedTime = localStorage.getItem(`${storageKey}-elapsed`);
|
const storedElapsedTime = localStorage.getItem(`${storageKey}-elapsed`);
|
||||||
|
|
||||||
if (isCompleted && storedElapsedTime) {
|
if (isCompleted && storedElapsedTime) {
|
||||||
// If completed, use stored elapsed time
|
// If completed, use stored elapsed time
|
||||||
setElapsedTime(parseFloat(storedElapsedTime));
|
setElapsedTime(parseFloat(storedElapsedTime));
|
||||||
} else if (storedStartTime) {
|
} else if (processingStartTime) {
|
||||||
// If in progress, use stored start time
|
// Use the start time passed from parent (when user clicked Complete)
|
||||||
setStartTime(parseInt(storedStartTime));
|
setStartTime(processingStartTime);
|
||||||
} else {
|
|
||||||
// First time, set new start time
|
|
||||||
const now = Date.now();
|
|
||||||
setStartTime(now);
|
|
||||||
localStorage.setItem(`${storageKey}-start`, now.toString());
|
|
||||||
}
|
}
|
||||||
}, [storageKey, isCompleted]);
|
}, [storageKey, isCompleted, processingStartTime]);
|
||||||
|
|
||||||
// Progress through steps
|
// Progress through steps
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -64,7 +60,6 @@ export function AnimatedProviderSteps({
|
||||||
const elapsed = Date.now() - startTime;
|
const elapsed = Date.now() - startTime;
|
||||||
setElapsedTime(elapsed);
|
setElapsedTime(elapsed);
|
||||||
localStorage.setItem(`${storageKey}-elapsed`, elapsed.toString());
|
localStorage.setItem(`${storageKey}-elapsed`, elapsed.toString());
|
||||||
localStorage.removeItem(`${storageKey}-start`);
|
|
||||||
}
|
}
|
||||||
}, [isCompleted, startTime, storageKey]);
|
}, [isCompleted, startTime, storageKey]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,11 @@ interface OnboardingCardProps {
|
||||||
setLoadingStatus?: (status: string[]) => void;
|
setLoadingStatus?: (status: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const STEP_LIST = [
|
const STEP_LIST = [
|
||||||
"Setting up your model provider",
|
"Setting up your model provider",
|
||||||
"Defining schema",
|
"Defining schema",
|
||||||
"Configuring Langflow",
|
"Configuring Langflow",
|
||||||
"Ingesting sample data",
|
"Ingesting sample data",
|
||||||
];
|
];
|
||||||
|
|
||||||
const TOTAL_PROVIDER_STEPS = STEP_LIST.length;
|
const TOTAL_PROVIDER_STEPS = STEP_LIST.length;
|
||||||
|
|
@ -106,7 +105,13 @@ const OnboardingCard = ({
|
||||||
llm_model: "",
|
llm_model: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const [currentStep, setCurrentStep] = useState<number | null>(isCompleted ? TOTAL_PROVIDER_STEPS : null);
|
const [currentStep, setCurrentStep] = useState<number | null>(
|
||||||
|
isCompleted ? TOTAL_PROVIDER_STEPS : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [processingStartTime, setProcessingStartTime] = useState<number | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
// Query tasks to track completion
|
// Query tasks to track completion
|
||||||
const { data: tasks } = useGetTasksQuery({
|
const { data: tasks } = useGetTasksQuery({
|
||||||
|
|
@ -131,8 +136,8 @@ const OnboardingCard = ({
|
||||||
// If no active tasks and we've started onboarding, complete it
|
// If no active tasks and we've started onboarding, complete it
|
||||||
if (
|
if (
|
||||||
(!activeTasks || (activeTasks.processed_files ?? 0) > 0) &&
|
(!activeTasks || (activeTasks.processed_files ?? 0) > 0) &&
|
||||||
tasks.length > 0
|
tasks.length > 0 &&
|
||||||
&& !isCompleted
|
!isCompleted
|
||||||
) {
|
) {
|
||||||
// Set to final step to show "Done"
|
// Set to final step to show "Done"
|
||||||
setCurrentStep(TOTAL_PROVIDER_STEPS);
|
setCurrentStep(TOTAL_PROVIDER_STEPS);
|
||||||
|
|
@ -189,6 +194,8 @@ const OnboardingCard = ({
|
||||||
onboardingData.project_id = settings.project_id;
|
onboardingData.project_id = settings.project_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record the start time when user clicks Complete
|
||||||
|
setProcessingStartTime(Date.now());
|
||||||
onboardingMutation.mutate(onboardingData);
|
onboardingMutation.mutate(onboardingData);
|
||||||
setCurrentStep(0);
|
setCurrentStep(0);
|
||||||
};
|
};
|
||||||
|
|
@ -211,30 +218,55 @@ const OnboardingCard = ({
|
||||||
onValueChange={handleSetModelProvider}
|
onValueChange={handleSetModelProvider}
|
||||||
>
|
>
|
||||||
<TabsList className="mb-4">
|
<TabsList className="mb-4">
|
||||||
<TabsTrigger
|
<TabsTrigger value="openai">
|
||||||
value="openai"
|
<div
|
||||||
>
|
className={cn(
|
||||||
<div className={cn("flex items-center justify-center gap-2 w-8 h-8 rounded-md", modelProvider === "openai" ? "bg-white" : "bg-muted")}>
|
"flex items-center justify-center gap-2 w-8 h-8 rounded-md",
|
||||||
<OpenAILogo className={cn("w-4 h-4 shrink-0", modelProvider === "openai" ? "text-black" : "text-muted-foreground")} />
|
modelProvider === "openai" ? "bg-white" : "bg-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<OpenAILogo
|
||||||
|
className={cn(
|
||||||
|
"w-4 h-4 shrink-0",
|
||||||
|
modelProvider === "openai"
|
||||||
|
? "text-black"
|
||||||
|
: "text-muted-foreground",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
OpenAI
|
OpenAI
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger value="watsonx">
|
||||||
value="watsonx"
|
<div
|
||||||
>
|
className={cn(
|
||||||
<div className={cn("flex items-center justify-center gap-2 w-8 h-8 rounded-md", modelProvider === "watsonx" ? "bg-[#1063FE]" : "bg-muted")}>
|
"flex items-center justify-center gap-2 w-8 h-8 rounded-md",
|
||||||
<IBMLogo className={cn("w-4 h-4 shrink-0", modelProvider === "watsonx" ? "text-white" : "text-muted-foreground")} />
|
modelProvider === "watsonx" ? "bg-[#1063FE]" : "bg-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<IBMLogo
|
||||||
|
className={cn(
|
||||||
|
"w-4 h-4 shrink-0",
|
||||||
|
modelProvider === "watsonx"
|
||||||
|
? "text-white"
|
||||||
|
: "text-muted-foreground",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
IBM watsonx.ai
|
IBM watsonx.ai
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger value="ollama">
|
||||||
value="ollama"
|
<div
|
||||||
>
|
className={cn(
|
||||||
<div className={cn("flex items-center justify-center gap-2 w-8 h-8 rounded-md", modelProvider === "ollama" ? "bg-white" : "bg-muted")}>
|
"flex items-center justify-center gap-2 w-8 h-8 rounded-md",
|
||||||
|
modelProvider === "ollama" ? "bg-white" : "bg-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<OllamaLogo
|
<OllamaLogo
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-4 h-4 shrink-0",
|
"w-4 h-4 shrink-0",
|
||||||
modelProvider === "ollama" ? "text-black" : "text-muted-foreground",
|
modelProvider === "ollama"
|
||||||
|
? "text-black"
|
||||||
|
: "text-muted-foreground",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -285,11 +317,13 @@ const OnboardingCard = ({
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
{!isComplete && (
|
{!isComplete && (
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
{isLoadingModels ? "Loading models..." : (!!settings.llm_model &&
|
{isLoadingModels
|
||||||
!!settings.embedding_model &&
|
? "Loading models..."
|
||||||
!isDoclingHealthy
|
: !!settings.llm_model &&
|
||||||
? "docling-serve must be running to continue"
|
!!settings.embedding_model &&
|
||||||
: "Please fill in all required fields")}
|
!isDoclingHealthy
|
||||||
|
? "docling-serve must be running to continue"
|
||||||
|
: "Please fill in all required fields"}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
@ -303,11 +337,12 @@ const OnboardingCard = ({
|
||||||
transition={{ duration: 0.4, ease: "easeInOut" }}
|
transition={{ duration: 0.4, ease: "easeInOut" }}
|
||||||
>
|
>
|
||||||
<AnimatedProviderSteps
|
<AnimatedProviderSteps
|
||||||
currentStep={currentStep}
|
currentStep={currentStep}
|
||||||
isCompleted={isCompleted}
|
isCompleted={isCompleted}
|
||||||
setCurrentStep={setCurrentStep}
|
setCurrentStep={setCurrentStep}
|
||||||
steps={STEP_LIST}
|
steps={STEP_LIST}
|
||||||
/>
|
processingStartTime={processingStartTime}
|
||||||
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,6 @@ export const TOTAL_ONBOARDING_STEPS = 3;
|
||||||
export const ONBOARDING_STEP_KEY = "onboarding_current_step";
|
export const ONBOARDING_STEP_KEY = "onboarding_current_step";
|
||||||
|
|
||||||
export const FILES_REGEX =
|
export const FILES_REGEX =
|
||||||
/(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/;
|
/(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/;
|
||||||
|
|
||||||
|
export const FILE_CONFIRMATION = "Confirm that you received this file.";
|
||||||
Loading…
Add table
Reference in a new issue