mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-03-26 17:51:54 +01:00
Fix assistant swap
This commit is contained in:
parent
3854ca11af
commit
70e3b8a4ef
@ -50,7 +50,7 @@ export default async function GalleryPage({
|
||||
chatSessions,
|
||||
availableSources,
|
||||
availableDocumentSets: documentSets,
|
||||
availablePersonas: assistants,
|
||||
availableAssistants: assistants,
|
||||
availableTags: tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
|
@ -52,7 +52,7 @@ export default async function GalleryPage({
|
||||
chatSessions,
|
||||
availableSources,
|
||||
availableDocumentSets: documentSets,
|
||||
availablePersonas: assistants,
|
||||
availableAssistants: assistants,
|
||||
availableTags: tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
|
@ -63,7 +63,6 @@ import { SettingsContext } from "@/components/settings/SettingsProvider";
|
||||
import Dropzone from "react-dropzone";
|
||||
import { checkLLMSupportsImageInput, getFinalLLM } from "@/lib/llm/utils";
|
||||
import { ChatInputBar } from "./input/ChatInputBar";
|
||||
import { ConfigurationModal } from "./modal/configuration/ConfigurationModal";
|
||||
import { useChatContext } from "@/components/context/ChatContext";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
|
||||
@ -82,38 +81,29 @@ const SYSTEM_MESSAGE_ID = -3;
|
||||
export function ChatPage({
|
||||
toggle,
|
||||
documentSidebarInitialWidth,
|
||||
defaultSelectedPersonaId,
|
||||
defaultSelectedAssistantId,
|
||||
toggledSidebar,
|
||||
}: {
|
||||
toggle: () => void;
|
||||
documentSidebarInitialWidth?: number;
|
||||
defaultSelectedPersonaId?: number;
|
||||
defaultSelectedAssistantId?: number;
|
||||
toggledSidebar: boolean;
|
||||
}) {
|
||||
const [configModalActiveTab, setConfigModalActiveTab] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
let {
|
||||
user,
|
||||
chatSessions,
|
||||
availableSources,
|
||||
availableDocumentSets,
|
||||
availablePersonas,
|
||||
availableAssistants,
|
||||
llmProviders,
|
||||
folders,
|
||||
openedFolders,
|
||||
} = useChatContext();
|
||||
|
||||
const filteredAssistants = orderAssistantsForUser(availablePersonas, user);
|
||||
|
||||
const [selectedAssistant, setSelectedAssistant] = useState<Persona | null>(
|
||||
null
|
||||
);
|
||||
const [alternativeGeneratingAssistant, setAlternativeGeneratingAssistant] =
|
||||
useState<Persona | null>(null);
|
||||
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
// chat session
|
||||
const existingChatIdRaw = searchParams.get("chatId");
|
||||
const existingChatSessionId = existingChatIdRaw
|
||||
? parseInt(existingChatIdRaw)
|
||||
@ -123,9 +113,51 @@ export function ChatPage({
|
||||
);
|
||||
const chatSessionIdRef = useRef<number | null>(existingChatSessionId);
|
||||
|
||||
// LLM
|
||||
const llmOverrideManager = useLlmOverride(selectedChatSession);
|
||||
|
||||
const existingChatSessionPersonaId = selectedChatSession?.persona_id;
|
||||
// Assistants
|
||||
const filteredAssistants = orderAssistantsForUser(availableAssistants, user);
|
||||
|
||||
const existingChatSessionAssistantId = selectedChatSession?.persona_id;
|
||||
const [selectedAssistant, setSelectedAssistant] = useState<
|
||||
Persona | undefined
|
||||
>(
|
||||
// NOTE: look through available assistants here, so that even if the user
|
||||
// has hidden this assistant it still shows the correct assistant when
|
||||
// going back to an old chat session
|
||||
existingChatSessionAssistantId !== undefined
|
||||
? availableAssistants.find(
|
||||
(assistant) => assistant.id === existingChatSessionAssistantId
|
||||
)
|
||||
: defaultSelectedAssistantId !== undefined
|
||||
? availableAssistants.find(
|
||||
(assistant) => assistant.id === defaultSelectedAssistantId
|
||||
)
|
||||
: undefined
|
||||
);
|
||||
const setSelectedAssistantFromId = (assistantId: number) => {
|
||||
// NOTE: also intentionally look through available assistants here, so that
|
||||
// even if the user has hidden an assistant they can still go back to it
|
||||
// for old chats
|
||||
setSelectedAssistant(
|
||||
availableAssistants.find((assistant) => assistant.id === assistantId)
|
||||
);
|
||||
};
|
||||
const liveAssistant =
|
||||
selectedAssistant || filteredAssistants[0] || availableAssistants[0];
|
||||
|
||||
// this is for "@"ing assistants
|
||||
const [alternativeAssistant, setAlternativeAssistant] =
|
||||
useState<Persona | null>(null);
|
||||
|
||||
// this is used to track which assistant is being used to generate the current message
|
||||
// for example, this would come into play when:
|
||||
// 1. default assistant is `Danswer`
|
||||
// 2. we "@"ed the `GPT` assistant and sent a message
|
||||
// 3. while the `GPT` assistant message is generating, we "@" the `Paraphrase` assistant
|
||||
const [alternativeGeneratingAssistant, setAlternativeGeneratingAssistant] =
|
||||
useState<Persona | null>(null);
|
||||
|
||||
// used to track whether or not the initial "submit on load" has been performed
|
||||
// this only applies if `?submit-on-load=true` or `?submit-on-load=1` is in the URL
|
||||
@ -182,14 +214,10 @@ export function ChatPage({
|
||||
async function initialSessionFetch() {
|
||||
if (existingChatSessionId === null) {
|
||||
setIsFetchingChatMessages(false);
|
||||
if (defaultSelectedPersonaId !== undefined) {
|
||||
setSelectedPersona(
|
||||
filteredAssistants.find(
|
||||
(persona) => persona.id === defaultSelectedPersonaId
|
||||
)
|
||||
);
|
||||
if (defaultSelectedAssistantId !== undefined) {
|
||||
setSelectedAssistantFromId(defaultSelectedAssistantId);
|
||||
} else {
|
||||
setSelectedPersona(undefined);
|
||||
setSelectedAssistant(undefined);
|
||||
}
|
||||
setCompleteMessageDetail({
|
||||
sessionId: null,
|
||||
@ -214,12 +242,7 @@ export function ChatPage({
|
||||
);
|
||||
|
||||
const chatSession = (await response.json()) as BackendChatSession;
|
||||
|
||||
setSelectedPersona(
|
||||
filteredAssistants.find(
|
||||
(persona) => persona.id === chatSession.persona_id
|
||||
)
|
||||
);
|
||||
setSelectedAssistantFromId(chatSession.persona_id);
|
||||
|
||||
const newMessageMap = processRawChatHistory(chatSession.messages);
|
||||
const newMessageHistory = buildLatestMessageChain(newMessageMap);
|
||||
@ -373,32 +396,18 @@ export function ChatPage({
|
||||
)
|
||||
: { aiMessage: null };
|
||||
|
||||
const [selectedPersona, setSelectedPersona] = useState<Persona | undefined>(
|
||||
existingChatSessionPersonaId !== undefined
|
||||
? filteredAssistants.find(
|
||||
(persona) => persona.id === existingChatSessionPersonaId
|
||||
)
|
||||
: defaultSelectedPersonaId !== undefined
|
||||
? filteredAssistants.find(
|
||||
(persona) => persona.id === defaultSelectedPersonaId
|
||||
)
|
||||
: undefined
|
||||
);
|
||||
const livePersona =
|
||||
selectedPersona || filteredAssistants[0] || availablePersonas[0];
|
||||
|
||||
const [chatSessionSharedStatus, setChatSessionSharedStatus] =
|
||||
useState<ChatSessionSharedStatus>(ChatSessionSharedStatus.Private);
|
||||
|
||||
useEffect(() => {
|
||||
if (messageHistory.length === 0 && chatSessionIdRef.current === null) {
|
||||
setSelectedPersona(
|
||||
setSelectedAssistant(
|
||||
filteredAssistants.find(
|
||||
(persona) => persona.id === defaultSelectedPersonaId
|
||||
(persona) => persona.id === defaultSelectedAssistantId
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [defaultSelectedPersonaId]);
|
||||
}, [defaultSelectedAssistantId]);
|
||||
|
||||
const [
|
||||
selectedDocuments,
|
||||
@ -414,7 +423,7 @@ export function ChatPage({
|
||||
useEffect(() => {
|
||||
async function fetchMaxTokens() {
|
||||
const response = await fetch(
|
||||
`/api/chat/max-selected-document-tokens?persona_id=${livePersona.id}`
|
||||
`/api/chat/max-selected-document-tokens?persona_id=${liveAssistant.id}`
|
||||
);
|
||||
if (response.ok) {
|
||||
const maxTokens = (await response.json()).max_tokens as number;
|
||||
@ -423,12 +432,12 @@ export function ChatPage({
|
||||
}
|
||||
|
||||
fetchMaxTokens();
|
||||
}, [livePersona]);
|
||||
}, [liveAssistant]);
|
||||
|
||||
const filterManager = useFilters();
|
||||
const [finalAvailableSources, finalAvailableDocumentSets] =
|
||||
computeAvailableFilters({
|
||||
selectedPersona,
|
||||
selectedPersona: selectedAssistant,
|
||||
availableSources,
|
||||
availableDocumentSets,
|
||||
});
|
||||
@ -624,16 +633,16 @@ export function ChatPage({
|
||||
queryOverride,
|
||||
forceSearch,
|
||||
isSeededChat,
|
||||
alternativeAssistant = null,
|
||||
alternativeAssistantOverride = null,
|
||||
}: {
|
||||
messageIdToResend?: number;
|
||||
messageOverride?: string;
|
||||
queryOverride?: string;
|
||||
forceSearch?: boolean;
|
||||
isSeededChat?: boolean;
|
||||
alternativeAssistant?: Persona | null;
|
||||
alternativeAssistantOverride?: Persona | null;
|
||||
} = {}) => {
|
||||
setAlternativeGeneratingAssistant(alternativeAssistant);
|
||||
setAlternativeGeneratingAssistant(alternativeAssistantOverride);
|
||||
|
||||
clientScrollToBottom();
|
||||
let currChatSessionId: number;
|
||||
@ -643,7 +652,7 @@ export function ChatPage({
|
||||
|
||||
if (isNewSession) {
|
||||
currChatSessionId = await createChatSession(
|
||||
livePersona?.id || 0,
|
||||
liveAssistant?.id || 0,
|
||||
searchParamBasedChatSessionName
|
||||
);
|
||||
} else {
|
||||
@ -721,9 +730,9 @@ export function ChatPage({
|
||||
parentMessage = frozenMessageMap.get(SYSTEM_MESSAGE_ID) || null;
|
||||
}
|
||||
|
||||
const currentAssistantId = alternativeAssistant
|
||||
? alternativeAssistant.id
|
||||
: selectedAssistant?.id;
|
||||
const currentAssistantId = alternativeAssistantOverride
|
||||
? alternativeAssistantOverride.id
|
||||
: alternativeAssistant?.id || liveAssistant.id;
|
||||
|
||||
resetInputBar();
|
||||
|
||||
@ -751,7 +760,7 @@ export function ChatPage({
|
||||
fileDescriptors: currentMessageFiles,
|
||||
parentMessageId: lastSuccessfulMessageId,
|
||||
chatSessionId: currChatSessionId,
|
||||
promptId: livePersona?.prompts[0]?.id || 0,
|
||||
promptId: liveAssistant?.prompts[0]?.id || 0,
|
||||
filters: buildFilters(
|
||||
filterManager.selectedSources,
|
||||
filterManager.selectedDocumentSets,
|
||||
@ -868,7 +877,7 @@ export function ChatPage({
|
||||
files: finalMessage?.files || aiMessageImages || [],
|
||||
toolCalls: finalMessage?.tool_calls || toolCalls,
|
||||
parentMessageId: newUserMessageId,
|
||||
alternateAssistantID: selectedAssistant?.id,
|
||||
alternateAssistantID: alternativeAssistant?.id,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@ -964,19 +973,23 @@ export function ChatPage({
|
||||
}
|
||||
};
|
||||
|
||||
const onPersonaChange = (persona: Persona | null) => {
|
||||
if (persona && persona.id !== livePersona.id) {
|
||||
const onAssistantChange = (assistant: Persona | null) => {
|
||||
if (assistant && assistant.id !== liveAssistant.id) {
|
||||
// remove uploaded files
|
||||
setCurrentMessageFiles([]);
|
||||
setSelectedPersona(persona);
|
||||
setSelectedAssistant(assistant);
|
||||
textAreaRef.current?.focus();
|
||||
router.push(buildChatUrl(searchParams, null, persona.id));
|
||||
router.push(buildChatUrl(searchParams, null, assistant.id));
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageUpload = (acceptedFiles: File[]) => {
|
||||
const llmAcceptsImages = checkLLMSupportsImageInput(
|
||||
...getFinalLLM(llmProviders, livePersona, llmOverrideManager.llmOverride)
|
||||
...getFinalLLM(
|
||||
llmProviders,
|
||||
liveAssistant,
|
||||
llmOverrideManager.llmOverride
|
||||
)
|
||||
);
|
||||
const imageFiles = acceptedFiles.filter((file) =>
|
||||
file.type.startsWith("image/")
|
||||
@ -1058,23 +1071,23 @@ export function ChatPage({
|
||||
useEffect(() => {
|
||||
const includes = checkAnyAssistantHasSearch(
|
||||
messageHistory,
|
||||
availablePersonas,
|
||||
livePersona
|
||||
availableAssistants,
|
||||
liveAssistant
|
||||
);
|
||||
setRetrievalEnabled(includes);
|
||||
}, [messageHistory, availablePersonas, livePersona]);
|
||||
}, [messageHistory, availableAssistants, liveAssistant]);
|
||||
|
||||
const [retrievalEnabled, setRetrievalEnabled] = useState(() => {
|
||||
return checkAnyAssistantHasSearch(
|
||||
messageHistory,
|
||||
availablePersonas,
|
||||
livePersona
|
||||
availableAssistants,
|
||||
liveAssistant
|
||||
);
|
||||
});
|
||||
|
||||
const innerSidebarElementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const currentPersona = selectedAssistant || livePersona;
|
||||
const currentPersona = alternativeAssistant || liveAssistant;
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
@ -1176,21 +1189,8 @@ export function ChatPage({
|
||||
/>
|
||||
)}
|
||||
|
||||
<ConfigurationModal
|
||||
chatSessionId={chatSessionIdRef.current!}
|
||||
activeTab={configModalActiveTab}
|
||||
setActiveTab={setConfigModalActiveTab}
|
||||
onClose={() => setConfigModalActiveTab(null)}
|
||||
filterManager={filterManager}
|
||||
availableAssistants={filteredAssistants}
|
||||
selectedAssistant={livePersona}
|
||||
setSelectedAssistant={onPersonaChange}
|
||||
llmProviders={llmProviders}
|
||||
llmOverrideManager={llmOverrideManager}
|
||||
/>
|
||||
|
||||
<div className="flex h-[calc(100dvh)] flex-col w-full">
|
||||
{livePersona && (
|
||||
{liveAssistant && (
|
||||
<FunctionalHeader
|
||||
page="chat"
|
||||
setSharingModalVisible={
|
||||
@ -1239,7 +1239,7 @@ export function ChatPage({
|
||||
!isStreaming && (
|
||||
<ChatIntro
|
||||
availableSources={finalAvailableSources}
|
||||
selectedPersona={livePersona}
|
||||
selectedPersona={liveAssistant}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
@ -1319,7 +1319,7 @@ export function ChatPage({
|
||||
|
||||
const currentAlternativeAssistant =
|
||||
message.alternateAssistantID != null
|
||||
? availablePersonas.find(
|
||||
? availableAssistants.find(
|
||||
(persona) =>
|
||||
persona.id ==
|
||||
message.alternateAssistantID
|
||||
@ -1342,7 +1342,7 @@ export function ChatPage({
|
||||
toggleDocumentSelectionAspects
|
||||
}
|
||||
docs={message.documents}
|
||||
currentPersona={livePersona}
|
||||
currentPersona={liveAssistant}
|
||||
alternativeAssistant={
|
||||
currentAlternativeAssistant
|
||||
}
|
||||
@ -1352,7 +1352,7 @@ export function ChatPage({
|
||||
query={
|
||||
messageHistory[i]?.query || undefined
|
||||
}
|
||||
personaName={livePersona.name}
|
||||
personaName={liveAssistant.name}
|
||||
citedDocuments={getCitedDocumentsFromMessage(
|
||||
message
|
||||
)}
|
||||
@ -1404,7 +1404,7 @@ export function ChatPage({
|
||||
messageIdToResend:
|
||||
previousMessage.messageId,
|
||||
queryOverride: newQuery,
|
||||
alternativeAssistant:
|
||||
alternativeAssistantOverride:
|
||||
currentAlternativeAssistant,
|
||||
});
|
||||
}
|
||||
@ -1435,7 +1435,7 @@ export function ChatPage({
|
||||
messageIdToResend:
|
||||
previousMessage.messageId,
|
||||
forceSearch: true,
|
||||
alternativeAssistant:
|
||||
alternativeAssistantOverride:
|
||||
currentAlternativeAssistant,
|
||||
});
|
||||
} else {
|
||||
@ -1460,9 +1460,9 @@ export function ChatPage({
|
||||
return (
|
||||
<div key={messageReactComponentKey}>
|
||||
<AIMessage
|
||||
currentPersona={livePersona}
|
||||
currentPersona={liveAssistant}
|
||||
messageId={message.messageId}
|
||||
personaName={livePersona.name}
|
||||
personaName={liveAssistant.name}
|
||||
content={
|
||||
<p className="text-red-700 text-sm my-auto">
|
||||
{message.message}
|
||||
@ -1481,13 +1481,13 @@ export function ChatPage({
|
||||
key={`${messageHistory.length}-${chatSessionIdRef.current}`}
|
||||
>
|
||||
<AIMessage
|
||||
currentPersona={livePersona}
|
||||
currentPersona={liveAssistant}
|
||||
alternativeAssistant={
|
||||
alternativeGeneratingAssistant ??
|
||||
selectedAssistant
|
||||
alternativeAssistant
|
||||
}
|
||||
messageId={null}
|
||||
personaName={livePersona.name}
|
||||
personaName={liveAssistant.name}
|
||||
content={
|
||||
<div className="text-sm my-auto">
|
||||
<ThreeDots
|
||||
@ -1513,7 +1513,7 @@ export function ChatPage({
|
||||
{currentPersona &&
|
||||
currentPersona.starter_messages &&
|
||||
currentPersona.starter_messages.length > 0 &&
|
||||
selectedPersona &&
|
||||
selectedAssistant &&
|
||||
messageHistory.length === 0 &&
|
||||
!isFetchingChatMessages && (
|
||||
<div
|
||||
@ -1570,32 +1570,25 @@ export function ChatPage({
|
||||
<ChatInputBar
|
||||
showDocs={() => setDocumentSelection(true)}
|
||||
selectedDocuments={selectedDocuments}
|
||||
setSelectedAssistant={onPersonaChange}
|
||||
onSetSelectedAssistant={(
|
||||
alternativeAssistant: Persona | null
|
||||
) => {
|
||||
setSelectedAssistant(alternativeAssistant);
|
||||
}}
|
||||
alternativeAssistant={selectedAssistant}
|
||||
personas={filteredAssistants}
|
||||
// assistant stuff
|
||||
assistantOptions={filteredAssistants}
|
||||
selectedAssistant={liveAssistant}
|
||||
setSelectedAssistant={onAssistantChange}
|
||||
setAlternativeAssistant={setAlternativeAssistant}
|
||||
alternativeAssistant={alternativeAssistant}
|
||||
// end assistant stuff
|
||||
message={message}
|
||||
setMessage={setMessage}
|
||||
onSubmit={onSubmit}
|
||||
isStreaming={isStreaming}
|
||||
setIsCancelled={setIsCancelled}
|
||||
retrievalDisabled={
|
||||
!personaIncludesRetrieval(currentPersona)
|
||||
}
|
||||
filterManager={filterManager}
|
||||
llmOverrideManager={llmOverrideManager}
|
||||
selectedAssistant={livePersona}
|
||||
files={currentMessageFiles}
|
||||
setFiles={setCurrentMessageFiles}
|
||||
handleFileUpload={handleImageUpload}
|
||||
setConfigModalActiveTab={setConfigModalActiveTab}
|
||||
textAreaRef={textAreaRef}
|
||||
chatSessionId={chatSessionIdRef.current!}
|
||||
availableAssistants={availablePersonas}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,10 +5,10 @@ import { ChatPage } from "./ChatPage";
|
||||
import FunctionalWrapper from "./shared_chat_search/FunctionalWrapper";
|
||||
|
||||
export default function WrappedChat({
|
||||
defaultPersonaId,
|
||||
defaultAssistantId,
|
||||
initiallyToggled,
|
||||
}: {
|
||||
defaultPersonaId?: number;
|
||||
defaultAssistantId?: number;
|
||||
initiallyToggled: boolean;
|
||||
}) {
|
||||
return (
|
||||
@ -17,7 +17,7 @@ export default function WrappedChat({
|
||||
content={(toggledSidebar, toggle) => (
|
||||
<ChatPage
|
||||
toggle={toggle}
|
||||
defaultSelectedPersonaId={defaultPersonaId}
|
||||
defaultSelectedAssistantId={defaultAssistantId}
|
||||
toggledSidebar={toggledSidebar}
|
||||
/>
|
||||
)}
|
||||
|
@ -21,7 +21,6 @@ import { IconType } from "react-icons";
|
||||
import Popup from "../../../components/popup/Popup";
|
||||
import { LlmTab } from "../modal/configuration/LlmTab";
|
||||
import { AssistantsTab } from "../modal/configuration/AssistantsTab";
|
||||
import ChatInputAssistant from "./ChatInputAssistant";
|
||||
import { DanswerDocument } from "@/lib/search/interfaces";
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import { Tooltip } from "@/components/tooltip/Tooltip";
|
||||
@ -29,7 +28,6 @@ import { Hoverable } from "@/components/Hoverable";
|
||||
const MAX_INPUT_HEIGHT = 200;
|
||||
|
||||
export function ChatInputBar({
|
||||
personas,
|
||||
showDocs,
|
||||
selectedDocuments,
|
||||
message,
|
||||
@ -37,34 +35,32 @@ export function ChatInputBar({
|
||||
onSubmit,
|
||||
isStreaming,
|
||||
setIsCancelled,
|
||||
retrievalDisabled,
|
||||
filterManager,
|
||||
llmOverrideManager,
|
||||
onSetSelectedAssistant,
|
||||
selectedAssistant,
|
||||
files,
|
||||
|
||||
// assistants
|
||||
selectedAssistant,
|
||||
assistantOptions,
|
||||
setSelectedAssistant,
|
||||
setAlternativeAssistant,
|
||||
|
||||
files,
|
||||
setFiles,
|
||||
handleFileUpload,
|
||||
setConfigModalActiveTab,
|
||||
textAreaRef,
|
||||
alternativeAssistant,
|
||||
chatSessionId,
|
||||
availableAssistants,
|
||||
}: {
|
||||
showDocs: () => void;
|
||||
selectedDocuments: DanswerDocument[];
|
||||
availableAssistants: Persona[];
|
||||
onSetSelectedAssistant: (alternativeAssistant: Persona | null) => void;
|
||||
assistantOptions: Persona[];
|
||||
setAlternativeAssistant: (alternativeAssistant: Persona | null) => void;
|
||||
setSelectedAssistant: (assistant: Persona) => void;
|
||||
personas: Persona[];
|
||||
message: string;
|
||||
setMessage: (message: string) => void;
|
||||
onSubmit: () => void;
|
||||
isStreaming: boolean;
|
||||
setIsCancelled: (value: boolean) => void;
|
||||
retrievalDisabled: boolean;
|
||||
filterManager: FilterManager;
|
||||
llmOverrideManager: LlmOverrideManager;
|
||||
selectedAssistant: Persona;
|
||||
@ -72,7 +68,6 @@ export function ChatInputBar({
|
||||
files: FileDescriptor[];
|
||||
setFiles: (files: FileDescriptor[]) => void;
|
||||
handleFileUpload: (files: File[]) => void;
|
||||
setConfigModalActiveTab: (tab: string) => void;
|
||||
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
||||
chatSessionId?: number;
|
||||
}) {
|
||||
@ -136,8 +131,10 @@ export function ChatInputBar({
|
||||
};
|
||||
|
||||
// Update selected persona
|
||||
const updateCurrentPersona = (persona: Persona) => {
|
||||
onSetSelectedAssistant(persona.id == selectedAssistant.id ? null : persona);
|
||||
const updatedTaggedAssistant = (assistant: Persona) => {
|
||||
setAlternativeAssistant(
|
||||
assistant.id == selectedAssistant.id ? null : assistant
|
||||
);
|
||||
hideSuggestions();
|
||||
setMessage("");
|
||||
};
|
||||
@ -160,8 +157,8 @@ export function ChatInputBar({
|
||||
}
|
||||
};
|
||||
|
||||
const filteredPersonas = personas.filter((persona) =>
|
||||
persona.name.toLowerCase().startsWith(
|
||||
const assistantTagOptions = assistantOptions.filter((assistant) =>
|
||||
assistant.name.toLowerCase().startsWith(
|
||||
message
|
||||
.slice(message.lastIndexOf("@") + 1)
|
||||
.split(/\s/)[0]
|
||||
@ -174,18 +171,18 @@ export function ChatInputBar({
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (
|
||||
showSuggestions &&
|
||||
filteredPersonas.length > 0 &&
|
||||
assistantTagOptions.length > 0 &&
|
||||
(e.key === "Tab" || e.key == "Enter")
|
||||
) {
|
||||
e.preventDefault();
|
||||
if (assistantIconIndex == filteredPersonas.length) {
|
||||
if (assistantIconIndex == assistantTagOptions.length) {
|
||||
window.open("/assistants/new", "_blank");
|
||||
hideSuggestions();
|
||||
setMessage("");
|
||||
} else {
|
||||
const option =
|
||||
filteredPersonas[assistantIconIndex >= 0 ? assistantIconIndex : 0];
|
||||
updateCurrentPersona(option);
|
||||
assistantTagOptions[assistantIconIndex >= 0 ? assistantIconIndex : 0];
|
||||
updatedTaggedAssistant(option);
|
||||
}
|
||||
}
|
||||
if (!showSuggestions) {
|
||||
@ -195,7 +192,7 @@ export function ChatInputBar({
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
setAssistantIconIndex((assistantIconIndex) =>
|
||||
Math.min(assistantIconIndex + 1, filteredPersonas.length)
|
||||
Math.min(assistantIconIndex + 1, assistantTagOptions.length)
|
||||
);
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
@ -219,35 +216,36 @@ export function ChatInputBar({
|
||||
mx-auto
|
||||
"
|
||||
>
|
||||
{showSuggestions && filteredPersonas.length > 0 && (
|
||||
{showSuggestions && assistantTagOptions.length > 0 && (
|
||||
<div
|
||||
ref={suggestionsRef}
|
||||
className="text-sm absolute inset-x-0 top-0 w-full transform -translate-y-full"
|
||||
>
|
||||
<div className="rounded-lg py-1.5 bg-background border border-border-medium shadow-lg mx-2 px-1.5 mt-2 rounded z-10">
|
||||
{filteredPersonas.map((currentPersona, index) => (
|
||||
{assistantTagOptions.map((currentAssistant, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className={`px-2 ${
|
||||
assistantIconIndex == index && "bg-hover-lightish"
|
||||
} rounded rounded-lg content-start flex gap-x-1 py-2 w-full hover:bg-hover-lightish cursor-pointer`}
|
||||
onClick={() => {
|
||||
updateCurrentPersona(currentPersona);
|
||||
updatedTaggedAssistant(currentAssistant);
|
||||
}}
|
||||
>
|
||||
<p className="font-bold">{currentPersona.name}</p>
|
||||
<p className="font-bold">{currentAssistant.name}</p>
|
||||
<p className="line-clamp-1">
|
||||
{currentPersona.id == selectedAssistant.id &&
|
||||
{currentAssistant.id == selectedAssistant.id &&
|
||||
"(default) "}
|
||||
{currentPersona.description}
|
||||
{currentAssistant.description}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
<a
|
||||
key={filteredPersonas.length}
|
||||
key={assistantTagOptions.length}
|
||||
target="_blank"
|
||||
className={`${
|
||||
assistantIconIndex == filteredPersonas.length && "bg-hover"
|
||||
assistantIconIndex == assistantTagOptions.length &&
|
||||
"bg-hover"
|
||||
} rounded rounded-lg px-3 flex gap-x-1 py-2 w-full items-center hover:bg-hover-lightish cursor-pointer"`}
|
||||
href="/assistants/new"
|
||||
>
|
||||
@ -301,7 +299,7 @@ export function ChatInputBar({
|
||||
|
||||
<Hoverable
|
||||
icon={FiX}
|
||||
onClick={() => onSetSelectedAssistant(null)}
|
||||
onClick={() => setAlternativeAssistant(null)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -409,7 +407,7 @@ export function ChatInputBar({
|
||||
removePadding
|
||||
content={(close) => (
|
||||
<AssistantsTab
|
||||
availableAssistants={availableAssistants}
|
||||
availableAssistants={assistantOptions}
|
||||
llmProviders={llmProviders}
|
||||
selectedAssistant={selectedAssistant}
|
||||
onSelect={(assistant) => {
|
||||
|
@ -505,7 +505,7 @@ export function removeMessage(
|
||||
|
||||
export function checkAnyAssistantHasSearch(
|
||||
messageHistory: Message[],
|
||||
availablePersonas: Persona[],
|
||||
availableAssistants: Persona[],
|
||||
livePersona: Persona
|
||||
): boolean {
|
||||
const response =
|
||||
@ -516,8 +516,8 @@ export function checkAnyAssistantHasSearch(
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const alternateAssistant = availablePersonas.find(
|
||||
(persona) => persona.id === message.alternateAssistantID
|
||||
const alternateAssistant = availableAssistants.find(
|
||||
(assistant) => assistant.id === message.alternateAssistantID
|
||||
);
|
||||
return alternateAssistant
|
||||
? personaIncludesRetrieval(alternateAssistant)
|
||||
|
@ -1,180 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { Modal } from "../../../../components/Modal";
|
||||
import { FilterManager, LlmOverrideManager } from "@/lib/hooks";
|
||||
import { FiltersTab } from "./FiltersTab";
|
||||
import { FiCpu, FiFilter, FiX } from "react-icons/fi";
|
||||
import { IconType } from "react-icons";
|
||||
import { FaBrain } from "react-icons/fa";
|
||||
import { AssistantsTab } from "./AssistantsTab";
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { LlmTab } from "./LlmTab";
|
||||
import { LLMProviderDescriptor } from "@/app/admin/models/llm/interfaces";
|
||||
import { AssistantsIcon, IconProps } from "@/components/icons/icons";
|
||||
|
||||
const TabButton = ({
|
||||
label,
|
||||
icon: Icon,
|
||||
isActive,
|
||||
onClick,
|
||||
}: {
|
||||
label: string;
|
||||
icon: IconType;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
}) => (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`
|
||||
pb-4
|
||||
pt-6
|
||||
px-2
|
||||
text-emphasis
|
||||
font-bold
|
||||
${isActive ? "border-b-2 border-accent" : ""}
|
||||
hover:bg-hover-light
|
||||
hover:text-strong
|
||||
transition
|
||||
duration-200
|
||||
ease-in-out
|
||||
flex
|
||||
`}
|
||||
>
|
||||
<Icon className="inline-block mr-2 my-auto" size="16" />
|
||||
|
||||
<p className="my-auto">{label}</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export function ConfigurationModal({
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
onClose,
|
||||
availableAssistants,
|
||||
selectedAssistant,
|
||||
setSelectedAssistant,
|
||||
filterManager,
|
||||
llmProviders,
|
||||
llmOverrideManager,
|
||||
chatSessionId,
|
||||
}: {
|
||||
activeTab: string | null;
|
||||
setActiveTab: (tab: string | null) => void;
|
||||
onClose: () => void;
|
||||
availableAssistants: Persona[];
|
||||
selectedAssistant: Persona;
|
||||
setSelectedAssistant: (assistant: Persona) => void;
|
||||
filterManager: FilterManager;
|
||||
llmProviders: LLMProviderDescriptor[];
|
||||
llmOverrideManager: LlmOverrideManager;
|
||||
chatSessionId?: number;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
if (!activeTab) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onOutsideClick={onClose}
|
||||
noPadding
|
||||
className="
|
||||
w-4/6
|
||||
h-4/6
|
||||
flex
|
||||
flex-col
|
||||
"
|
||||
>
|
||||
<div className="rounded flex flex-col overflow-hidden">
|
||||
<div className="mb-4">
|
||||
<div className="flex border-b border-border bg-background-emphasis">
|
||||
<div className="flex px-6 gap-x-2">
|
||||
<TabButton
|
||||
label="Assistants"
|
||||
icon={FaBrain}
|
||||
isActive={activeTab === "assistants"}
|
||||
onClick={() => setActiveTab("assistants")}
|
||||
/>
|
||||
<TabButton
|
||||
label="Models"
|
||||
icon={FiCpu}
|
||||
isActive={activeTab === "llms"}
|
||||
onClick={() => setActiveTab("llms")}
|
||||
/>
|
||||
<TabButton
|
||||
label="Filters"
|
||||
icon={FiFilter}
|
||||
isActive={activeTab === "filters"}
|
||||
onClick={() => setActiveTab("filters")}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="
|
||||
ml-auto
|
||||
px-1
|
||||
py-1
|
||||
text-xs
|
||||
font-medium
|
||||
rounded
|
||||
hover:bg-hover
|
||||
focus:outline-none
|
||||
focus:ring-2
|
||||
focus:ring-offset-2
|
||||
focus:ring-subtle
|
||||
flex
|
||||
items-center
|
||||
h-fit
|
||||
my-auto
|
||||
mr-5
|
||||
"
|
||||
onClick={onClose}
|
||||
>
|
||||
<FiX size={24} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col overflow-y-auto">
|
||||
<div className="px-8 pt-4">
|
||||
{activeTab === "filters" && (
|
||||
<FiltersTab filterManager={filterManager} />
|
||||
)}
|
||||
|
||||
{activeTab === "llms" && (
|
||||
<LlmTab
|
||||
chatSessionId={chatSessionId}
|
||||
llmOverrideManager={llmOverrideManager}
|
||||
currentAssistant={selectedAssistant}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeTab === "assistants" && (
|
||||
<div>
|
||||
<AssistantsTab
|
||||
availableAssistants={availableAssistants}
|
||||
llmProviders={llmProviders}
|
||||
selectedAssistant={selectedAssistant}
|
||||
onSelect={(assistant) => {
|
||||
setSelectedAssistant(assistant);
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -35,7 +35,7 @@ export default async function Page({
|
||||
folders,
|
||||
toggleSidebar,
|
||||
openedFolders,
|
||||
defaultPersonaId,
|
||||
defaultAssistantId,
|
||||
finalDocumentSidebarInitialWidth,
|
||||
shouldShowWelcomeModal,
|
||||
shouldDisplaySourcesIncompleteModal,
|
||||
@ -58,7 +58,7 @@ export default async function Page({
|
||||
chatSessions,
|
||||
availableSources,
|
||||
availableDocumentSets: documentSets,
|
||||
availablePersonas: assistants,
|
||||
availableAssistants: assistants,
|
||||
availableTags: tags,
|
||||
llmProviders,
|
||||
folders,
|
||||
@ -66,7 +66,7 @@ export default async function Page({
|
||||
}}
|
||||
>
|
||||
<WrappedChat
|
||||
defaultPersonaId={defaultPersonaId}
|
||||
defaultAssistantId={defaultAssistantId}
|
||||
initiallyToggled={toggleSidebar}
|
||||
/>
|
||||
</ChatProvider>
|
||||
|
@ -1,111 +0,0 @@
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { BasicSelectable } from "@/components/BasicClickable";
|
||||
import { AssistantsIcon } from "@/components/icons/icons";
|
||||
import { User } from "@/lib/types";
|
||||
import { Text } from "@tremor/react";
|
||||
import Link from "next/link";
|
||||
import { FaRobot } from "react-icons/fa";
|
||||
import { FiEdit2 } from "react-icons/fi";
|
||||
|
||||
function AssistantDisplay({
|
||||
persona,
|
||||
onSelect,
|
||||
user,
|
||||
}: {
|
||||
persona: Persona;
|
||||
onSelect: (persona: Persona) => void;
|
||||
user: User | null;
|
||||
}) {
|
||||
const isEditable =
|
||||
(!user || user.id === persona.owner?.id) &&
|
||||
!persona.default_persona &&
|
||||
(!persona.is_public || !user || user.role === "admin");
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div className="w-full" onClick={() => onSelect(persona)}>
|
||||
<BasicSelectable selected={false} fullWidth>
|
||||
<div className="flex">
|
||||
<div className="truncate w-48 3xl:w-56 flex">
|
||||
<AssistantsIcon className="mr-2 my-auto" size={16} />{" "}
|
||||
{persona.name}
|
||||
</div>
|
||||
</div>
|
||||
</BasicSelectable>
|
||||
</div>
|
||||
{isEditable && (
|
||||
<div className="pl-2 my-auto">
|
||||
<Link href={`/assistants/edit/${persona.id}`}>
|
||||
<FiEdit2
|
||||
className="my-auto ml-auto hover:bg-hover p-0.5"
|
||||
size={20}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function AssistantsTab({
|
||||
personas,
|
||||
onPersonaChange,
|
||||
user,
|
||||
}: {
|
||||
personas: Persona[];
|
||||
onPersonaChange: (persona: Persona | null) => void;
|
||||
user: User | null;
|
||||
}) {
|
||||
const globalAssistants = personas.filter((persona) => persona.is_public);
|
||||
const personalAssistants = personas.filter(
|
||||
(persona) =>
|
||||
(!user || persona.users.some((u) => u.id === user.id)) &&
|
||||
!persona.is_public
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 pb-1 overflow-y-auto h-full flex flex-col gap-y-1">
|
||||
<Text className="mx-3 text-xs mb-4">
|
||||
Select an Assistant below to begin a new chat with them!
|
||||
</Text>
|
||||
|
||||
<div className="mx-3">
|
||||
{globalAssistants.length > 0 && (
|
||||
<>
|
||||
<div className="text-xs text-subtle flex pb-0.5 ml-1 mb-1.5 font-bold">
|
||||
Global
|
||||
</div>
|
||||
{globalAssistants.map((persona) => {
|
||||
return (
|
||||
<AssistantDisplay
|
||||
key={persona.id}
|
||||
persona={persona}
|
||||
onSelect={onPersonaChange}
|
||||
user={user}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
{personalAssistants.length > 0 && (
|
||||
<>
|
||||
<div className="text-xs text-subtle flex pb-0.5 ml-1 mb-1.5 mt-5 font-bold">
|
||||
Personal
|
||||
</div>
|
||||
{personalAssistants.map((persona) => {
|
||||
return (
|
||||
<AssistantDisplay
|
||||
key={persona.id}
|
||||
persona={persona}
|
||||
onSelect={onPersonaChange}
|
||||
user={user}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -18,7 +18,7 @@ interface ChatContextProps {
|
||||
chatSessions: ChatSession[];
|
||||
availableSources: ValidSources[];
|
||||
availableDocumentSets: DocumentSet[];
|
||||
availablePersonas: Persona[];
|
||||
availableAssistants: Persona[];
|
||||
availableTags: Tag[];
|
||||
llmProviders: LLMProviderDescriptor[];
|
||||
folders: Folder[];
|
||||
|
@ -19,7 +19,7 @@ export function Citation({
|
||||
return (
|
||||
<CustomTooltip
|
||||
citation
|
||||
content={<p className="inline-block p-0 m-0 truncate">{link}</p>}
|
||||
content={<div className="inline-block p-0 m-0 truncate">{link}</div>}
|
||||
>
|
||||
<a
|
||||
onClick={() => (link ? window.open(link, "_blank") : undefined)}
|
||||
|
@ -30,7 +30,6 @@ import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS";
|
||||
interface FetchChatDataResult {
|
||||
user: User | null;
|
||||
chatSessions: ChatSession[];
|
||||
|
||||
ccPairs: CCPairBasicInfo[];
|
||||
availableSources: ValidSources[];
|
||||
documentSets: DocumentSet[];
|
||||
@ -39,7 +38,7 @@ interface FetchChatDataResult {
|
||||
llmProviders: LLMProviderDescriptor[];
|
||||
folders: Folder[];
|
||||
openedFolders: Record<string, boolean>;
|
||||
defaultPersonaId?: number;
|
||||
defaultAssistantId?: number;
|
||||
toggleSidebar: boolean;
|
||||
finalDocumentSidebarInitialWidth?: number;
|
||||
shouldShowWelcomeModal: boolean;
|
||||
@ -150,9 +149,9 @@ export async function fetchChatData(searchParams: {
|
||||
console.log(`Failed to fetch tags - ${tagsResponse?.status}`);
|
||||
}
|
||||
|
||||
const defaultPersonaIdRaw = searchParams["assistantId"];
|
||||
const defaultPersonaId = defaultPersonaIdRaw
|
||||
? parseInt(defaultPersonaIdRaw)
|
||||
const defaultAssistantIdRaw = searchParams["assistantId"];
|
||||
const defaultAssistantId = defaultAssistantIdRaw
|
||||
? parseInt(defaultAssistantIdRaw)
|
||||
: undefined;
|
||||
|
||||
const documentSidebarCookieInitialWidth = cookies().get(
|
||||
@ -209,7 +208,7 @@ export async function fetchChatData(searchParams: {
|
||||
llmProviders,
|
||||
folders,
|
||||
openedFolders,
|
||||
defaultPersonaId,
|
||||
defaultAssistantId,
|
||||
finalDocumentSidebarInitialWidth,
|
||||
toggleSidebar,
|
||||
shouldShowWelcomeModal,
|
||||
|
Loading…
x
Reference in New Issue
Block a user