This commit is contained in:
pablonyx 2025-02-17 13:21:43 -08:00 committed by GitHub
parent 86bd121806
commit 58b252727f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 178 additions and 64 deletions

View File

@ -409,10 +409,6 @@ class DefaultMultiLLM(LLM):
self._record_call(processed_prompt)
try:
print(
"model is",
f"{self.config.model_provider}/{self.config.deployment_name or self.config.model_name}",
)
return litellm.completion(
mock_response=MOCK_LLM_RESPONSE,
# model choice

View File

@ -1,6 +1,11 @@
"use client";
import { redirect, useRouter, useSearchParams } from "next/navigation";
import {
redirect,
usePathname,
useRouter,
useSearchParams,
} from "next/navigation";
import {
BackendChatSession,
BackendMessage,
@ -130,6 +135,7 @@ import {
} from "@/lib/browserUtilities";
import { Button } from "@/components/ui/button";
import { ConfirmEntityModal } from "@/components/modals/ConfirmEntityModal";
import { MessageChannel } from "node:worker_threads";
const TEMP_USER_MESSAGE_ID = -1;
const TEMP_ASSISTANT_MESSAGE_ID = -2;
@ -1145,6 +1151,7 @@ export function ChatPage({
regenerationRequest?: RegenerationRequest | null;
overrideFileDescriptors?: FileDescriptor[];
} = {}) => {
navigatingAway.current = false;
let frozenSessionId = currentSessionId();
updateCanContinue(false, frozenSessionId);
@ -1267,7 +1274,6 @@ export function ChatPage({
let stackTrace: string | null = null;
let sub_questions: SubQuestionDetail[] = [];
let second_level_sub_questions: SubQuestionDetail[] = [];
let is_generating: boolean = false;
let second_level_generating: boolean = false;
let finalMessage: BackendMessage | null = null;
@ -1291,7 +1297,7 @@ export function ChatPage({
const stack = new CurrentMessageFIFO();
updateCurrentMessageFIFO(stack, {
signal: controller.signal, // Add this line
signal: controller.signal,
message: currMessage,
alternateAssistantId: currentAssistantId,
fileDescriptors: overrideFileDescriptors || currentMessageFiles,
@ -1712,7 +1718,10 @@ export function ChatPage({
const newUrl = buildChatUrl(searchParams, currChatSessionId, null);
// newUrl is like /chat?chatId=10
// current page is like /chat
router.push(newUrl, { scroll: false });
if (pathname == "/chat" && !navigatingAway.current) {
router.push(newUrl, { scroll: false });
}
}
}
if (
@ -2086,6 +2095,31 @@ export function ChatPage({
llmOverrideManager.updateImageFilesPresent(imageFileInMessageHistory);
}, [imageFileInMessageHistory]);
const pathname = usePathname();
useEffect(() => {
return () => {
// Cleanup which only runs when the component unmounts (i.e. when you navigate away).
const currentSession = currentSessionId();
const controller = abortControllersRef.current.get(currentSession);
if (controller) {
controller.abort();
navigatingAway.current = true;
setAbortControllers((prev) => {
const newControllers = new Map(prev);
newControllers.delete(currentSession);
return newControllers;
});
}
};
}, [pathname]);
const navigatingAway = useRef(false);
// Keep a ref to abortControllers to ensure we always have the latest value
const abortControllersRef = useRef(abortControllers);
useEffect(() => {
abortControllersRef.current = abortControllers;
}, [abortControllers]);
useSidebarShortcut(router, toggleSidebar);
const [sharedChatSession, setSharedChatSession] =
@ -2300,7 +2334,7 @@ export function ChatPage({
fixed
left-0
z-40
bg-background-100
bg-neutral-200
h-screen
transition-all
bg-opacity-80
@ -2557,12 +2591,21 @@ export function ChatPage({
) {
return <></>;
}
const nextMessage =
messageHistory.length > i + 1
? messageHistory[i + 1]
: null;
return (
<div
id={`message-${message.messageId}`}
key={messageReactComponentKey}
>
<HumanMessage
disableSwitchingForStreaming={
(nextMessage &&
nextMessage.is_generating) ||
false
}
stopGenerating={stopGenerating}
content={message.message}
files={message.files}

View File

@ -94,7 +94,7 @@ export function AgenticToggle({
Agent Search (BETA)
</h3>
</div>
<p className="text-xs text-neutarl-600 dark:text-neutral-700 mb-2">
<p className="text-xs text-neutral-600 dark:text-neutral-700 mb-2">
Use AI agents to break down questions and run deep iterative
research through promising pathways. Gives more thorough and
accurate responses but takes slightly longer.

View File

@ -113,7 +113,7 @@ export default function LLMPopover({
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger asChild>
<button
className="focus:outline-none"
className="dark:text-[#fff] text-[#000] focus:outline-none"
data-testid="llm-popover-trigger"
>
<ChatInputOption

View File

@ -250,7 +250,7 @@ export async function* sendMessage({
throw new Error(`HTTP error! status: ${response.status}`);
}
yield* handleSSEStream<PacketType>(response);
yield* handleSSEStream<PacketType>(response, signal);
}
export async function nameChatSession(chatSessionId: string) {

View File

@ -9,6 +9,12 @@ import React, {
useMemo,
useState,
} from "react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import ReactMarkdown from "react-markdown";
import { OnyxDocument, FilteredOnyxDocument } from "@/lib/search/interfaces";
import remarkGfm from "remark-gfm";
@ -308,7 +314,7 @@ export const AgenticMessage = ({
const renderedAlternativeMarkdown = useMemo(() => {
return (
<ReactMarkdown
className="prose max-w-full text-base"
className="prose dark:prose-invert max-w-full text-base"
components={{
...markdownComponents,
code: ({ node, className, children }: any) => {
@ -335,7 +341,7 @@ export const AgenticMessage = ({
const renderedMarkdown = useMemo(() => {
return (
<ReactMarkdown
className="prose max-w-full text-base"
className="prose dark:prose-invert max-w-full text-base"
components={markdownComponents}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
@ -530,6 +536,7 @@ export const AgenticMessage = ({
{includeMessageSwitcher && (
<div className="-mx-1 mr-auto">
<MessageSwitcher
disableForStreaming={!isComplete}
currentPage={currentMessageInd + 1}
totalPages={otherMessagesCanSwitchTo.length}
handlePrevious={() => {
@ -616,6 +623,7 @@ export const AgenticMessage = ({
{includeMessageSwitcher && (
<div className="-mx-1 mr-auto">
<MessageSwitcher
disableForStreaming={!isComplete}
currentPage={currentMessageInd + 1}
totalPages={otherMessagesCanSwitchTo.length}
handlePrevious={() => {
@ -694,27 +702,52 @@ function MessageSwitcher({
totalPages,
handlePrevious,
handleNext,
disableForStreaming,
}: {
currentPage: number;
totalPages: number;
handlePrevious: () => void;
handleNext: () => void;
disableForStreaming?: boolean;
}) {
return (
<div className="flex items-center text-sm space-x-0.5">
<Hoverable
icon={FiChevronLeft}
onClick={currentPage === 1 ? undefined : handlePrevious}
/>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<Hoverable
icon={FiChevronLeft}
onClick={currentPage === 1 ? undefined : handlePrevious}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{disableForStreaming ? "Disabled" : "Previous"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span className="text-text-darker select-none">
{currentPage} / {totalPages}
{disableForStreaming ? "Complete" : "Generating"}
</span>
<Hoverable
icon={FiChevronRight}
onClick={currentPage === totalPages ? undefined : handleNext}
/>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<Hoverable
icon={FiChevronRight}
onClick={currentPage === totalPages ? undefined : handleNext}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{disableForStreaming ? "Disabled" : "Next"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}

View File

@ -383,7 +383,7 @@ export const AIMessage = ({
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
<ReactMarkdown
className="prose max-w-full text-base"
className="prose dark:prose-invert max-w-full text-base"
components={markdownComponents}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
@ -495,7 +495,10 @@ export const AIMessage = ({
{docs && docs.length > 0 && (
<div
className={`mobile:hidden ${
query && "mt-2"
(query ||
toolCall?.tool_name ===
INTERNET_SEARCH_TOOL_NAME) &&
"mt-2"
} -mx-8 w-full mb-4 flex relative`}
>
<div className="w-full">
@ -795,27 +798,67 @@ function MessageSwitcher({
totalPages,
handlePrevious,
handleNext,
disableForStreaming,
}: {
currentPage: number;
totalPages: number;
handlePrevious: () => void;
handleNext: () => void;
disableForStreaming?: boolean;
}) {
return (
<div className="flex items-center text-sm space-x-0.5">
<Hoverable
icon={FiChevronLeft}
onClick={currentPage === 1 ? undefined : handlePrevious}
/>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<Hoverable
icon={FiChevronLeft}
onClick={
disableForStreaming
? () => null
: currentPage === 1
? undefined
: handlePrevious
}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{disableForStreaming
? "Wait for agent message to complete"
: "Previous"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span className="text-text-darker select-none">
{currentPage} / {totalPages}
</span>
<Hoverable
icon={FiChevronRight}
onClick={currentPage === totalPages ? undefined : handleNext}
/>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<div>
<Hoverable
icon={FiChevronRight}
onClick={
disableForStreaming
? () => null
: currentPage === totalPages
? undefined
: handleNext
}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{disableForStreaming
? "Wait for agent message to complete"
: "Next"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}
@ -829,6 +872,7 @@ export const HumanMessage = ({
onMessageSelection,
shared,
stopGenerating = () => null,
disableSwitchingForStreaming = false,
}: {
shared?: boolean;
content: string;
@ -838,6 +882,7 @@ export const HumanMessage = ({
onEdit?: (editedContent: string) => void;
onMessageSelection?: (messageId: number) => void;
stopGenerating?: () => void;
disableSwitchingForStreaming?: boolean;
}) => {
const textareaRef = useRef<HTMLTextAreaElement>(null);
@ -1067,6 +1112,7 @@ export const HumanMessage = ({
otherMessagesCanSwitchTo.length > 1 && (
<div className="ml-auto mr-3">
<MessageSwitcher
disableForStreaming={disableSwitchingForStreaming}
currentPage={currentMessageInd + 1}
totalPages={otherMessagesCanSwitchTo.length}
handlePrevious={() => {

View File

@ -294,7 +294,7 @@ const SubQuestionDisplay: React.FC<{
const renderedMarkdown = useMemo(() => {
return (
<ReactMarkdown
className="prose max-w-full text-base"
className="prose dark:prose-invert max-w-full text-base"
components={markdownComponents}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex]}
@ -340,7 +340,7 @@ const SubQuestionDisplay: React.FC<{
{subQuestion?.question || temporaryDisplay?.question}
</div>
<ChevronDown
className={`mt-0.5 text-text-darker transition-transform duration-500 ease-in-out ${
className={`mt-0.5 flex-none text-text-darker transition-transform duration-500 ease-in-out ${
toggled ? "" : "-rotate-90"
}`}
size={20}
@ -632,9 +632,7 @@ const SubQuestionsDisplay: React.FC<SubQuestionsDisplayProps> = ({
}
`}</style>
<div className="relative">
{/* {subQuestions.map((subQuestion, index) => ( */}
{memoizedSubQuestions.map((subQuestion, index) => (
// {dynamicSubQuestions.map((subQuestion, index) => (
<SubQuestionDisplay
currentlyOpen={
currentlyOpenQuestion?.level === subQuestion.level &&

View File

@ -131,7 +131,7 @@ const StandardAnswersTableRow = ({
/>,
<ReactMarkdown
key={`answer-${standardAnswer.id}`}
className="prose"
className="prose dark:prose-invert"
remarkPlugins={[remarkGfm]}
>
{standardAnswer.answer}

View File

@ -562,6 +562,7 @@ body {
.prose :where(pre):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
background-color: theme("colors.code-bg");
font-size: theme("fontSize.code-sm");
color: #fff;
}
pre[class*="language-"],
@ -655,16 +656,3 @@ ul > li > p {
display: inline;
/* Make paragraphs inline to reduce vertical space */
}
.dark strong {
color: white;
}
.prose.dark li,
.prose.dark h1,
.prose.dark h2,
.prose.dark h3,
.prose.dark h4,
.prose.dark h5 {
color: #e5e5e5;
}

View File

@ -17,7 +17,7 @@ export const Hoverable: React.FC<{
<div className="flex items-center">
<Icon
size={size}
className="hover:bg-background-chat-hover dark:text-[#B4B4B4] text-neutral-600 rounded h-fit cursor-pointer"
className="dark:text-[#B4B4B4] text-neutral-600 rounded h-fit cursor-pointer"
/>
{hoverText && (
<div className="max-w-0 leading-none whitespace-nowrap overflow-hidden transition-all duration-300 ease-in-out group-hover:max-w-xs group-hover:ml-2">

View File

@ -50,7 +50,7 @@ export function SearchResultIcon({ url }: { url: string }) {
return <SourceIcon sourceType={ValidSources.Web} iconSize={18} />;
}
if (url.includes("docs.onyx.app")) {
return <OnyxIcon size={18} />;
return <OnyxIcon size={18} className="dark:text-[#fff] text-[#000]" />;
}
return (

View File

@ -23,7 +23,7 @@ export function WebResultIcon({
return (
<>
{hostname == "docs.onyx.app" ? (
<OnyxIcon size={size} />
<OnyxIcon size={size} className="dark:text-[#fff] text-[#000]" />
) : !error ? (
<img
className="my-0 rounded-full py-0"

View File

@ -432,7 +432,10 @@ export const MarkdownFormField = ({
</div>
{isPreviewOpen ? (
<div className="p-4 border-t border-background-300">
<ReactMarkdown className="prose" remarkPlugins={[remarkGfm]}>
<ReactMarkdown
className="prose dark:prose-invert"
remarkPlugins={[remarkGfm]}
>
{field.value}
</ReactMarkdown>
</div>

View File

@ -9,7 +9,7 @@ export default function BlurBackground({
<div
onClick={onClick}
className={`
desktop:hidden w-full h-full fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm z-30 transition-opacity duration-300 ease-in-out ${
desktop:hidden w-full h-full fixed inset-0 bg-neutral-700 bg-opacity-50 backdrop-blur-sm z-30 transition-opacity duration-300 ease-in-out ${
visible
? "opacity-100 pointer-events-auto"
: "opacity-0 pointer-events-none"

View File

@ -35,7 +35,7 @@ export const MinimalMarkdown: React.FC<MinimalMarkdownProps> = ({
return (
<ReactMarkdown
className={`w-full text-wrap break-word ${className}`}
className={`w-full text-wrap break-word prose dark:prose-invert ${className}`}
components={markdownComponents}
remarkPlugins={[remarkGfm]}
>

View File

@ -78,7 +78,7 @@ export function getUniqueIcons(docs: OnyxDocument[]): JSX.Element[] {
for (const doc of docs) {
// If it's a web source, we check domain uniqueness
if (doc.source_type === ValidSources.Web && doc.link) {
if ((doc.is_internet || doc.source_type === ValidSources.Web) && doc.link) {
const domain = getDomainFromUrl(doc.link);
if (domain && !seenDomains.has(domain)) {
seenDomains.add(domain);

View File

@ -47,7 +47,7 @@ export default function LogoWithText({
className="flex gap-x-2 items-center ml-0 cursor-pointer desktop:hidden "
>
{!toggled ? (
<Logo className="desktop:hidden -my-2" height={24} width={24} />
<Logo className="desktop:hidden" height={24} width={24} />
) : (
<LogoComponent
show={toggled}

View File

@ -23,8 +23,11 @@ import { AllUsersResponse } from "./types";
import { Credential } from "./connectors/credentials";
import { SettingsContext } from "@/components/settings/SettingsProvider";
import { Persona, PersonaLabel } from "@/app/admin/assistants/interfaces";
import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces";
import { isAnthropic } from "@/app/admin/configuration/llm/interfaces";
import {
isAnthropic,
LLMProviderDescriptor,
} from "@/app/admin/configuration/llm/interfaces";
import { getSourceMetadata } from "./sources";
import { AuthType, NEXT_PUBLIC_CLOUD_ENABLED } from "./constants";
import { useUser } from "@/components/user/UserProvider";

View File

@ -79,12 +79,18 @@ export async function* handleStream<T extends NonEmptyObject>(
}
export async function* handleSSEStream<T extends PacketType>(
streamingResponse: Response
streamingResponse: Response,
signal?: AbortSignal
): AsyncGenerator<T, void, unknown> {
const reader = streamingResponse.body?.getReader();
const decoder = new TextDecoder();
let buffer = "";
if (signal) {
signal.addEventListener("abort", () => {
console.log("aborting");
reader?.cancel();
});
}
while (true) {
const rawChunk = await reader?.read();
if (!rawChunk) {

View File

@ -21,7 +21,6 @@ module.exports = {
transitionProperty: {
spacing: "margin, padding",
},
keyframes: {
"subtle-pulse": {
"0%, 100%": { opacity: 0.9 },
@ -148,7 +147,6 @@ module.exports = {
"text-mobile-sidebar": "var(--text-800)",
"background-search-filter": "var(--neutral-100-border-light)",
"background-search-filter-dropdown": "var(--neutral-100-border-light)",
"tw-prose-bold": "var(--text-800)",
"user-bubble": "var(--off-white)",