This commit is contained in:
pablonyx
2025-02-17 13:21:43 -08:00
committed by GitHub
parent 86bd121806
commit 58b252727f
21 changed files with 178 additions and 64 deletions

View File

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

View File

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

View File

@@ -94,7 +94,7 @@ export function AgenticToggle({
Agent Search (BETA) Agent Search (BETA)
</h3> </h3>
</div> </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 Use AI agents to break down questions and run deep iterative
research through promising pathways. Gives more thorough and research through promising pathways. Gives more thorough and
accurate responses but takes slightly longer. accurate responses but takes slightly longer.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -562,6 +562,7 @@ body {
.prose :where(pre):not(:where([class~="not-prose"], [class~="not-prose"] *)) { .prose :where(pre):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
background-color: theme("colors.code-bg"); background-color: theme("colors.code-bg");
font-size: theme("fontSize.code-sm"); font-size: theme("fontSize.code-sm");
color: #fff;
} }
pre[class*="language-"], pre[class*="language-"],
@@ -655,16 +656,3 @@ ul > li > p {
display: inline; display: inline;
/* Make paragraphs inline to reduce vertical space */ /* 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"> <div className="flex items-center">
<Icon <Icon
size={size} 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 && ( {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"> <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} />; return <SourceIcon sourceType={ValidSources.Web} iconSize={18} />;
} }
if (url.includes("docs.onyx.app")) { if (url.includes("docs.onyx.app")) {
return <OnyxIcon size={18} />; return <OnyxIcon size={18} className="dark:text-[#fff] text-[#000]" />;
} }
return ( return (

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ export default function BlurBackground({
<div <div
onClick={onClick} onClick={onClick}
className={` 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 visible
? "opacity-100 pointer-events-auto" ? "opacity-100 pointer-events-auto"
: "opacity-0 pointer-events-none" : "opacity-0 pointer-events-none"

View File

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

View File

@@ -78,7 +78,7 @@ export function getUniqueIcons(docs: OnyxDocument[]): JSX.Element[] {
for (const doc of docs) { for (const doc of docs) {
// If it's a web source, we check domain uniqueness // 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); const domain = getDomainFromUrl(doc.link);
if (domain && !seenDomains.has(domain)) { if (domain && !seenDomains.has(domain)) {
seenDomains.add(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 " className="flex gap-x-2 items-center ml-0 cursor-pointer desktop:hidden "
> >
{!toggled ? ( {!toggled ? (
<Logo className="desktop:hidden -my-2" height={24} width={24} /> <Logo className="desktop:hidden" height={24} width={24} />
) : ( ) : (
<LogoComponent <LogoComponent
show={toggled} show={toggled}

View File

@@ -23,8 +23,11 @@ import { AllUsersResponse } from "./types";
import { Credential } from "./connectors/credentials"; import { Credential } from "./connectors/credentials";
import { SettingsContext } from "@/components/settings/SettingsProvider"; import { SettingsContext } from "@/components/settings/SettingsProvider";
import { Persona, PersonaLabel } from "@/app/admin/assistants/interfaces"; import { Persona, PersonaLabel } from "@/app/admin/assistants/interfaces";
import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; import {
import { isAnthropic } from "@/app/admin/configuration/llm/interfaces"; isAnthropic,
LLMProviderDescriptor,
} from "@/app/admin/configuration/llm/interfaces";
import { getSourceMetadata } from "./sources"; import { getSourceMetadata } from "./sources";
import { AuthType, NEXT_PUBLIC_CLOUD_ENABLED } from "./constants"; import { AuthType, NEXT_PUBLIC_CLOUD_ENABLED } from "./constants";
import { useUser } from "@/components/user/UserProvider"; 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>( export async function* handleSSEStream<T extends PacketType>(
streamingResponse: Response streamingResponse: Response,
signal?: AbortSignal
): AsyncGenerator<T, void, unknown> { ): AsyncGenerator<T, void, unknown> {
const reader = streamingResponse.body?.getReader(); const reader = streamingResponse.body?.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = ""; let buffer = "";
if (signal) {
signal.addEventListener("abort", () => {
console.log("aborting");
reader?.cancel();
});
}
while (true) { while (true) {
const rawChunk = await reader?.read(); const rawChunk = await reader?.read();
if (!rawChunk) { if (!rawChunk) {

View File

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