mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-19 20:24:32 +02:00
Performance Improvements (#2162)
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
import { HistorySidebar } from "@/app/chat/sessionSidebar/HistorySidebar";
|
||||
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
||||
import { UserDropdown } from "@/components/UserDropdown";
|
||||
import { ChatProvider } from "@/components/context/ChatContext";
|
||||
import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
|
||||
import { fetchChatData } from "@/lib/chat/fetchChatData";
|
||||
import { unstable_noStore as noStore } from "next/cache";
|
||||
@@ -24,47 +21,27 @@ export default async function GalleryPage({
|
||||
const {
|
||||
user,
|
||||
chatSessions,
|
||||
availableSources,
|
||||
documentSets,
|
||||
assistants,
|
||||
tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
openedFolders,
|
||||
shouldShowWelcomeModal,
|
||||
toggleSidebar,
|
||||
userInputPrompts,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<>
|
||||
<InstantSSRAutoRefresh />
|
||||
|
||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||
|
||||
<ChatProvider
|
||||
value={{
|
||||
user,
|
||||
chatSessions,
|
||||
availableSources,
|
||||
availableDocumentSets: documentSets,
|
||||
availableAssistants: assistants,
|
||||
availableTags: tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
openedFolders,
|
||||
userInputPrompts,
|
||||
}}
|
||||
>
|
||||
<WrappedAssistantsGallery
|
||||
initiallyToggled={toggleSidebar}
|
||||
chatSessions={chatSessions}
|
||||
folders={folders}
|
||||
openedFolders={openedFolders}
|
||||
user={user}
|
||||
assistants={assistants}
|
||||
/>
|
||||
</ChatProvider>
|
||||
<InstantSSRAutoRefresh />
|
||||
|
||||
<WrappedAssistantsGallery
|
||||
initiallyToggled={toggleSidebar}
|
||||
chatSessions={chatSessions}
|
||||
folders={folders}
|
||||
openedFolders={openedFolders}
|
||||
user={user}
|
||||
assistants={assistants}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@@ -48,7 +48,6 @@ export default function WrappedPrompts({
|
||||
content={(contentProps) => (
|
||||
<div className="mx-auto w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar">
|
||||
<AssistantsPageTitle>Prompt Gallery</AssistantsPageTitle>
|
||||
<InstantSSRAutoRefresh />
|
||||
<PromptSection
|
||||
promptLibrary={promptLibrary || []}
|
||||
isLoading={promptLibraryIsLoading}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
||||
import { ChatProvider } from "@/components/context/ChatContext";
|
||||
import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
|
||||
import { fetchChatData } from "@/lib/chat/fetchChatData";
|
||||
import { unstable_noStore as noStore } from "next/cache";
|
||||
@@ -22,47 +21,27 @@ export default async function GalleryPage({
|
||||
const {
|
||||
user,
|
||||
chatSessions,
|
||||
availableSources,
|
||||
documentSets,
|
||||
assistants,
|
||||
tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
openedFolders,
|
||||
shouldShowWelcomeModal,
|
||||
toggleSidebar,
|
||||
userInputPrompts,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<>
|
||||
<InstantSSRAutoRefresh />
|
||||
|
||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||
|
||||
<ChatProvider
|
||||
value={{
|
||||
user,
|
||||
chatSessions,
|
||||
availableSources,
|
||||
availableDocumentSets: documentSets,
|
||||
availableAssistants: assistants,
|
||||
availableTags: tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
openedFolders,
|
||||
userInputPrompts,
|
||||
}}
|
||||
>
|
||||
<WrappedAssistantsMine
|
||||
initiallyToggled={toggleSidebar}
|
||||
chatSessions={chatSessions}
|
||||
folders={folders}
|
||||
openedFolders={openedFolders}
|
||||
user={user}
|
||||
assistants={assistants}
|
||||
/>
|
||||
</ChatProvider>
|
||||
<InstantSSRAutoRefresh />
|
||||
|
||||
<WrappedAssistantsMine
|
||||
initiallyToggled={toggleSidebar}
|
||||
chatSessions={chatSessions}
|
||||
folders={folders}
|
||||
openedFolders={openedFolders}
|
||||
user={user}
|
||||
assistants={assistants}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
|
||||
import { redirect, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import {
|
||||
BackendChatSession,
|
||||
BackendMessage,
|
||||
@@ -23,7 +22,6 @@ import Cookies from "js-cookie";
|
||||
import { HistorySidebar } from "./sessionSidebar/HistorySidebar";
|
||||
import { Persona } from "../admin/assistants/interfaces";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
||||
import {
|
||||
buildChatUrl,
|
||||
buildLatestMessageChain,
|
||||
@@ -84,7 +82,6 @@ import FixedLogo from "./shared_chat_search/FixedLogo";
|
||||
import { getSecondsUntilExpiration } from "@/lib/time";
|
||||
import { SetDefaultModelModal } from "./modal/SetDefaultModelModal";
|
||||
import { DeleteChatModal } from "./modal/DeleteChatModal";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { MinimalMarkdown } from "@/components/chat_search/MinimalMarkdown";
|
||||
import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
|
||||
|
||||
@@ -1304,7 +1301,6 @@ export function ChatPage({
|
||||
return (
|
||||
<>
|
||||
<HealthCheckBanner secondsUntilExpiration={secondsUntilExpiration} />
|
||||
<InstantSSRAutoRefresh />
|
||||
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
|
||||
Only used in the EE version of the app. */}
|
||||
{popup}
|
||||
|
@@ -1,281 +0,0 @@
|
||||
import { useChatContext } from "@/components/context/ChatContext";
|
||||
import { FilterManager } from "@/lib/hooks";
|
||||
import { listSourceMetadata } from "@/lib/sources";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
DateRangePicker,
|
||||
DateRangePickerItem,
|
||||
Divider,
|
||||
Text,
|
||||
} from "@tremor/react";
|
||||
import { getXDaysAgo } from "@/lib/dateUtils";
|
||||
import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable";
|
||||
import { Bubble } from "@/components/Bubble";
|
||||
import { FiX } from "react-icons/fi";
|
||||
import { getValidTags } from "@/lib/tags/tagUtils";
|
||||
import debounce from "lodash/debounce";
|
||||
import { Tag } from "@/lib/types";
|
||||
|
||||
export function FiltersTab({
|
||||
filterManager,
|
||||
}: {
|
||||
filterManager: FilterManager;
|
||||
}): JSX.Element {
|
||||
const { availableSources, availableDocumentSets, availableTags } =
|
||||
useChatContext();
|
||||
|
||||
const [filterValue, setFilterValue] = useState<string>("");
|
||||
const [filteredTags, setFilteredTags] = useState<Tag[]>(availableTags);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const allSources = listSourceMetadata();
|
||||
const availableSourceMetadata = allSources.filter((source) =>
|
||||
availableSources.includes(source.internalName)
|
||||
);
|
||||
|
||||
const debouncedFetchTags = useRef(
|
||||
debounce(async (value: string) => {
|
||||
if (value) {
|
||||
const fetchedTags = await getValidTags(value);
|
||||
setFilteredTags(fetchedTags);
|
||||
} else {
|
||||
setFilteredTags(availableTags);
|
||||
}
|
||||
}, 50)
|
||||
).current;
|
||||
|
||||
useEffect(() => {
|
||||
debouncedFetchTags(filterValue);
|
||||
|
||||
return () => {
|
||||
debouncedFetchTags.cancel();
|
||||
};
|
||||
}, [filterValue, availableTags, debouncedFetchTags]);
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden flex flex-col">
|
||||
<div className="overflow-y-auto">
|
||||
<div>
|
||||
<div className="pb-4">
|
||||
<h3 className="text-lg font-semibold">Time Range</h3>
|
||||
<Text>
|
||||
Choose the time range we should search over. If only one date is
|
||||
selected, will only search after the specified date.
|
||||
</Text>
|
||||
<div className="mt-2">
|
||||
<DateRangePicker
|
||||
className="w-96"
|
||||
value={{
|
||||
from: filterManager.timeRange?.from,
|
||||
to: filterManager.timeRange?.to,
|
||||
selectValue: filterManager.timeRange?.selectValue,
|
||||
}}
|
||||
onValueChange={(value) =>
|
||||
filterManager.setTimeRange({
|
||||
from: value.from,
|
||||
to: value.to,
|
||||
selectValue: value.selectValue,
|
||||
})
|
||||
}
|
||||
selectPlaceholder="Select range"
|
||||
enableSelect
|
||||
>
|
||||
<DateRangePickerItem
|
||||
key="Last 30 Days"
|
||||
value="Last 30 Days"
|
||||
from={getXDaysAgo(30)}
|
||||
to={new Date()}
|
||||
>
|
||||
Last 30 Days
|
||||
</DateRangePickerItem>
|
||||
<DateRangePickerItem
|
||||
key="Last 7 Days"
|
||||
value="Last 7 Days"
|
||||
from={getXDaysAgo(7)}
|
||||
to={new Date()}
|
||||
>
|
||||
Last 7 Days
|
||||
</DateRangePickerItem>
|
||||
<DateRangePickerItem
|
||||
key="Today"
|
||||
value="Today"
|
||||
from={getXDaysAgo(1)}
|
||||
to={new Date()}
|
||||
>
|
||||
Today
|
||||
</DateRangePickerItem>
|
||||
</DateRangePicker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg font-semibold">Knowledge Sets</h3>
|
||||
<Text>
|
||||
Choose which knowledge sets we should search over. If multiple are
|
||||
selected, we will search through all of them.
|
||||
</Text>
|
||||
<ul className="mt-3">
|
||||
{availableDocumentSets.length > 0 ? (
|
||||
availableDocumentSets.map((set) => {
|
||||
const isSelected =
|
||||
filterManager.selectedDocumentSets.includes(set.name);
|
||||
return (
|
||||
<DocumentSetSelectable
|
||||
key={set.id}
|
||||
documentSet={set}
|
||||
isSelected={isSelected}
|
||||
onSelect={() =>
|
||||
filterManager.setSelectedDocumentSets((prev) =>
|
||||
isSelected
|
||||
? prev.filter((s) => s !== set.name)
|
||||
: [...prev, set.name]
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<li>No knowledge sets available</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-semibold">Sources</h3>
|
||||
<Text>
|
||||
Choose which sources we should search over. If multiple sources
|
||||
are selected, we will search through all of them.
|
||||
</Text>
|
||||
<ul className="mt-3 flex gap-2">
|
||||
{availableSourceMetadata.length > 0 ? (
|
||||
availableSourceMetadata.map((sourceMetadata) => {
|
||||
const isSelected = filterManager.selectedSources.some(
|
||||
(selectedSource) =>
|
||||
selectedSource.internalName ===
|
||||
sourceMetadata.internalName
|
||||
);
|
||||
return (
|
||||
<Bubble
|
||||
key={sourceMetadata.internalName}
|
||||
isSelected={isSelected}
|
||||
onClick={() =>
|
||||
filterManager.setSelectedSources((prev) =>
|
||||
isSelected
|
||||
? prev.filter(
|
||||
(s) =>
|
||||
s.internalName !== sourceMetadata.internalName
|
||||
)
|
||||
: [...prev, sourceMetadata]
|
||||
)
|
||||
}
|
||||
showCheckbox={true}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
{sourceMetadata?.icon({ size: 16 })}
|
||||
<span>{sourceMetadata.displayName}</span>
|
||||
</div>
|
||||
</Bubble>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<li>No sources available</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg font-semibold">Tags</h3>
|
||||
<ul className="space-2 gap-2 flex flex-wrap mt-2">
|
||||
{filterManager.selectedTags.length > 0 ? (
|
||||
filterManager.selectedTags.map((tag) => (
|
||||
<Bubble
|
||||
key={tag.tag_key + tag.tag_value}
|
||||
isSelected={true}
|
||||
onClick={() =>
|
||||
filterManager.setSelectedTags((prev) =>
|
||||
prev.filter(
|
||||
(t) =>
|
||||
t.tag_key !== tag.tag_key ||
|
||||
t.tag_value !== tag.tag_value
|
||||
)
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center space-x-2 text-sm">
|
||||
<p>
|
||||
{tag.tag_key}={tag.tag_value}
|
||||
</p>{" "}
|
||||
<FiX />
|
||||
</div>
|
||||
</Bubble>
|
||||
))
|
||||
) : (
|
||||
<p className="text-xs italic">No selected tags</p>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
<div className="w-96 mt-2">
|
||||
<div>
|
||||
<div className="mb-2 pt-2">
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="w-full border border-border py-0.5 px-2 rounded text-sm h-8"
|
||||
placeholder="Find a tag"
|
||||
value={filterValue}
|
||||
onChange={(event) => setFilterValue(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="max-h-48 flex flex-col gap-y-1 overflow-y-auto">
|
||||
{filteredTags.length > 0 ? (
|
||||
filteredTags
|
||||
.filter(
|
||||
(tag) =>
|
||||
!filterManager.selectedTags.some(
|
||||
(selectedTag) =>
|
||||
selectedTag.tag_key === tag.tag_key &&
|
||||
selectedTag.tag_value === tag.tag_value
|
||||
)
|
||||
)
|
||||
.slice(0, 12)
|
||||
.map((tag) => (
|
||||
<Bubble
|
||||
key={tag.tag_key + tag.tag_value}
|
||||
isSelected={filterManager.selectedTags.includes(tag)}
|
||||
onClick={() =>
|
||||
filterManager.setSelectedTags((prev) =>
|
||||
filterManager.selectedTags.includes(tag)
|
||||
? prev.filter(
|
||||
(t) =>
|
||||
t.tag_key !== tag.tag_key ||
|
||||
t.tag_value !== tag.tag_value
|
||||
)
|
||||
: [...prev, tag]
|
||||
)
|
||||
}
|
||||
>
|
||||
<>
|
||||
{tag.tag_key}={tag.tag_value}
|
||||
</>
|
||||
</Bubble>
|
||||
))
|
||||
) : (
|
||||
<div className="text-sm px-2 py-2">
|
||||
No matching tags found
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -3,11 +3,8 @@ import { unstable_noStore as noStore } from "next/cache";
|
||||
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
||||
import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
|
||||
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
||||
import { NoCompleteSourcesModal } from "@/components/initialSetup/search/NoCompleteSourceModal";
|
||||
import { ChatProvider } from "@/components/context/ChatContext";
|
||||
import { fetchChatData } from "@/lib/chat/fetchChatData";
|
||||
import FunctionalWrapper from "./shared_chat_search/FunctionalWrapper";
|
||||
import { ChatPage } from "./ChatPage";
|
||||
import WrappedChat from "./WrappedChat";
|
||||
|
||||
export default async function Page({
|
||||
|
@@ -69,11 +69,11 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
||||
|
||||
const currentChatId = currentChatSession?.id;
|
||||
|
||||
// prevent the NextJS Router cache from causing the chat sidebar to not
|
||||
// update / show an outdated list of chats
|
||||
useEffect(() => {
|
||||
router.refresh();
|
||||
}, [currentChatId]);
|
||||
// NOTE: do not do something like the below - assume that the parent
|
||||
// will handle properly refreshing the existingChats
|
||||
// useEffect(() => {
|
||||
// router.refresh();
|
||||
// }, [currentChatId]);
|
||||
|
||||
const combinedSettings = useContext(SettingsContext);
|
||||
if (!combinedSettings) {
|
||||
|
@@ -1,12 +1,19 @@
|
||||
import "./globals.css";
|
||||
|
||||
import { getCombinedSettings } from "@/components/settings/lib";
|
||||
import { CUSTOM_ANALYTICS_ENABLED } from "@/lib/constants";
|
||||
import {
|
||||
fetchEnterpriseSettingsSS,
|
||||
getCombinedSettings,
|
||||
} from "@/components/settings/lib";
|
||||
import {
|
||||
CUSTOM_ANALYTICS_ENABLED,
|
||||
SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED,
|
||||
} from "@/lib/constants";
|
||||
import { SettingsProvider } from "@/components/settings/SettingsProvider";
|
||||
import { Metadata } from "next";
|
||||
import { buildClientUrl } from "@/lib/utilsSS";
|
||||
import { Inter } from "next/font/google";
|
||||
import Head from "next/head";
|
||||
import { EnterpriseSettings } from "./admin/settings/interfaces";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
@@ -15,15 +22,18 @@ const inter = Inter({
|
||||
});
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const dynamicSettings = await getCombinedSettings({ forceRetrieval: true });
|
||||
const logoLocation =
|
||||
dynamicSettings.enterpriseSettings &&
|
||||
dynamicSettings.enterpriseSettings?.use_custom_logo
|
||||
? "/api/enterprise-settings/logo"
|
||||
: buildClientUrl("/danswer.ico");
|
||||
let logoLocation = buildClientUrl("/danswer.ico");
|
||||
let enterpriseSettings: EnterpriseSettings | null = null;
|
||||
if (SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED) {
|
||||
enterpriseSettings = await (await fetchEnterpriseSettingsSS()).json();
|
||||
logoLocation =
|
||||
enterpriseSettings && enterpriseSettings.use_custom_logo
|
||||
? "/api/enterprise-settings/logo"
|
||||
: buildClientUrl("/danswer.ico");
|
||||
}
|
||||
|
||||
return {
|
||||
title: dynamicSettings.enterpriseSettings?.application_name ?? "Danswer",
|
||||
title: enterpriseSettings?.application_name ?? "Danswer",
|
||||
description: "Question answering for your documents",
|
||||
icons: {
|
||||
icon: logoLocation,
|
||||
|
@@ -185,6 +185,7 @@ export default async function Home() {
|
||||
<>
|
||||
<HealthCheckBanner secondsUntilExpiration={secondsUntilExpiration} />
|
||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||
<InstantSSRAutoRefresh />
|
||||
|
||||
{!shouldShowWelcomeModal &&
|
||||
!shouldDisplayNoSourcesModal &&
|
||||
@@ -200,7 +201,6 @@ export default async function Home() {
|
||||
Only used in the EE version of the app. */}
|
||||
<ChatPopup />
|
||||
|
||||
<InstantSSRAutoRefresh />
|
||||
<WrappedSearch
|
||||
disabledAgentic={DISABLE_LLM_DOC_RELEVANCE}
|
||||
initiallyToggled={toggleSidebar}
|
||||
|
@@ -10,12 +10,24 @@ import {
|
||||
import { fetchSS } from "@/lib/utilsSS";
|
||||
import { getWebVersion } from "@/lib/version";
|
||||
|
||||
export async function fetchStandardSettingsSS() {
|
||||
return fetchSS("/settings");
|
||||
}
|
||||
|
||||
export async function fetchEnterpriseSettingsSS() {
|
||||
return fetchSS("/enterprise-settings");
|
||||
}
|
||||
|
||||
export async function fetchCustomAnalyticsScriptSS() {
|
||||
return fetchSS("/enterprise-settings/custom-analytics-script");
|
||||
}
|
||||
|
||||
export async function fetchSettingsSS() {
|
||||
const tasks = [fetchSS("/settings")];
|
||||
const tasks = [fetchStandardSettingsSS()];
|
||||
if (SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED) {
|
||||
tasks.push(fetchSS("/enterprise-settings"));
|
||||
tasks.push(fetchEnterpriseSettingsSS());
|
||||
if (CUSTOM_ANALYTICS_ENABLED) {
|
||||
tasks.push(fetchSS("/enterprise-settings/custom-analytics-script"));
|
||||
tasks.push(fetchCustomAnalyticsScriptSS());
|
||||
}
|
||||
}
|
||||
|
||||
|
236
web/src/lib/chat/fetchSomeChatData.ts
Normal file
236
web/src/lib/chat/fetchSomeChatData.ts
Normal file
@@ -0,0 +1,236 @@
|
||||
import {
|
||||
AuthTypeMetadata,
|
||||
getAuthTypeMetadataSS,
|
||||
getCurrentUserSS,
|
||||
} from "@/lib/userSS";
|
||||
import { fetchSS } from "@/lib/utilsSS";
|
||||
import {
|
||||
CCPairBasicInfo,
|
||||
DocumentSet,
|
||||
Tag,
|
||||
User,
|
||||
ValidSources,
|
||||
} from "@/lib/types";
|
||||
import { ChatSession } from "@/app/chat/interfaces";
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { InputPrompt } from "@/app/admin/prompt-library/interfaces";
|
||||
import { FullEmbeddingModelResponse } from "@/components/embedding/interfaces";
|
||||
import { Settings } from "@/app/admin/settings/interfaces";
|
||||
import { fetchLLMProvidersSS } from "@/lib/llm/fetchLLMs";
|
||||
import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces";
|
||||
import { Folder } from "@/app/chat/folders/interfaces";
|
||||
import { personaComparator } from "@/app/admin/assistants/lib";
|
||||
import { cookies } from "next/headers";
|
||||
import {
|
||||
SIDEBAR_TOGGLED_COOKIE_NAME,
|
||||
DOCUMENT_SIDEBAR_WIDTH_COOKIE_NAME,
|
||||
} from "@/components/resizable/constants";
|
||||
import { hasCompletedWelcomeFlowSS } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
|
||||
import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS";
|
||||
import { NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN } from "../constants";
|
||||
|
||||
interface FetchChatDataResult {
|
||||
user?: User | null;
|
||||
chatSessions?: ChatSession[];
|
||||
ccPairs?: CCPairBasicInfo[];
|
||||
availableSources?: ValidSources[];
|
||||
documentSets?: DocumentSet[];
|
||||
assistants?: Persona[];
|
||||
tags?: Tag[];
|
||||
llmProviders?: LLMProviderDescriptor[];
|
||||
folders?: Folder[];
|
||||
openedFolders?: Record<string, boolean>;
|
||||
defaultAssistantId?: number;
|
||||
toggleSidebar?: boolean;
|
||||
finalDocumentSidebarInitialWidth?: number;
|
||||
shouldShowWelcomeModal?: boolean;
|
||||
shouldDisplaySourcesIncompleteModal?: boolean;
|
||||
userInputPrompts?: InputPrompt[];
|
||||
}
|
||||
|
||||
type FetchOption =
|
||||
| "user"
|
||||
| "chatSessions"
|
||||
| "ccPairs"
|
||||
| "documentSets"
|
||||
| "assistants"
|
||||
| "tags"
|
||||
| "llmProviders"
|
||||
| "folders"
|
||||
| "userInputPrompts";
|
||||
|
||||
/*
|
||||
NOTE: currently unused, but leaving here for future use.
|
||||
*/
|
||||
export async function fetchSomeChatData(
|
||||
searchParams: { [key: string]: string },
|
||||
fetchOptions: FetchOption[] = []
|
||||
): Promise<FetchChatDataResult | { redirect: string }> {
|
||||
const tasks: Promise<any>[] = [];
|
||||
const taskMap: Record<FetchOption, () => Promise<any>> = {
|
||||
user: getCurrentUserSS,
|
||||
chatSessions: () => fetchSS("/chat/get-user-chat-sessions"),
|
||||
ccPairs: () => fetchSS("/manage/indexing-status"),
|
||||
documentSets: () => fetchSS("/manage/document-set"),
|
||||
assistants: fetchAssistantsSS,
|
||||
tags: () => fetchSS("/query/valid-tags"),
|
||||
llmProviders: fetchLLMProvidersSS,
|
||||
folders: () => fetchSS("/folder"),
|
||||
userInputPrompts: () => fetchSS("/input_prompt?include_public=true"),
|
||||
};
|
||||
|
||||
// Always fetch auth type metadata
|
||||
tasks.push(getAuthTypeMetadataSS());
|
||||
|
||||
// Add tasks based on fetchOptions
|
||||
fetchOptions.forEach((option) => {
|
||||
if (taskMap[option]) {
|
||||
tasks.push(taskMap[option]());
|
||||
}
|
||||
});
|
||||
|
||||
let results: any[] = await Promise.all(tasks);
|
||||
|
||||
const authTypeMetadata = results.shift() as AuthTypeMetadata | null;
|
||||
const authDisabled = authTypeMetadata?.authType === "disabled";
|
||||
|
||||
let user: User | null = null;
|
||||
if (fetchOptions.includes("user")) {
|
||||
user = results.shift();
|
||||
if (!authDisabled && !user) {
|
||||
return { redirect: "/auth/login" };
|
||||
}
|
||||
if (user && !user.is_verified && authTypeMetadata?.requiresVerification) {
|
||||
return { redirect: "/auth/waiting-on-verification" };
|
||||
}
|
||||
}
|
||||
|
||||
const result: FetchChatDataResult = {};
|
||||
|
||||
for (let i = 0; i < fetchOptions.length; i++) {
|
||||
const option = fetchOptions[i];
|
||||
const result = results[i];
|
||||
|
||||
switch (option) {
|
||||
case "user":
|
||||
result.user = user;
|
||||
break;
|
||||
case "chatSessions":
|
||||
result.chatSessions = result?.ok
|
||||
? ((await result.json()) as { sessions: ChatSession[] }).sessions
|
||||
: [];
|
||||
break;
|
||||
case "ccPairs":
|
||||
result.ccPairs = result?.ok
|
||||
? ((await result.json()) as CCPairBasicInfo[])
|
||||
: [];
|
||||
break;
|
||||
case "documentSets":
|
||||
result.documentSets = result?.ok
|
||||
? ((await result.json()) as DocumentSet[])
|
||||
: [];
|
||||
break;
|
||||
case "assistants":
|
||||
const [rawAssistantsList, assistantsFetchError] = result as [
|
||||
Persona[],
|
||||
string | null,
|
||||
];
|
||||
result.assistants = rawAssistantsList
|
||||
.filter((assistant) => assistant.is_visible)
|
||||
.sort(personaComparator);
|
||||
break;
|
||||
case "tags":
|
||||
result.tags = result?.ok
|
||||
? ((await result.json()) as { tags: Tag[] }).tags
|
||||
: [];
|
||||
break;
|
||||
case "llmProviders":
|
||||
result.llmProviders = result || [];
|
||||
break;
|
||||
case "folders":
|
||||
result.folders = result?.ok
|
||||
? ((await result.json()) as { folders: Folder[] }).folders
|
||||
: [];
|
||||
break;
|
||||
case "userInputPrompts":
|
||||
result.userInputPrompts = result?.ok
|
||||
? ((await result.json()) as InputPrompt[])
|
||||
: [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.ccPairs) {
|
||||
result.availableSources = Array.from(
|
||||
new Set(result.ccPairs.map((ccPair) => ccPair.source))
|
||||
);
|
||||
}
|
||||
|
||||
if (result.chatSessions) {
|
||||
result.chatSessions.sort((a, b) => (a.id > b.id ? -1 : 1));
|
||||
}
|
||||
|
||||
if (fetchOptions.includes("assistants") && result.assistants) {
|
||||
const hasAnyConnectors = result.ccPairs && result.ccPairs.length > 0;
|
||||
if (!hasAnyConnectors) {
|
||||
result.assistants = result.assistants.filter(
|
||||
(assistant) => assistant.num_chunks === 0
|
||||
);
|
||||
}
|
||||
|
||||
const hasOpenAIProvider =
|
||||
result.llmProviders &&
|
||||
result.llmProviders.some((provider) => provider.provider === "openai");
|
||||
if (!hasOpenAIProvider) {
|
||||
result.assistants = result.assistants.filter(
|
||||
(assistant) =>
|
||||
!assistant.tools.some(
|
||||
(tool) => tool.in_code_tool_id === "ImageGenerationTool"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (fetchOptions.includes("folders")) {
|
||||
const openedFoldersCookie = cookies().get("openedFolders");
|
||||
result.openedFolders = openedFoldersCookie
|
||||
? JSON.parse(openedFoldersCookie.value)
|
||||
: {};
|
||||
}
|
||||
|
||||
const defaultAssistantIdRaw = searchParams["assistantId"];
|
||||
result.defaultAssistantId = defaultAssistantIdRaw
|
||||
? parseInt(defaultAssistantIdRaw)
|
||||
: undefined;
|
||||
|
||||
const documentSidebarCookieInitialWidth = cookies().get(
|
||||
DOCUMENT_SIDEBAR_WIDTH_COOKIE_NAME
|
||||
);
|
||||
const sidebarToggled = cookies().get(SIDEBAR_TOGGLED_COOKIE_NAME);
|
||||
|
||||
result.toggleSidebar = sidebarToggled
|
||||
? sidebarToggled.value.toLowerCase() === "true"
|
||||
: NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN;
|
||||
|
||||
result.finalDocumentSidebarInitialWidth = documentSidebarCookieInitialWidth
|
||||
? parseInt(documentSidebarCookieInitialWidth.value)
|
||||
: undefined;
|
||||
|
||||
if (fetchOptions.includes("ccPairs") && result.ccPairs) {
|
||||
const hasAnyConnectors = result.ccPairs.length > 0;
|
||||
result.shouldShowWelcomeModal =
|
||||
!hasCompletedWelcomeFlowSS() &&
|
||||
!hasAnyConnectors &&
|
||||
(!user || user.role === "admin");
|
||||
|
||||
result.shouldDisplaySourcesIncompleteModal =
|
||||
hasAnyConnectors &&
|
||||
!result.shouldShowWelcomeModal &&
|
||||
!result.ccPairs.some(
|
||||
(ccPair) => ccPair.has_successful_run && ccPair.docs_indexed > 0
|
||||
) &&
|
||||
(!user || user.role === "admin");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
Reference in New Issue
Block a user