diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index 927b21b58..b60d3a5d2 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -132,18 +132,16 @@ import { import { getSourceMetadata } from "@/lib/sources"; import { UserSettingsModal } from "./modal/UserSettingsModal"; -import { AlignStartVertical } from "lucide-react"; import { AgenticMessage } from "./message/AgenticMessage"; import AssistantModal from "../assistants/mine/AssistantModal"; -import { - OperatingSystem, - useOperatingSystem, - useSidebarShortcut, -} from "@/lib/browserUtilities"; -import { Button } from "@/components/ui/button"; +import { useSidebarShortcut } from "@/lib/browserUtilities"; import { ConfirmEntityModal } from "@/components/modals/ConfirmEntityModal"; +<<<<<<< HEAD import { MessageChannel } from "node:worker_threads"; import { ChatSearchModal } from "./chat_search/ChatSearchModal"; +======= +import { ErrorBanner } from "./message/Resubmit"; +>>>>>>> db242d0b3 (add error handling) const TEMP_USER_MESSAGE_ID = -1; const TEMP_ASSISTANT_MESSAGE_ID = -2; @@ -1169,6 +1167,7 @@ export function ChatPage({ navigatingAway.current = false; let frozenSessionId = currentSessionId(); updateCanContinue(false, frozenSessionId); + setUncaughtError(null); // Mark that we've sent a message for this session in the current page load markSessionMessageSent(frozenSessionId); @@ -1319,6 +1318,7 @@ export function ChatPage({ let isStreamingQuestions = true; let includeAgentic = false; let secondLevelMessageId: number | null = null; + let isAgentic: boolean = false; let initialFetchDetails: null | { user_message_id: number; @@ -1481,6 +1481,9 @@ export function ChatPage({ second_level_generating = true; } } + if (Object.hasOwn(packet, "is_agentic")) { + isAgentic = (packet as any).is_agentic; + } if (Object.hasOwn(packet, "refined_answer_improvement")) { isImprovement = (packet as RefinedAnswerImprovement) @@ -1514,6 +1517,7 @@ export function ChatPage({ ); } else if (Object.hasOwn(packet, "sub_question")) { updateChatState("toolBuilding", frozenSessionId); + isAgentic = true; is_generating = true; sub_questions = constructSubQuestions( sub_questions, @@ -1714,6 +1718,7 @@ export function ChatPage({ sub_questions: sub_questions, second_level_generating: second_level_generating, agentic_docs: agenticDocs, + is_agentic: isAgentic, }, ...(includeAgentic ? [ @@ -2062,6 +2067,26 @@ export function ChatPage({ const [sharedChatSession, setSharedChatSession] = useState(); + const handleResubmitLastMessage = () => { + // Grab the last user-type message + const lastUserMsg = messageHistory + .slice() + .reverse() + .find((m) => m.type === "user"); + if (!lastUserMsg) { + setPopup({ + message: "No previously-submitted user message found.", + type: "error", + }); + return; + } + // We call onSubmit, passing a `messageOverride` + onSubmit({ + messageIdToResend: lastUserMsg.messageId, + messageOverride: lastUserMsg.message, + }); + }; + const showShareModal = (chatSession: ChatSession) => { setSharedChatSession(chatSession); }; @@ -2644,9 +2669,9 @@ export function ChatPage({ : null } > - {message.sub_questions && - message.sub_questions.length > 0 ? ( + {message.is_agentic ? ( - {message.message} - {message.stackTrace && ( - - setStackTraceModalContent( - message.stackTrace! - ) - } - className="ml-2 cursor-pointer underline" - > - Show stack trace. - - )} -

+ + setStackTraceModalContent( + message.stackTrace! + ) + : undefined + } + /> } /> diff --git a/web/src/app/chat/interfaces.ts b/web/src/app/chat/interfaces.ts index aa4970e25..6f614c7d2 100644 --- a/web/src/app/chat/interfaces.ts +++ b/web/src/app/chat/interfaces.ts @@ -103,6 +103,7 @@ export interface Message { overridden_model?: string; stopReason?: StreamStopReason | null; sub_questions?: SubQuestionDetail[] | null; + is_agentic?: boolean | null; // Streaming only second_level_generating?: boolean; @@ -148,6 +149,7 @@ export interface BackendMessage { comments: any; parentMessageId: number | null; refined_answer_improvement: boolean | null; + is_agentic: boolean | null; } export interface MessageResponseIDInfo { diff --git a/web/src/app/chat/message/AgenticMessage.tsx b/web/src/app/chat/message/AgenticMessage.tsx index 6bc114960..b206d8a96 100644 --- a/web/src/app/chat/message/AgenticMessage.tsx +++ b/web/src/app/chat/message/AgenticMessage.tsx @@ -50,6 +50,9 @@ import "katex/dist/katex.min.css"; import SubQuestionsDisplay from "./SubQuestionsDisplay"; import { StatusRefinement } from "../Refinement"; import { copyAll, handleCopy } from "./copyingUtils"; +import { Button } from "@/components/ui/button"; +import { RefreshCw } from "lucide-react"; +import { ErrorBanner, Resubmit } from "./Resubmit"; export const AgenticMessage = ({ isStreamingQuestions, @@ -84,7 +87,9 @@ export const AgenticMessage = ({ secondLevelSubquestions, toggleDocDisplay, error, + resubmit, }: { + resubmit?: () => void; isStreamingQuestions: boolean; isGenerating: boolean; docSidebarToggled?: boolean; @@ -268,8 +273,8 @@ export const AgenticMessage = ({ ? docs : agenticDocs : agenticDocs && agenticDocs.length > 0 - ? agenticDocs - : docs + ? agenticDocs + : docs } subQuestions={[ ...(subQuestions || []), @@ -437,8 +442,8 @@ export const AgenticMessage = ({ !allowDocuments ? [] : isViewingInitialAnswer - ? docs! - : agenticDocs! + ? docs! + : agenticDocs! } toggleDocumentSelection={() => { toggleDocumentSelection!(!isViewingInitialAnswer); @@ -503,9 +508,7 @@ export const AgenticMessage = ({ content )} {error && ( -

- {error} -

+ )} @@ -513,15 +516,13 @@ export const AgenticMessage = ({ ) : isComplete ? ( error && (

- {error} +

) ) : ( <> {error && ( -

- {error} -

+ )} )} diff --git a/web/src/app/chat/message/Resubmit.tsx b/web/src/app/chat/message/Resubmit.tsx new file mode 100644 index 000000000..c8febd55a --- /dev/null +++ b/web/src/app/chat/message/Resubmit.tsx @@ -0,0 +1,58 @@ +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { AlertCircle } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { RefreshCw } from "lucide-react"; + +interface ResubmitProps { + resubmit: () => void; +} + +export const Resubmit: React.FC = ({ resubmit }) => { + return ( +
+

+ There was an error with the response. +

+ +
+ ); +}; + +export const ErrorBanner = ({ + error, + showStackTrace, + resubmit, +}: { + error: string; + showStackTrace?: () => void; + resubmit?: () => void; +}) => { + return ( +
+ + + Error + + {error} + {showStackTrace && ( + + Show stack trace + + )} + + + {resubmit && } +
+ ); +}; diff --git a/web/src/components/ui/alert.tsx b/web/src/components/ui/alert.tsx index 74b63b270..ee668e2f4 100644 --- a/web/src/components/ui/alert.tsx +++ b/web/src/components/ui/alert.tsx @@ -8,8 +8,10 @@ const alertVariants = cva( { variants: { variant: { + broken: + "border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-100 dark:dark:border-red-900 dark:[&>svg]:text-red-700 bg-red-50 dark:bg-red-950", + ark: "border-amber-500/50 text-amber-500 dark:border-amber-500 [&>svg]:text-amber-500 dark:border-amber-900/50 dark:text-amber-900 dark:dark:border-amber-900 dark:[&>svg]:text-amber-900 bg-amber-50 dark:bg-amber-950", info: "border-black/50 dark:border-black dark:border-black/50 dark:dark:border-black", - default: "bg-neutral-50 text-neutral-darker dark:bg-neutral-950 dark:text-text", destructive: diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx index fa8fd9d6f..3a8b69083 100644 --- a/web/src/components/ui/button.tsx +++ b/web/src/components/ui/button.tsx @@ -9,6 +9,8 @@ const buttonVariants = cva( { variants: { variant: { + agent: + "bg-agent text-white hover:bg-agent-hovered dark:bg-agent dark:text-white dark:hover:bg-agent/90", success: "bg-green-100 text-green-600 hover:bg-green-500/90 dark:bg-green-700 dark:text-green-100 dark:hover:bg-green-600/90", "success-reverse":