mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-04-07 11:28:09 +02:00
Clearer onboarding + Provider Updates (#2361)
This commit is contained in:
parent
148c2a7375
commit
ace041415a
@ -31,11 +31,13 @@ export function LLMProviderUpdateForm({
|
||||
existingLlmProvider,
|
||||
shouldMarkAsDefault,
|
||||
setPopup,
|
||||
hideAdvanced,
|
||||
}: {
|
||||
llmProviderDescriptor: WellKnownLLMProviderDescriptor;
|
||||
onClose: () => void;
|
||||
existingLlmProvider?: FullLLMProvider;
|
||||
shouldMarkAsDefault?: boolean;
|
||||
hideAdvanced?: boolean;
|
||||
setPopup?: (popup: PopupSpec) => void;
|
||||
}) {
|
||||
const { mutate } = useSWRConfig();
|
||||
@ -52,7 +54,7 @@ export function LLMProviderUpdateForm({
|
||||
|
||||
// Define the initial values based on the provider's requirements
|
||||
const initialValues = {
|
||||
name: existingLlmProvider?.name ?? "",
|
||||
name: existingLlmProvider?.name || (hideAdvanced ? "Default" : ""),
|
||||
api_key: existingLlmProvider?.api_key ?? "",
|
||||
api_base: existingLlmProvider?.api_base ?? "",
|
||||
api_version: existingLlmProvider?.api_version ?? "",
|
||||
@ -218,17 +220,20 @@ export function LLMProviderUpdateForm({
|
||||
}}
|
||||
>
|
||||
{({ values, setFieldValue }) => (
|
||||
<Form className="gap-y-6 items-stretch mt-8">
|
||||
<TextFormField
|
||||
name="name"
|
||||
label="Display Name"
|
||||
subtext="A name which you can use to identify this provider when selecting it in the UI."
|
||||
placeholder="Display Name"
|
||||
disabled={existingLlmProvider ? true : false}
|
||||
/>
|
||||
<Form className="gap-y-4 items-stretch mt-6">
|
||||
{!hideAdvanced && (
|
||||
<TextFormField
|
||||
name="name"
|
||||
label="Display Name"
|
||||
subtext="A name which you can use to identify this provider when selecting it in the UI."
|
||||
placeholder="Display Name"
|
||||
disabled={existingLlmProvider ? true : false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{llmProviderDescriptor.api_key_required && (
|
||||
<TextFormField
|
||||
small={hideAdvanced}
|
||||
name="api_key"
|
||||
label="API Key"
|
||||
placeholder="API Key"
|
||||
@ -238,6 +243,7 @@ export function LLMProviderUpdateForm({
|
||||
|
||||
{llmProviderDescriptor.api_base_required && (
|
||||
<TextFormField
|
||||
small={hideAdvanced}
|
||||
name="api_base"
|
||||
label="API Base"
|
||||
placeholder="API Base"
|
||||
@ -246,6 +252,7 @@ export function LLMProviderUpdateForm({
|
||||
|
||||
{llmProviderDescriptor.api_version_required && (
|
||||
<TextFormField
|
||||
small={hideAdvanced}
|
||||
name="api_version"
|
||||
label="API Version"
|
||||
placeholder="API Version"
|
||||
@ -255,6 +262,7 @@ export function LLMProviderUpdateForm({
|
||||
{llmProviderDescriptor.custom_config_keys?.map((customConfigKey) => (
|
||||
<div key={customConfigKey.name}>
|
||||
<TextFormField
|
||||
small={hideAdvanced}
|
||||
name={`custom_config.${customConfigKey.name}`}
|
||||
label={
|
||||
customConfigKey.is_required
|
||||
@ -266,134 +274,144 @@ export function LLMProviderUpdateForm({
|
||||
</div>
|
||||
))}
|
||||
|
||||
<Divider />
|
||||
|
||||
{llmProviderDescriptor.llm_names.length > 0 ? (
|
||||
<SelectorFormField
|
||||
name="default_model_name"
|
||||
subtext="The model to use by default for this provider unless otherwise specified."
|
||||
label="Default Model"
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
name: getDisplayNameForModel(name),
|
||||
value: name,
|
||||
}))}
|
||||
maxHeight="max-h-56"
|
||||
/>
|
||||
) : (
|
||||
<TextFormField
|
||||
name="default_model_name"
|
||||
subtext="The model to use by default for this provider unless otherwise specified."
|
||||
label="Default Model"
|
||||
placeholder="E.g. gpt-4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{llmProviderDescriptor.llm_names.length > 0 ? (
|
||||
<SelectorFormField
|
||||
name="fast_default_model_name"
|
||||
subtext={`The model to use for lighter flows like \`LLM Chunk Filter\`
|
||||
for this provider. If \`Default\` is specified, will use
|
||||
the Default Model configured above.`}
|
||||
label="[Optional] Fast Model"
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
name: getDisplayNameForModel(name),
|
||||
value: name,
|
||||
}))}
|
||||
includeDefault
|
||||
maxHeight="max-h-56"
|
||||
/>
|
||||
) : (
|
||||
<TextFormField
|
||||
name="fast_default_model_name"
|
||||
subtext={`The model to use for lighter flows like \`LLM Chunk Filter\`
|
||||
for this provider. If \`Default\` is specified, will use
|
||||
the Default Model configured above.`}
|
||||
label="[Optional] Fast Model"
|
||||
placeholder="E.g. gpt-4"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
{llmProviderDescriptor.name != "azure" && (
|
||||
<AdvancedOptionsToggle
|
||||
showAdvancedOptions={showAdvancedOptions}
|
||||
setShowAdvancedOptions={setShowAdvancedOptions}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showAdvancedOptions && (
|
||||
{!hideAdvanced && (
|
||||
<>
|
||||
{llmProviderDescriptor.llm_names.length > 0 && (
|
||||
<div className="w-full">
|
||||
<MultiSelectField
|
||||
selectedInitially={values.display_model_names}
|
||||
name="display_model_names"
|
||||
label="Display Models"
|
||||
subtext="Select the models to make available to users. Unselected models will not be available."
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
value: name,
|
||||
label: getDisplayNameForModel(name),
|
||||
}))}
|
||||
onChange={(selected) =>
|
||||
setFieldValue("display_model_names", selected)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
{llmProviderDescriptor.llm_names.length > 0 ? (
|
||||
<SelectorFormField
|
||||
name="default_model_name"
|
||||
subtext="The model to use by default for this provider unless otherwise specified."
|
||||
label="Default Model"
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
name: getDisplayNameForModel(name),
|
||||
value: name,
|
||||
}))}
|
||||
maxHeight="max-h-56"
|
||||
/>
|
||||
) : (
|
||||
<TextFormField
|
||||
name="default_model_name"
|
||||
subtext="The model to use by default for this provider unless otherwise specified."
|
||||
label="Default Model"
|
||||
placeholder="E.g. gpt-4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{isPaidEnterpriseFeaturesEnabled && userGroups && (
|
||||
<>
|
||||
<BooleanFormField
|
||||
small
|
||||
removeIndent
|
||||
alignTop
|
||||
name="is_public"
|
||||
label="Is Public?"
|
||||
subtext="If set, this LLM Provider will be available to all users. If not, only the specified User Groups will be able to use it."
|
||||
/>
|
||||
{llmProviderDescriptor.llm_names.length > 0 ? (
|
||||
<SelectorFormField
|
||||
name="fast_default_model_name"
|
||||
subtext={`The model to use for lighter flows like \`LLM Chunk Filter\`
|
||||
for this provider. If \`Default\` is specified, will use
|
||||
the Default Model configured above.`}
|
||||
label="[Optional] Fast Model"
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
name: getDisplayNameForModel(name),
|
||||
value: name,
|
||||
}))}
|
||||
includeDefault
|
||||
maxHeight="max-h-56"
|
||||
/>
|
||||
) : (
|
||||
<TextFormField
|
||||
name="fast_default_model_name"
|
||||
subtext={`The model to use for lighter flows like \`LLM Chunk Filter\`
|
||||
for this provider. If \`Default\` is specified, will use
|
||||
the Default Model configured above.`}
|
||||
label="[Optional] Fast Model"
|
||||
placeholder="E.g. gpt-4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{userGroups && userGroups.length > 0 && !values.is_public && (
|
||||
<div>
|
||||
<Text>
|
||||
Select which User Groups should have access to this LLM
|
||||
Provider.
|
||||
</Text>
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
{userGroups.map((userGroup) => {
|
||||
const isSelected = values.groups.includes(
|
||||
userGroup.id
|
||||
);
|
||||
return (
|
||||
<Bubble
|
||||
key={userGroup.id}
|
||||
isSelected={isSelected}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
setFieldValue(
|
||||
"groups",
|
||||
values.groups.filter(
|
||||
(id) => id !== userGroup.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
setFieldValue("groups", [
|
||||
...values.groups,
|
||||
userGroup.id,
|
||||
]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex">
|
||||
<GroupsIcon />
|
||||
<div className="ml-1">{userGroup.name}</div>
|
||||
</div>
|
||||
</Bubble>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
{llmProviderDescriptor.name != "azure" && (
|
||||
<AdvancedOptionsToggle
|
||||
showAdvancedOptions={showAdvancedOptions}
|
||||
setShowAdvancedOptions={setShowAdvancedOptions}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showAdvancedOptions && (
|
||||
<>
|
||||
{llmProviderDescriptor.llm_names.length > 0 && (
|
||||
<div className="w-full">
|
||||
<MultiSelectField
|
||||
selectedInitially={values.display_model_names}
|
||||
name="display_model_names"
|
||||
label="Display Models"
|
||||
subtext="Select the models to make available to users. Unselected models will not be available."
|
||||
options={llmProviderDescriptor.llm_names.map(
|
||||
(name) => ({
|
||||
value: name,
|
||||
label: getDisplayNameForModel(name),
|
||||
})
|
||||
)}
|
||||
onChange={(selected) =>
|
||||
setFieldValue("display_model_names", selected)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isPaidEnterpriseFeaturesEnabled && userGroups && (
|
||||
<>
|
||||
<BooleanFormField
|
||||
small
|
||||
removeIndent
|
||||
alignTop
|
||||
name="is_public"
|
||||
label="Is Public?"
|
||||
subtext="If set, this LLM Provider will be available to all users. If not, only the specified User Groups will be able to use it."
|
||||
/>
|
||||
|
||||
{userGroups &&
|
||||
userGroups.length > 0 &&
|
||||
!values.is_public && (
|
||||
<div>
|
||||
<Text>
|
||||
Select which User Groups should have access to
|
||||
this LLM Provider.
|
||||
</Text>
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
{userGroups.map((userGroup) => {
|
||||
const isSelected = values.groups.includes(
|
||||
userGroup.id
|
||||
);
|
||||
return (
|
||||
<Bubble
|
||||
key={userGroup.id}
|
||||
isSelected={isSelected}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
setFieldValue(
|
||||
"groups",
|
||||
values.groups.filter(
|
||||
(id) => id !== userGroup.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
setFieldValue("groups", [
|
||||
...values.groups,
|
||||
userGroup.id,
|
||||
]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex">
|
||||
<GroupsIcon />
|
||||
<div className="ml-1">
|
||||
{userGroup.name}
|
||||
</div>
|
||||
</div>
|
||||
</Bubble>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@ -432,6 +450,27 @@ export function LLMProviderUpdateForm({
|
||||
return;
|
||||
}
|
||||
|
||||
// If the deleted provider was the default, set the first remaining provider as default
|
||||
const remainingProvidersResponse = await fetch(
|
||||
LLM_PROVIDERS_ADMIN_URL
|
||||
);
|
||||
if (remainingProvidersResponse.ok) {
|
||||
const remainingProviders =
|
||||
await remainingProvidersResponse.json();
|
||||
|
||||
if (remainingProviders.length > 0) {
|
||||
const setDefaultResponse = await fetch(
|
||||
`${LLM_PROVIDERS_ADMIN_URL}/${remainingProviders[0].id}/default`,
|
||||
{
|
||||
method: "POST",
|
||||
}
|
||||
);
|
||||
if (!setDefaultResponse.ok) {
|
||||
console.error("Failed to set new default provider");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutate(LLM_PROVIDERS_ADMIN_URL);
|
||||
onClose();
|
||||
}}
|
||||
|
@ -98,6 +98,7 @@ import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
|
||||
|
||||
import { SEARCH_TOOL_NAME } from "./tools/constants";
|
||||
import { useUser } from "@/components/user/UserProvider";
|
||||
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
||||
|
||||
const TEMP_USER_MESSAGE_ID = -1;
|
||||
const TEMP_ASSISTANT_MESSAGE_ID = -2;
|
||||
@ -106,12 +107,10 @@ const SYSTEM_MESSAGE_ID = -3;
|
||||
export function ChatPage({
|
||||
toggle,
|
||||
documentSidebarInitialWidth,
|
||||
defaultSelectedAssistantId,
|
||||
toggledSidebar,
|
||||
}: {
|
||||
toggle: (toggled?: boolean) => void;
|
||||
documentSidebarInitialWidth?: number;
|
||||
defaultSelectedAssistantId?: number;
|
||||
toggledSidebar: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
@ -126,8 +125,13 @@ export function ChatPage({
|
||||
folders,
|
||||
openedFolders,
|
||||
userInputPrompts,
|
||||
defaultAssistantId,
|
||||
shouldShowWelcomeModal,
|
||||
refreshChatSessions,
|
||||
} = useChatContext();
|
||||
|
||||
const [showApiKeyModal, setShowApiKeyModal] = useState(true);
|
||||
|
||||
const { user, refreshUser, isLoadingUser } = useUser();
|
||||
|
||||
// chat session
|
||||
@ -162,9 +166,9 @@ export function ChatPage({
|
||||
? availableAssistants.find(
|
||||
(assistant) => assistant.id === existingChatSessionAssistantId
|
||||
)
|
||||
: defaultSelectedAssistantId !== undefined
|
||||
: defaultAssistantId !== undefined
|
||||
? availableAssistants.find(
|
||||
(assistant) => assistant.id === defaultSelectedAssistantId
|
||||
(assistant) => assistant.id === defaultAssistantId
|
||||
)
|
||||
: undefined
|
||||
);
|
||||
@ -327,8 +331,8 @@ export function ChatPage({
|
||||
async function initialSessionFetch() {
|
||||
if (existingChatSessionId === null) {
|
||||
setIsFetchingChatMessages(false);
|
||||
if (defaultSelectedAssistantId !== undefined) {
|
||||
setSelectedAssistantFromId(defaultSelectedAssistantId);
|
||||
if (defaultAssistantId !== undefined) {
|
||||
setSelectedAssistantFromId(defaultAssistantId);
|
||||
} else {
|
||||
setSelectedAssistant(undefined);
|
||||
}
|
||||
@ -402,7 +406,7 @@ export function ChatPage({
|
||||
// force re-name if the chat session doesn't have one
|
||||
if (!chatSession.description) {
|
||||
await nameChatSession(existingChatSessionId, seededMessage);
|
||||
router.refresh(); // need to refresh to update name on sidebar
|
||||
refreshChatSessions();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -676,12 +680,10 @@ export function ChatPage({
|
||||
useEffect(() => {
|
||||
if (messageHistory.length === 0 && chatSessionIdRef.current === null) {
|
||||
setSelectedAssistant(
|
||||
filteredAssistants.find(
|
||||
(persona) => persona.id === defaultSelectedAssistantId
|
||||
)
|
||||
filteredAssistants.find((persona) => persona.id === defaultAssistantId)
|
||||
);
|
||||
}
|
||||
}, [defaultSelectedAssistantId]);
|
||||
}, [defaultAssistantId]);
|
||||
|
||||
const [
|
||||
selectedDocuments,
|
||||
@ -1111,6 +1113,12 @@ export function ChatPage({
|
||||
console.error(
|
||||
"First packet should contain message response info "
|
||||
);
|
||||
if (Object.hasOwn(packet, "error")) {
|
||||
const error = (packet as StreamingError).error;
|
||||
setLoadingError(error);
|
||||
updateChatState("input");
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1330,6 +1338,7 @@ export function ChatPage({
|
||||
if (!searchParamBasedChatSessionName) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
await nameChatSession(currChatSessionId, currMessage);
|
||||
refreshChatSessions();
|
||||
}
|
||||
|
||||
// NOTE: don't switch pages if the user has navigated away from the chat
|
||||
@ -1465,6 +1474,7 @@ export function ChatPage({
|
||||
|
||||
// Used to maintain a "time out" for history sidebar so our existing refs can have time to process change
|
||||
const [untoggled, setUntoggled] = useState(false);
|
||||
const [loadingError, setLoadingError] = useState<string | null>(null);
|
||||
|
||||
const explicitlyUntoggle = () => {
|
||||
setShowDocSidebar(false);
|
||||
@ -1588,6 +1598,11 @@ export function ChatPage({
|
||||
return (
|
||||
<>
|
||||
<HealthCheckBanner />
|
||||
|
||||
{showApiKeyModal && !shouldShowWelcomeModal && (
|
||||
<ApiKeyModal hide={() => setShowApiKeyModal(false)} />
|
||||
)}
|
||||
|
||||
{/* 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}
|
||||
@ -1760,7 +1775,6 @@ export function ChatPage({
|
||||
className={`h-full w-full relative flex-auto transition-margin duration-300 overflow-x-auto mobile:pb-12 desktop:pb-[100px]`}
|
||||
{...getRootProps()}
|
||||
>
|
||||
{/* <input {...getInputProps()} /> */}
|
||||
<div
|
||||
className={`w-full h-full flex flex-col overflow-y-auto include-scrollbar overflow-x-hidden relative`}
|
||||
ref={scrollableDivRef}
|
||||
@ -1770,7 +1784,8 @@ export function ChatPage({
|
||||
|
||||
{messageHistory.length === 0 &&
|
||||
!isFetchingChatMessages &&
|
||||
currentSessionChatState == "input" && (
|
||||
currentSessionChatState == "input" &&
|
||||
!loadingError && (
|
||||
<ChatIntro
|
||||
availableSources={finalAvailableSources}
|
||||
selectedPersona={liveAssistant}
|
||||
@ -2078,16 +2093,17 @@ export function ChatPage({
|
||||
}
|
||||
})}
|
||||
|
||||
{currentSessionChatState == "loading" &&
|
||||
!currentSessionRegenerationState?.regenerating &&
|
||||
messageHistory[messageHistory.length - 1]?.type !=
|
||||
"user" && (
|
||||
<HumanMessage
|
||||
key={-2}
|
||||
messageId={-1}
|
||||
content={submittedMessage}
|
||||
/>
|
||||
)}
|
||||
{currentSessionChatState == "loading" ||
|
||||
(loadingError &&
|
||||
!currentSessionRegenerationState?.regenerating &&
|
||||
messageHistory[messageHistory.length - 1]
|
||||
?.type != "user" && (
|
||||
<HumanMessage
|
||||
key={-2}
|
||||
messageId={-1}
|
||||
content={submittedMessage}
|
||||
/>
|
||||
))}
|
||||
|
||||
{currentSessionChatState == "loading" && (
|
||||
<div
|
||||
@ -2116,6 +2132,20 @@ export function ChatPage({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loadingError && (
|
||||
<div key={-1}>
|
||||
<AIMessage
|
||||
currentPersona={liveAssistant}
|
||||
messageId={-1}
|
||||
personaName={liveAssistant.name}
|
||||
content={
|
||||
<p className="text-red-700 text-sm my-auto">
|
||||
{loadingError}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{currentPersona &&
|
||||
currentPersona.starter_messages &&
|
||||
currentPersona.starter_messages.length > 0 &&
|
||||
@ -2177,6 +2207,9 @@ export function ChatPage({
|
||||
</div>
|
||||
)}
|
||||
<ChatInputBar
|
||||
showConfigureAPIKey={() =>
|
||||
setShowApiKeyModal(true)
|
||||
}
|
||||
chatState={currentSessionChatState}
|
||||
stopGenerating={stopGenerating}
|
||||
openModelSettings={() => setSettingsToggled(true)}
|
||||
|
@ -3,21 +3,15 @@ import { ChatPage } from "./ChatPage";
|
||||
import FunctionalWrapper from "./shared_chat_search/FunctionalWrapper";
|
||||
|
||||
export default function WrappedChat({
|
||||
defaultAssistantId,
|
||||
initiallyToggled,
|
||||
}: {
|
||||
defaultAssistantId?: number;
|
||||
initiallyToggled: boolean;
|
||||
}) {
|
||||
return (
|
||||
<FunctionalWrapper
|
||||
initiallyToggled={initiallyToggled}
|
||||
content={(toggledSidebar, toggle) => (
|
||||
<ChatPage
|
||||
toggle={toggle}
|
||||
defaultSelectedAssistantId={defaultAssistantId}
|
||||
toggledSidebar={toggledSidebar}
|
||||
/>
|
||||
<ChatPage toggle={toggle} toggledSidebar={toggledSidebar} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
@ -33,12 +33,15 @@ import { Tooltip } from "@/components/tooltip/Tooltip";
|
||||
import { Hoverable } from "@/components/Hoverable";
|
||||
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
||||
import { ChatState } from "../types";
|
||||
import UnconfiguredProviderText from "@/components/chat_search/UnconfiguredProviderText";
|
||||
import { useSearchContext } from "@/components/context/SearchContext";
|
||||
|
||||
const MAX_INPUT_HEIGHT = 200;
|
||||
|
||||
export function ChatInputBar({
|
||||
openModelSettings,
|
||||
showDocs,
|
||||
showConfigureAPIKey,
|
||||
selectedDocuments,
|
||||
message,
|
||||
setMessage,
|
||||
@ -62,6 +65,7 @@ export function ChatInputBar({
|
||||
chatSessionId,
|
||||
inputPrompts,
|
||||
}: {
|
||||
showConfigureAPIKey: () => void;
|
||||
openModelSettings: () => void;
|
||||
chatState: ChatState;
|
||||
stopGenerating: () => void;
|
||||
@ -111,6 +115,7 @@ export function ChatInputBar({
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const settings = useContext(SettingsContext);
|
||||
|
||||
const { llmProviders } = useChatContext();
|
||||
@ -364,6 +369,9 @@ export function ChatInputBar({
|
||||
<div>
|
||||
<SelectedFilterDisplay filterManager={filterManager} />
|
||||
</div>
|
||||
|
||||
<UnconfiguredProviderText showConfigureAPIKey={showConfigureAPIKey} />
|
||||
|
||||
<div
|
||||
className="
|
||||
opacity-100
|
||||
|
@ -2,10 +2,10 @@ import { redirect } from "next/navigation";
|
||||
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 { ChatProvider } from "@/components/context/ChatContext";
|
||||
import { fetchChatData } from "@/lib/chat/fetchChatData";
|
||||
import WrappedChat from "./WrappedChat";
|
||||
import { ProviderContextProvider } from "@/components/chat_search/ProviderContext";
|
||||
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
@ -23,7 +23,6 @@ export default async function Page({
|
||||
const {
|
||||
user,
|
||||
chatSessions,
|
||||
ccPairs,
|
||||
availableSources,
|
||||
documentSets,
|
||||
assistants,
|
||||
@ -33,9 +32,7 @@ export default async function Page({
|
||||
toggleSidebar,
|
||||
openedFolders,
|
||||
defaultAssistantId,
|
||||
finalDocumentSidebarInitialWidth,
|
||||
shouldShowWelcomeModal,
|
||||
shouldDisplaySourcesIncompleteModal,
|
||||
userInputPrompts,
|
||||
} = data;
|
||||
|
||||
@ -43,9 +40,7 @@ export default async function Page({
|
||||
<>
|
||||
<InstantSSRAutoRefresh />
|
||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||
{!shouldShowWelcomeModal && !shouldDisplaySourcesIncompleteModal && (
|
||||
<ApiKeyModal user={user} />
|
||||
)}
|
||||
|
||||
<ChatProvider
|
||||
value={{
|
||||
chatSessions,
|
||||
@ -57,12 +52,13 @@ export default async function Page({
|
||||
folders,
|
||||
openedFolders,
|
||||
userInputPrompts,
|
||||
shouldShowWelcomeModal,
|
||||
defaultAssistantId,
|
||||
}}
|
||||
>
|
||||
<WrappedChat
|
||||
defaultAssistantId={defaultAssistantId}
|
||||
initiallyToggled={toggleSidebar}
|
||||
/>
|
||||
<ProviderContextProvider>
|
||||
<WrappedChat initiallyToggled={toggleSidebar} />
|
||||
</ProviderContextProvider>
|
||||
</ChatProvider>
|
||||
</>
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
} from "@/components/settings/lib";
|
||||
import {
|
||||
CUSTOM_ANALYTICS_ENABLED,
|
||||
EE_ENABLED,
|
||||
SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED,
|
||||
} from "@/lib/constants";
|
||||
import { SettingsProvider } from "@/components/settings/SettingsProvider";
|
||||
@ -53,6 +54,7 @@ export default async function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const combinedSettings = await fetchSettingsSS();
|
||||
|
||||
if (!combinedSettings) {
|
||||
// Just display a simple full page error if fetching fails.
|
||||
|
||||
@ -72,8 +74,34 @@ export default async function RootLayout({
|
||||
<h1 className="text-2xl font-bold mb-4 text-error">Error</h1>
|
||||
<p className="text-text-500">
|
||||
Your Danswer instance was not configured properly and your
|
||||
settings could not be loaded. Please contact your admin to fix
|
||||
this error.
|
||||
settings could not be loaded. This could be due to an admin
|
||||
configuration issue or an incomplete setup.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
If you're an admin, please check{" "}
|
||||
<a
|
||||
className="text-link"
|
||||
href="https://docs.danswer.dev/introduction?utm_source=app&utm_medium=error_page&utm_campaign=config_error"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
our docs
|
||||
</a>{" "}
|
||||
to see how to configure Danswer properly. If you're a user,
|
||||
please contact your admin to fix this error.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
For additional support and guidance, you can reach out to our
|
||||
community on{" "}
|
||||
<a
|
||||
className="text-link"
|
||||
href="https://danswer.ai?utm_source=app&utm_medium=error_page&utm_campaign=config_error"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -1,31 +1,12 @@
|
||||
"use client";
|
||||
import { SearchSection } from "@/components/search/SearchSection";
|
||||
import FunctionalWrapper from "../chat/shared_chat_search/FunctionalWrapper";
|
||||
import { CCPairBasicInfo, DocumentSet, Tag, User } from "@/lib/types";
|
||||
import { Persona } from "../admin/assistants/interfaces";
|
||||
import { ChatSession } from "../chat/interfaces";
|
||||
|
||||
export default function WrappedSearch({
|
||||
querySessions,
|
||||
ccPairs,
|
||||
documentSets,
|
||||
personas,
|
||||
searchTypeDefault,
|
||||
tags,
|
||||
user,
|
||||
agenticSearchEnabled,
|
||||
initiallyToggled,
|
||||
disabledAgentic,
|
||||
}: {
|
||||
disabledAgentic: boolean;
|
||||
querySessions: ChatSession[];
|
||||
ccPairs: CCPairBasicInfo[];
|
||||
documentSets: DocumentSet[];
|
||||
personas: Persona[];
|
||||
searchTypeDefault: string;
|
||||
tags: Tag[];
|
||||
user: User | null;
|
||||
agenticSearchEnabled: boolean;
|
||||
initiallyToggled: boolean;
|
||||
}) {
|
||||
return (
|
||||
@ -33,16 +14,8 @@ export default function WrappedSearch({
|
||||
initiallyToggled={initiallyToggled}
|
||||
content={(toggledSidebar, toggle) => (
|
||||
<SearchSection
|
||||
disabledAgentic={disabledAgentic}
|
||||
agenticSearchEnabled={agenticSearchEnabled}
|
||||
toggle={toggle}
|
||||
toggledSidebar={toggledSidebar}
|
||||
querySessions={querySessions}
|
||||
user={user}
|
||||
ccPairs={ccPairs}
|
||||
documentSets={documentSets}
|
||||
personas={personas}
|
||||
tags={tags}
|
||||
defaultSearchType={searchTypeDefault}
|
||||
/>
|
||||
)}
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
} from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
||||
import { fetchSS } from "@/lib/utilsSS";
|
||||
import { CCPairBasicInfo, DocumentSet, Tag, User } from "@/lib/types";
|
||||
import { cookies } from "next/headers";
|
||||
@ -34,6 +33,8 @@ import {
|
||||
DISABLE_LLM_DOC_RELEVANCE,
|
||||
} from "@/lib/constants";
|
||||
import WrappedSearch from "./WrappedSearch";
|
||||
import { SearchProvider } from "@/components/context/SearchContext";
|
||||
import { ProviderContextProvider } from "@/components/chat_search/ProviderContext";
|
||||
|
||||
export default async function Home() {
|
||||
// Disable caching so we always get the up to date connector / document set / persona info
|
||||
@ -185,10 +186,6 @@ export default async function Home() {
|
||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||
<InstantSSRAutoRefresh />
|
||||
|
||||
{!shouldShowWelcomeModal &&
|
||||
!shouldDisplayNoSourcesModal &&
|
||||
!shouldDisplaySourcesIncompleteModal && <ApiKeyModal user={user} />}
|
||||
|
||||
{shouldDisplayNoSourcesModal && <NoSourcesModal />}
|
||||
|
||||
{shouldDisplaySourcesIncompleteModal && (
|
||||
@ -199,18 +196,27 @@ export default async function Home() {
|
||||
Only used in the EE version of the app. */}
|
||||
<ChatPopup />
|
||||
|
||||
<WrappedSearch
|
||||
disabledAgentic={DISABLE_LLM_DOC_RELEVANCE}
|
||||
initiallyToggled={toggleSidebar}
|
||||
querySessions={querySessions}
|
||||
user={user}
|
||||
ccPairs={ccPairs}
|
||||
documentSets={documentSets}
|
||||
personas={assistants}
|
||||
tags={tags}
|
||||
searchTypeDefault={searchTypeDefault}
|
||||
agenticSearchEnabled={agenticSearchEnabled}
|
||||
/>
|
||||
<SearchProvider
|
||||
value={{
|
||||
querySessions,
|
||||
ccPairs,
|
||||
documentSets,
|
||||
assistants,
|
||||
tags,
|
||||
agenticSearchEnabled,
|
||||
disabledAgentic: DISABLE_LLM_DOC_RELEVANCE,
|
||||
initiallyToggled: toggleSidebar,
|
||||
shouldShowWelcomeModal,
|
||||
shouldDisplayNoSources: shouldDisplayNoSourcesModal,
|
||||
}}
|
||||
>
|
||||
<ProviderContextProvider>
|
||||
<WrappedSearch
|
||||
initiallyToggled={toggleSidebar}
|
||||
searchTypeDefault={searchTypeDefault}
|
||||
/>
|
||||
</ProviderContextProvider>
|
||||
</SearchProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ export function TextFormField({
|
||||
rounded-lg
|
||||
w-full
|
||||
py-2
|
||||
px-3
|
||||
px-3
|
||||
mt-1
|
||||
placeholder:font-description
|
||||
placeholder:text-base
|
||||
|
70
web/src/components/chat_search/ProviderContext.tsx
Normal file
70
web/src/components/chat_search/ProviderContext.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
"use client";
|
||||
import { WellKnownLLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces";
|
||||
import React, { createContext, useContext, useState, useEffect } from "react";
|
||||
import { useUser } from "../user/UserProvider";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { checkLlmProvider } from "../initialSetup/welcome/lib";
|
||||
|
||||
interface ProviderContextType {
|
||||
shouldShowConfigurationNeeded: boolean;
|
||||
providerOptions: WellKnownLLMProviderDescriptor[];
|
||||
refreshProviderInfo: () => Promise<void>; // Add this line
|
||||
}
|
||||
|
||||
const ProviderContext = createContext<ProviderContextType | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
export function ProviderContextProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { user } = useUser();
|
||||
const router = useRouter();
|
||||
|
||||
const [validProviderExists, setValidProviderExists] = useState<boolean>(true);
|
||||
const [providerOptions, setProviderOptions] = useState<
|
||||
WellKnownLLMProviderDescriptor[]
|
||||
>([]);
|
||||
|
||||
const fetchProviderInfo = async () => {
|
||||
const { providers, options, defaultCheckSuccessful } =
|
||||
await checkLlmProvider(user);
|
||||
setValidProviderExists(providers.length > 0 && defaultCheckSuccessful);
|
||||
setProviderOptions(options);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchProviderInfo();
|
||||
}, [router, user]);
|
||||
|
||||
const shouldShowConfigurationNeeded =
|
||||
!validProviderExists && providerOptions.length > 0;
|
||||
|
||||
const refreshProviderInfo = async () => {
|
||||
await fetchProviderInfo();
|
||||
};
|
||||
|
||||
return (
|
||||
<ProviderContext.Provider
|
||||
value={{
|
||||
shouldShowConfigurationNeeded,
|
||||
providerOptions,
|
||||
refreshProviderInfo, // Add this line
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ProviderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useProviderStatus() {
|
||||
const context = useContext(ProviderContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
"useProviderStatus must be used within a ProviderContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
27
web/src/components/chat_search/UnconfiguredProviderText.tsx
Normal file
27
web/src/components/chat_search/UnconfiguredProviderText.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { useProviderStatus } from "./ProviderContext";
|
||||
|
||||
export default function CredentialNotConfigured({
|
||||
showConfigureAPIKey,
|
||||
}: {
|
||||
showConfigureAPIKey: () => void;
|
||||
}) {
|
||||
const { shouldShowConfigurationNeeded } = useProviderStatus();
|
||||
|
||||
if (!shouldShowConfigurationNeeded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p className="text-base text-center w-full text-subtle">
|
||||
Please note that you have not yet configured an LLM provider. You can
|
||||
configure one{" "}
|
||||
<button
|
||||
onClick={showConfigureAPIKey}
|
||||
className="text-link hover:underline cursor-pointer"
|
||||
>
|
||||
here
|
||||
</button>
|
||||
.
|
||||
</p>
|
||||
);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
|
||||
import { Dispatch, SetStateAction, useEffect, useRef } from "react";
|
||||
|
||||
interface UseSidebarVisibilityProps {
|
||||
toggledSidebar: boolean;
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext } from "react";
|
||||
import React, { createContext, useContext, useState } from "react";
|
||||
import { DocumentSet, Tag, User, ValidSources } from "@/lib/types";
|
||||
import { ChatSession } from "@/app/chat/interfaces";
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
@ -18,15 +18,40 @@ interface ChatContextProps {
|
||||
folders: Folder[];
|
||||
openedFolders: Record<string, boolean>;
|
||||
userInputPrompts: InputPrompt[];
|
||||
shouldShowWelcomeModal?: boolean;
|
||||
shouldDisplaySourcesIncompleteModal?: boolean;
|
||||
defaultAssistantId?: number;
|
||||
refreshChatSessions: () => Promise<void>;
|
||||
}
|
||||
|
||||
const ChatContext = createContext<ChatContextProps | undefined>(undefined);
|
||||
|
||||
// We use Omit to exclude 'refreshChatSessions' from the value prop type
|
||||
// because we're defining it within the component
|
||||
export const ChatProvider: React.FC<{
|
||||
value: ChatContextProps;
|
||||
value: Omit<ChatContextProps, "refreshChatSessions">;
|
||||
children: React.ReactNode;
|
||||
}> = ({ value, children }) => {
|
||||
return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
|
||||
const [chatSessions, setChatSessions] = useState(value?.chatSessions || []);
|
||||
|
||||
const refreshChatSessions = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/chat/get-user-chat-sessions");
|
||||
if (!response.ok) throw new Error("Failed to fetch chat sessions");
|
||||
const { sessions } = await response.json();
|
||||
setChatSessions(sessions);
|
||||
} catch (error) {
|
||||
console.error("Error refreshing chat sessions:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ChatContext.Provider
|
||||
value={{ ...value, chatSessions, refreshChatSessions }}
|
||||
>
|
||||
{children}
|
||||
</ChatContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useChatContext = (): ChatContextProps => {
|
||||
|
38
web/src/components/context/SearchContext.tsx
Normal file
38
web/src/components/context/SearchContext.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext } from "react";
|
||||
import { CCPairBasicInfo, DocumentSet, Tag } from "@/lib/types";
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { ChatSession } from "@/app/chat/interfaces";
|
||||
|
||||
interface SearchContextProps {
|
||||
querySessions: ChatSession[];
|
||||
ccPairs: CCPairBasicInfo[];
|
||||
documentSets: DocumentSet[];
|
||||
assistants: Persona[];
|
||||
tags: Tag[];
|
||||
agenticSearchEnabled: boolean;
|
||||
disabledAgentic: boolean;
|
||||
initiallyToggled: boolean;
|
||||
shouldShowWelcomeModal: boolean;
|
||||
shouldDisplayNoSources: boolean;
|
||||
}
|
||||
|
||||
const SearchContext = createContext<SearchContextProps | undefined>(undefined);
|
||||
|
||||
export const SearchProvider: React.FC<{
|
||||
value: SearchContextProps;
|
||||
children: React.ReactNode;
|
||||
}> = ({ value, children }) => {
|
||||
return (
|
||||
<SearchContext.Provider value={value}>{children}</SearchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useSearchContext = (): SearchContextProps => {
|
||||
const context = useContext(SearchContext);
|
||||
if (!context) {
|
||||
throw new Error("useSearchContext must be used within a SearchProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
@ -27,13 +27,11 @@ function UsageTypeSection({
|
||||
title,
|
||||
description,
|
||||
callToAction,
|
||||
icon,
|
||||
onClick,
|
||||
}: {
|
||||
title: string;
|
||||
description: string | JSX.Element;
|
||||
callToAction: string;
|
||||
icon?: React.ElementType;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
@ -243,7 +241,6 @@ export function _WelcomeModal({ user }: { user: User | null }) {
|
||||
this is the option for you!
|
||||
</Text>
|
||||
}
|
||||
icon={FiMessageSquare}
|
||||
callToAction="Get Started"
|
||||
onClick={() => {
|
||||
setSelectedFlow("chat");
|
||||
|
@ -55,6 +55,7 @@ export const ApiKeyForm = ({
|
||||
return (
|
||||
<TabPanel key={provider.name}>
|
||||
<LLMProviderUpdateForm
|
||||
hideAdvanced
|
||||
llmProviderDescriptor={provider}
|
||||
onClose={() => onSuccess()}
|
||||
shouldMarkAsDefault
|
||||
|
@ -1,60 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { ApiKeyForm } from "./ApiKeyForm";
|
||||
import { Modal } from "../Modal";
|
||||
import { WellKnownLLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces";
|
||||
import { checkLlmProvider } from "../initialSetup/welcome/lib";
|
||||
import { User } from "@/lib/types";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useProviderStatus } from "../chat_search/ProviderContext";
|
||||
|
||||
export const ApiKeyModal = ({ user }: { user: User | null }) => {
|
||||
export const ApiKeyModal = ({ hide }: { hide: () => void }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const [forceHidden, setForceHidden] = useState<boolean>(false);
|
||||
const [validProviderExists, setValidProviderExists] = useState<boolean>(true);
|
||||
const [providerOptions, setProviderOptions] = useState<
|
||||
WellKnownLLMProviderDescriptor[]
|
||||
>([]);
|
||||
const {
|
||||
shouldShowConfigurationNeeded,
|
||||
providerOptions,
|
||||
refreshProviderInfo,
|
||||
} = useProviderStatus();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchProviderInfo() {
|
||||
const { providers, options, defaultCheckSuccessful } =
|
||||
await checkLlmProvider(user);
|
||||
setValidProviderExists(providers.length > 0 && defaultCheckSuccessful);
|
||||
setProviderOptions(options);
|
||||
}
|
||||
|
||||
fetchProviderInfo();
|
||||
}, []);
|
||||
|
||||
// don't show if
|
||||
// (1) a valid provider has been setup or
|
||||
// (2) there are no provider options (e.g. user isn't an admin)
|
||||
// (3) user explicitly hides the modal
|
||||
if (validProviderExists || !providerOptions.length || forceHidden) {
|
||||
if (!shouldShowConfigurationNeeded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="LLM Key Setup"
|
||||
className="max-w-4xl"
|
||||
onOutsideClick={() => setForceHidden(true)}
|
||||
title="Set an API Key!"
|
||||
className="max-w-3xl"
|
||||
onOutsideClick={() => hide()}
|
||||
>
|
||||
<div className="max-h-[75vh] overflow-y-auto flex flex-col px-4">
|
||||
<div>
|
||||
<div className="mb-5 text-sm">
|
||||
Please setup an LLM below in order to start using Danswer Search or
|
||||
Danswer Chat. Don't worry, you can always change this later in
|
||||
the Admin Panel.
|
||||
Please provide an API Key below in order to start using
|
||||
Danswer – you can always change this later.
|
||||
<br />
|
||||
<br />
|
||||
Or if you'd rather look around first,{" "}
|
||||
<strong
|
||||
onClick={() => setForceHidden(true)}
|
||||
className="text-link cursor-pointer"
|
||||
>
|
||||
If you'd rather look around first, you can
|
||||
<strong onClick={() => hide()} className="text-link cursor-pointer">
|
||||
{" "}
|
||||
skip this step
|
||||
</strong>
|
||||
.
|
||||
@ -63,7 +41,8 @@ export const ApiKeyModal = ({ user }: { user: User | null }) => {
|
||||
<ApiKeyForm
|
||||
onSuccess={() => {
|
||||
router.refresh();
|
||||
setForceHidden(true);
|
||||
refreshProviderInfo();
|
||||
hide();
|
||||
}}
|
||||
providerOptions={providerOptions}
|
||||
/>
|
||||
|
@ -37,6 +37,10 @@ import { FeedbackModal } from "@/app/chat/modal/FeedbackModal";
|
||||
import { deleteChatSession, handleChatFeedback } from "@/app/chat/lib";
|
||||
import SearchAnswer from "./SearchAnswer";
|
||||
import { DeleteEntityModal } from "../modals/DeleteEntityModal";
|
||||
import { ApiKeyModal } from "../llm/ApiKeyModal";
|
||||
import { useSearchContext } from "../context/SearchContext";
|
||||
import { useUser } from "../user/UserProvider";
|
||||
import UnconfiguredProviderText from "../chat_search/UnconfiguredProviderText";
|
||||
|
||||
export type searchState =
|
||||
| "input"
|
||||
@ -58,33 +62,28 @@ const VALID_QUESTION_RESPONSE_DEFAULT: ValidQuestionResponse = {
|
||||
};
|
||||
|
||||
interface SearchSectionProps {
|
||||
disabledAgentic: boolean;
|
||||
ccPairs: CCPairBasicInfo[];
|
||||
documentSets: DocumentSet[];
|
||||
personas: Persona[];
|
||||
tags: Tag[];
|
||||
toggle: () => void;
|
||||
querySessions: ChatSession[];
|
||||
defaultSearchType: SearchType;
|
||||
user: User | null;
|
||||
toggledSidebar: boolean;
|
||||
agenticSearchEnabled: boolean;
|
||||
}
|
||||
|
||||
export const SearchSection = ({
|
||||
ccPairs,
|
||||
toggle,
|
||||
disabledAgentic,
|
||||
documentSets,
|
||||
agenticSearchEnabled,
|
||||
personas,
|
||||
user,
|
||||
tags,
|
||||
querySessions,
|
||||
toggledSidebar,
|
||||
defaultSearchType,
|
||||
}: SearchSectionProps) => {
|
||||
// Search Bar
|
||||
const {
|
||||
querySessions,
|
||||
ccPairs,
|
||||
documentSets,
|
||||
assistants,
|
||||
tags,
|
||||
shouldShowWelcomeModal,
|
||||
agenticSearchEnabled,
|
||||
disabledAgentic,
|
||||
shouldDisplayNoSources,
|
||||
} = useSearchContext();
|
||||
|
||||
const [query, setQuery] = useState<string>("");
|
||||
const [comments, setComments] = useState<any>(null);
|
||||
const [contentEnriched, setContentEnriched] = useState(false);
|
||||
@ -100,6 +99,8 @@ export const SearchSection = ({
|
||||
messageId: null,
|
||||
});
|
||||
|
||||
const [showApiKeyModal, setShowApiKeyModal] = useState(true);
|
||||
|
||||
const [agentic, setAgentic] = useState(agenticSearchEnabled);
|
||||
|
||||
const toggleAgentic = () => {
|
||||
@ -147,7 +148,7 @@ export const SearchSection = ({
|
||||
useState<SearchType>(defaultSearchType);
|
||||
|
||||
const [selectedPersona, setSelectedPersona] = useState<number>(
|
||||
personas[0]?.id || 0
|
||||
assistants[0]?.id || 0
|
||||
);
|
||||
|
||||
// Used for search state display
|
||||
@ -158,8 +159,8 @@ export const SearchSection = ({
|
||||
const availableSources = ccPairs.map((ccPair) => ccPair.source);
|
||||
const [finalAvailableSources, finalAvailableDocumentSets] =
|
||||
computeAvailableFilters({
|
||||
selectedPersona: personas.find(
|
||||
(persona) => persona.id === selectedPersona
|
||||
selectedPersona: assistants.find(
|
||||
(assistant) => assistant.id === selectedPersona
|
||||
),
|
||||
availableSources: availableSources,
|
||||
availableDocumentSets: documentSets,
|
||||
@ -362,6 +363,7 @@ export const SearchSection = ({
|
||||
setSearchState("input");
|
||||
}
|
||||
};
|
||||
const { user } = useUser();
|
||||
const [searchAnswerExpanded, setSearchAnswerExpanded] = useState(false);
|
||||
|
||||
const resetInput = (finalized?: boolean) => {
|
||||
@ -403,8 +405,8 @@ export const SearchSection = ({
|
||||
documentSets: filterManager.selectedDocumentSets,
|
||||
timeRange: filterManager.timeRange,
|
||||
tags: filterManager.selectedTags,
|
||||
persona: personas.find(
|
||||
(persona) => persona.id === selectedPersona
|
||||
persona: assistants.find(
|
||||
(assistant) => assistant.id === selectedPersona
|
||||
) as Persona,
|
||||
updateCurrentAnswer: cancellable({
|
||||
cancellationToken: lastSearchCancellationToken.current,
|
||||
@ -595,6 +597,12 @@ export const SearchSection = ({
|
||||
<div className="flex relative pr-[8px] h-full text-default">
|
||||
{popup}
|
||||
|
||||
{!shouldDisplayNoSources &&
|
||||
showApiKeyModal &&
|
||||
!shouldShowWelcomeModal && (
|
||||
<ApiKeyModal hide={() => setShowApiKeyModal(false)} />
|
||||
)}
|
||||
|
||||
{deletingChatSession && (
|
||||
<DeleteEntityModal
|
||||
entityType="search"
|
||||
@ -747,6 +755,11 @@ export const SearchSection = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UnconfiguredProviderText
|
||||
showConfigureAPIKey={() => setShowApiKeyModal(true)}
|
||||
/>
|
||||
|
||||
<FullSearchBar
|
||||
toggleAgentic={
|
||||
disabledAgentic ? undefined : toggleAgentic
|
||||
|
@ -44,7 +44,6 @@ interface FetchChatDataResult {
|
||||
toggleSidebar: boolean;
|
||||
finalDocumentSidebarInitialWidth?: number;
|
||||
shouldShowWelcomeModal: boolean;
|
||||
shouldDisplaySourcesIncompleteModal: boolean;
|
||||
userInputPrompts: InputPrompt[];
|
||||
}
|
||||
|
||||
@ -242,7 +241,6 @@ export async function fetchChatData(searchParams: {
|
||||
finalDocumentSidebarInitialWidth,
|
||||
toggleSidebar,
|
||||
shouldShowWelcomeModal,
|
||||
shouldDisplaySourcesIncompleteModal,
|
||||
userInputPrompts,
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user