Skip to content

Commit 58b2527

Browse files
authored
UX (#4014)
1 parent 86bd121 commit 58b2527

File tree

21 files changed

+178
-64
lines changed

21 files changed

+178
-64
lines changed

backend/onyx/llm/chat_llm.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,10 +409,6 @@ def _completion(
409409
self._record_call(processed_prompt)
410410

411411
try:
412-
print(
413-
"model is",
414-
f"{self.config.model_provider}/{self.config.deployment_name or self.config.model_name}",
415-
)
416412
return litellm.completion(
417413
mock_response=MOCK_LLM_RESPONSE,
418414
# model choice

web/src/app/chat/ChatPage.tsx

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
"use client";
22

3-
import { redirect, useRouter, useSearchParams } from "next/navigation";
3+
import {
4+
redirect,
5+
usePathname,
6+
useRouter,
7+
useSearchParams,
8+
} from "next/navigation";
49
import {
510
BackendChatSession,
611
BackendMessage,
@@ -130,6 +135,7 @@ import {
130135
} from "@/lib/browserUtilities";
131136
import { Button } from "@/components/ui/button";
132137
import { ConfirmEntityModal } from "@/components/modals/ConfirmEntityModal";
138+
import { MessageChannel } from "node:worker_threads";
133139

134140
const TEMP_USER_MESSAGE_ID = -1;
135141
const TEMP_ASSISTANT_MESSAGE_ID = -2;
@@ -1145,6 +1151,7 @@ export function ChatPage({
11451151
regenerationRequest?: RegenerationRequest | null;
11461152
overrideFileDescriptors?: FileDescriptor[];
11471153
} = {}) => {
1154+
navigatingAway.current = false;
11481155
let frozenSessionId = currentSessionId();
11491156
updateCanContinue(false, frozenSessionId);
11501157

@@ -1267,7 +1274,6 @@ export function ChatPage({
12671274
let stackTrace: string | null = null;
12681275

12691276
let sub_questions: SubQuestionDetail[] = [];
1270-
let second_level_sub_questions: SubQuestionDetail[] = [];
12711277
let is_generating: boolean = false;
12721278
let second_level_generating: boolean = false;
12731279
let finalMessage: BackendMessage | null = null;
@@ -1291,7 +1297,7 @@ export function ChatPage({
12911297

12921298
const stack = new CurrentMessageFIFO();
12931299
updateCurrentMessageFIFO(stack, {
1294-
signal: controller.signal, // Add this line
1300+
signal: controller.signal,
12951301
message: currMessage,
12961302
alternateAssistantId: currentAssistantId,
12971303
fileDescriptors: overrideFileDescriptors || currentMessageFiles,
@@ -1712,7 +1718,10 @@ export function ChatPage({
17121718
const newUrl = buildChatUrl(searchParams, currChatSessionId, null);
17131719
// newUrl is like /chat?chatId=10
17141720
// current page is like /chat
1715-
router.push(newUrl, { scroll: false });
1721+
1722+
if (pathname == "/chat" && !navigatingAway.current) {
1723+
router.push(newUrl, { scroll: false });
1724+
}
17161725
}
17171726
}
17181727
if (
@@ -2086,6 +2095,31 @@ export function ChatPage({
20862095
llmOverrideManager.updateImageFilesPresent(imageFileInMessageHistory);
20872096
}, [imageFileInMessageHistory]);
20882097

2098+
const pathname = usePathname();
2099+
useEffect(() => {
2100+
return () => {
2101+
// Cleanup which only runs when the component unmounts (i.e. when you navigate away).
2102+
const currentSession = currentSessionId();
2103+
const controller = abortControllersRef.current.get(currentSession);
2104+
if (controller) {
2105+
controller.abort();
2106+
navigatingAway.current = true;
2107+
setAbortControllers((prev) => {
2108+
const newControllers = new Map(prev);
2109+
newControllers.delete(currentSession);
2110+
return newControllers;
2111+
});
2112+
}
2113+
};
2114+
}, [pathname]);
2115+
2116+
const navigatingAway = useRef(false);
2117+
// Keep a ref to abortControllers to ensure we always have the latest value
2118+
const abortControllersRef = useRef(abortControllers);
2119+
useEffect(() => {
2120+
abortControllersRef.current = abortControllers;
2121+
}, [abortControllers]);
2122+
20892123
useSidebarShortcut(router, toggleSidebar);
20902124

20912125
const [sharedChatSession, setSharedChatSession] =
@@ -2300,7 +2334,7 @@ export function ChatPage({
23002334
fixed
23012335
left-0
23022336
z-40
2303-
bg-background-100
2337+
bg-neutral-200
23042338
h-screen
23052339
transition-all
23062340
bg-opacity-80
@@ -2557,12 +2591,21 @@ export function ChatPage({
25572591
) {
25582592
return <></>;
25592593
}
2594+
const nextMessage =
2595+
messageHistory.length > i + 1
2596+
? messageHistory[i + 1]
2597+
: null;
25602598
return (
25612599
<div
25622600
id={`message-${message.messageId}`}
25632601
key={messageReactComponentKey}
25642602
>
25652603
<HumanMessage
2604+
disableSwitchingForStreaming={
2605+
(nextMessage &&
2606+
nextMessage.is_generating) ||
2607+
false
2608+
}
25662609
stopGenerating={stopGenerating}
25672610
content={message.message}
25682611
files={message.files}

web/src/app/chat/input/AgenticToggle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export function AgenticToggle({
9494
Agent Search (BETA)
9595
</h3>
9696
</div>
97-
<p className="text-xs text-neutarl-600 dark:text-neutral-700 mb-2">
97+
<p className="text-xs text-neutral-600 dark:text-neutral-700 mb-2">
9898
Use AI agents to break down questions and run deep iterative
9999
research through promising pathways. Gives more thorough and
100100
accurate responses but takes slightly longer.

web/src/app/chat/input/LLMPopover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export default function LLMPopover({
113113
<Popover open={isOpen} onOpenChange={setIsOpen}>
114114
<PopoverTrigger asChild>
115115
<button
116-
className="focus:outline-none"
116+
className="dark:text-[#fff] text-[#000] focus:outline-none"
117117
data-testid="llm-popover-trigger"
118118
>
119119
<ChatInputOption

web/src/app/chat/lib.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export async function* sendMessage({
250250
throw new Error(`HTTP error! status: ${response.status}`);
251251
}
252252

253-
yield* handleSSEStream<PacketType>(response);
253+
yield* handleSSEStream<PacketType>(response, signal);
254254
}
255255

256256
export async function nameChatSession(chatSessionId: string) {

web/src/app/chat/message/AgenticMessage.tsx

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import React, {
99
useMemo,
1010
useState,
1111
} from "react";
12+
import {
13+
Tooltip,
14+
TooltipContent,
15+
TooltipProvider,
16+
TooltipTrigger,
17+
} from "@/components/ui/tooltip";
1218
import ReactMarkdown from "react-markdown";
1319
import { OnyxDocument, FilteredOnyxDocument } from "@/lib/search/interfaces";
1420
import remarkGfm from "remark-gfm";
@@ -308,7 +314,7 @@ export const AgenticMessage = ({
308314
const renderedAlternativeMarkdown = useMemo(() => {
309315
return (
310316
<ReactMarkdown
311-
className="prose max-w-full text-base"
317+
className="prose dark:prose-invert max-w-full text-base"
312318
components={{
313319
...markdownComponents,
314320
code: ({ node, className, children }: any) => {
@@ -335,7 +341,7 @@ export const AgenticMessage = ({
335341
const renderedMarkdown = useMemo(() => {
336342
return (
337343
<ReactMarkdown
338-
className="prose max-w-full text-base"
344+
className="prose dark:prose-invert max-w-full text-base"
339345
components={markdownComponents}
340346
remarkPlugins={[remarkGfm, remarkMath]}
341347
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
@@ -530,6 +536,7 @@ export const AgenticMessage = ({
530536
{includeMessageSwitcher && (
531537
<div className="-mx-1 mr-auto">
532538
<MessageSwitcher
539+
disableForStreaming={!isComplete}
533540
currentPage={currentMessageInd + 1}
534541
totalPages={otherMessagesCanSwitchTo.length}
535542
handlePrevious={() => {
@@ -616,6 +623,7 @@ export const AgenticMessage = ({
616623
{includeMessageSwitcher && (
617624
<div className="-mx-1 mr-auto">
618625
<MessageSwitcher
626+
disableForStreaming={!isComplete}
619627
currentPage={currentMessageInd + 1}
620628
totalPages={otherMessagesCanSwitchTo.length}
621629
handlePrevious={() => {
@@ -694,27 +702,52 @@ function MessageSwitcher({
694702
totalPages,
695703
handlePrevious,
696704
handleNext,
705+
disableForStreaming,
697706
}: {
698707
currentPage: number;
699708
totalPages: number;
700709
handlePrevious: () => void;
701710
handleNext: () => void;
711+
disableForStreaming?: boolean;
702712
}) {
703713
return (
704714
<div className="flex items-center text-sm space-x-0.5">
705-
<Hoverable
706-
icon={FiChevronLeft}
707-
onClick={currentPage === 1 ? undefined : handlePrevious}
708-
/>
715+
<TooltipProvider>
716+
<Tooltip>
717+
<TooltipTrigger asChild>
718+
<div>
719+
<Hoverable
720+
icon={FiChevronLeft}
721+
onClick={currentPage === 1 ? undefined : handlePrevious}
722+
/>
723+
</div>
724+
</TooltipTrigger>
725+
<TooltipContent>
726+
{disableForStreaming ? "Disabled" : "Previous"}
727+
</TooltipContent>
728+
</Tooltip>
729+
</TooltipProvider>
709730

710731
<span className="text-text-darker select-none">
711732
{currentPage} / {totalPages}
733+
{disableForStreaming ? "Complete" : "Generating"}
712734
</span>
713735

714-
<Hoverable
715-
icon={FiChevronRight}
716-
onClick={currentPage === totalPages ? undefined : handleNext}
717-
/>
736+
<TooltipProvider>
737+
<Tooltip>
738+
<TooltipTrigger asChild>
739+
<div>
740+
<Hoverable
741+
icon={FiChevronRight}
742+
onClick={currentPage === totalPages ? undefined : handleNext}
743+
/>
744+
</div>
745+
</TooltipTrigger>
746+
<TooltipContent>
747+
{disableForStreaming ? "Disabled" : "Next"}
748+
</TooltipContent>
749+
</Tooltip>
750+
</TooltipProvider>
718751
</div>
719752
);
720753
}

web/src/app/chat/message/Messages.tsx

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ export const AIMessage = ({
383383
dangerouslySetInnerHTML={{ __html: htmlContent }}
384384
/>
385385
<ReactMarkdown
386-
className="prose max-w-full text-base"
386+
className="prose dark:prose-invert max-w-full text-base"
387387
components={markdownComponents}
388388
remarkPlugins={[remarkGfm, remarkMath]}
389389
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
@@ -495,7 +495,10 @@ export const AIMessage = ({
495495
{docs && docs.length > 0 && (
496496
<div
497497
className={`mobile:hidden ${
498-
query && "mt-2"
498+
(query ||
499+
toolCall?.tool_name ===
500+
INTERNET_SEARCH_TOOL_NAME) &&
501+
"mt-2"
499502
} -mx-8 w-full mb-4 flex relative`}
500503
>
501504
<div className="w-full">
@@ -795,27 +798,67 @@ function MessageSwitcher({
795798
totalPages,
796799
handlePrevious,
797800
handleNext,
801+
disableForStreaming,
798802
}: {
799803
currentPage: number;
800804
totalPages: number;
801805
handlePrevious: () => void;
802806
handleNext: () => void;
807+
disableForStreaming?: boolean;
803808
}) {
804809
return (
805810
<div className="flex items-center text-sm space-x-0.5">
806-
<Hoverable
807-
icon={FiChevronLeft}
808-
onClick={currentPage === 1 ? undefined : handlePrevious}
809-
/>
811+
<TooltipProvider>
812+
<Tooltip>
813+
<TooltipTrigger asChild>
814+
<div>
815+
<Hoverable
816+
icon={FiChevronLeft}
817+
onClick={
818+
disableForStreaming
819+
? () => null
820+
: currentPage === 1
821+
? undefined
822+
: handlePrevious
823+
}
824+
/>
825+
</div>
826+
</TooltipTrigger>
827+
<TooltipContent>
828+
{disableForStreaming
829+
? "Wait for agent message to complete"
830+
: "Previous"}
831+
</TooltipContent>
832+
</Tooltip>
833+
</TooltipProvider>
810834

811835
<span className="text-text-darker select-none">
812836
{currentPage} / {totalPages}
813837
</span>
814838

815-
<Hoverable
816-
icon={FiChevronRight}
817-
onClick={currentPage === totalPages ? undefined : handleNext}
818-
/>
839+
<TooltipProvider>
840+
<Tooltip>
841+
<TooltipTrigger>
842+
<div>
843+
<Hoverable
844+
icon={FiChevronRight}
845+
onClick={
846+
disableForStreaming
847+
? () => null
848+
: currentPage === totalPages
849+
? undefined
850+
: handleNext
851+
}
852+
/>
853+
</div>
854+
</TooltipTrigger>
855+
<TooltipContent>
856+
{disableForStreaming
857+
? "Wait for agent message to complete"
858+
: "Next"}
859+
</TooltipContent>
860+
</Tooltip>
861+
</TooltipProvider>
819862
</div>
820863
);
821864
}
@@ -829,6 +872,7 @@ export const HumanMessage = ({
829872
onMessageSelection,
830873
shared,
831874
stopGenerating = () => null,
875+
disableSwitchingForStreaming = false,
832876
}: {
833877
shared?: boolean;
834878
content: string;
@@ -838,6 +882,7 @@ export const HumanMessage = ({
838882
onEdit?: (editedContent: string) => void;
839883
onMessageSelection?: (messageId: number) => void;
840884
stopGenerating?: () => void;
885+
disableSwitchingForStreaming?: boolean;
841886
}) => {
842887
const textareaRef = useRef<HTMLTextAreaElement>(null);
843888

@@ -1067,6 +1112,7 @@ export const HumanMessage = ({
10671112
otherMessagesCanSwitchTo.length > 1 && (
10681113
<div className="ml-auto mr-3">
10691114
<MessageSwitcher
1115+
disableForStreaming={disableSwitchingForStreaming}
10701116
currentPage={currentMessageInd + 1}
10711117
totalPages={otherMessagesCanSwitchTo.length}
10721118
handlePrevious={() => {

0 commit comments

Comments
 (0)