diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index a1e5fd2b7..e63e57212 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -47,6 +47,7 @@ import { removeMessage, sendMessage, setMessageAsLatest, + updateLlmOverrideForChatSession, updateParentChildren, uploadFilesForChat, useScrollonStream, @@ -65,7 +66,7 @@ import { import { usePopup } from "@/components/admin/connectors/Popup"; import { SEARCH_PARAM_NAMES, shouldSubmitOnLoad } from "./searchParams"; import { useDocumentSelection } from "./useDocumentSelection"; -import { LlmOverride, useFilters, useLlmOverride } from "@/lib/hooks"; +import { LlmDescriptor, useFilters, useLlmManager } from "@/lib/hooks"; import { ChatState, FeedbackType, RegenerationState } from "./types"; import { DocumentResults } from "./documentSidebar/DocumentResults"; import { OnyxInitializingLoader } from "@/components/OnyxInitializingLoader"; @@ -89,7 +90,11 @@ import { import { buildFilters } from "@/lib/search/utils"; import { SettingsContext } from "@/components/settings/SettingsProvider"; import Dropzone from "react-dropzone"; -import { checkLLMSupportsImageInput, getFinalLLM } from "@/lib/llm/utils"; +import { + checkLLMSupportsImageInput, + getFinalLLM, + structureValue, +} from "@/lib/llm/utils"; import { ChatInputBar } from "./input/ChatInputBar"; import { useChatContext } from "@/components/context/ChatContext"; import { v4 as uuidv4 } from "uuid"; @@ -356,7 +361,7 @@ export function ChatPage({ ] ); - const llmOverrideManager = useLlmOverride( + const llmManager = useLlmManager( llmProviders, selectedChatSession, liveAssistant @@ -1138,7 +1143,7 @@ export function ChatPage({ forceSearch, isSeededChat, alternativeAssistantOverride = null, - modelOverRide, + modelOverride, regenerationRequest, overrideFileDescriptors, }: { @@ -1148,7 +1153,7 @@ export function ChatPage({ forceSearch?: boolean; isSeededChat?: boolean; alternativeAssistantOverride?: Persona | null; - modelOverRide?: LlmOverride; + modelOverride?: LlmDescriptor; regenerationRequest?: RegenerationRequest | null; overrideFileDescriptors?: FileDescriptor[]; } = {}) => { @@ -1191,6 +1196,22 @@ export function ChatPage({ currChatSessionId = chatSessionIdRef.current as string; } frozenSessionId = currChatSessionId; + // update the selected model for the chat session if one is specified so that + // it persists across page reloads. Do not `await` here so that the message + // request can continue and this will just happen in the background. + // NOTE: only set the model override for the chat session once we send a + // message with it. If the user switches models and then starts a new + // chat session, it is unexpected for that model to be used when they + // return to this session the next day. + let finalLLM = modelOverride || llmManager.currentLlm; + updateLlmOverrideForChatSession( + currChatSessionId, + structureValue( + finalLLM.name || "", + finalLLM.provider || "", + finalLLM.modelName || "" + ) + ); updateStatesWithNewSessionId(currChatSessionId); @@ -1250,11 +1271,14 @@ export function ChatPage({ : null) || (messageMap.size === 1 ? Array.from(messageMap.values())[0] : null); - const currentAssistantId = alternativeAssistantOverride - ? alternativeAssistantOverride.id - : alternativeAssistant - ? alternativeAssistant.id - : liveAssistant.id; + let currentAssistantId; + if (alternativeAssistantOverride) { + currentAssistantId = alternativeAssistantOverride.id; + } else if (alternativeAssistant) { + currentAssistantId = alternativeAssistant.id; + } else { + currentAssistantId = liveAssistant.id; + } resetInputBar(); let messageUpdates: Message[] | null = null; @@ -1326,15 +1350,13 @@ export function ChatPage({ forceSearch, regenerate: regenerationRequest !== undefined, modelProvider: - modelOverRide?.name || - llmOverrideManager.llmOverride.name || - undefined, + modelOverride?.name || llmManager.currentLlm.name || undefined, modelVersion: - modelOverRide?.modelName || - llmOverrideManager.llmOverride.modelName || + modelOverride?.modelName || + llmManager.currentLlm.modelName || searchParams.get(SEARCH_PARAM_NAMES.MODEL_VERSION) || undefined, - temperature: llmOverrideManager.temperature || undefined, + temperature: llmManager.temperature || undefined, systemPromptOverride: searchParams.get(SEARCH_PARAM_NAMES.SYSTEM_PROMPT) || undefined, useExistingUserMessage: isSeededChat, @@ -1802,7 +1824,7 @@ export function ChatPage({ const [_, llmModel] = getFinalLLM( llmProviders, liveAssistant, - llmOverrideManager.llmOverride + llmManager.currentLlm ); const llmAcceptsImages = checkLLMSupportsImageInput(llmModel); @@ -2121,7 +2143,7 @@ export function ChatPage({ }, [searchParams, router]); useEffect(() => { - llmOverrideManager.updateImageFilesPresent(imageFileInMessageHistory); + llmManager.updateImageFilesPresent(imageFileInMessageHistory); }, [imageFileInMessageHistory]); const pathname = usePathname(); @@ -2175,9 +2197,9 @@ export function ChatPage({ function createRegenerator(regenerationRequest: RegenerationRequest) { // Returns new function that only needs `modelOverRide` to be specified when called - return async function (modelOverRide: LlmOverride) { + return async function (modelOverride: LlmDescriptor) { return await onSubmit({ - modelOverRide, + modelOverride, messageIdToResend: regenerationRequest.parentMessage.messageId, regenerationRequest, forceSearch: regenerationRequest.forceSearch, @@ -2258,9 +2280,7 @@ export function ChatPage({ {(settingsToggled || userSettingsToggled) && ( - llmOverrideManager.updateLLMOverride(newOverride) - } + setCurrentLlm={(newLlm) => llmManager.updateCurrentLlm(newLlm)} defaultModel={user?.preferences.default_model!} llmProviders={llmProviders} onClose={() => { @@ -2324,7 +2344,7 @@ export function ChatPage({ setSharedChatSession(null)} @@ -2342,7 +2362,7 @@ export function ChatPage({ setSharingModalVisible(false)} @@ -3058,7 +3078,7 @@ export function ChatPage({ messageId: message.messageId, parentMessage: parentMessage!, forceSearch: true, - })(llmOverrideManager.llmOverride); + })(llmManager.currentLlm); } else { setPopup({ type: "error", @@ -3203,7 +3223,7 @@ export function ChatPage({ availableDocumentSets={documentSets} availableTags={tags} filterManager={filterManager} - llmOverrideManager={llmOverrideManager} + llmManager={llmManager} removeDocs={() => { clearSelectedDocuments(); }} diff --git a/web/src/app/chat/RegenerateOption.tsx b/web/src/app/chat/RegenerateOption.tsx index f947ebf78..1265db2eb 100644 --- a/web/src/app/chat/RegenerateOption.tsx +++ b/web/src/app/chat/RegenerateOption.tsx @@ -1,8 +1,8 @@ import { useChatContext } from "@/components/context/ChatContext"; import { getDisplayNameForModel, - LlmOverride, - useLlmOverride, + LlmDescriptor, + useLlmManager, } from "@/lib/hooks"; import { StringOrNumberOption } from "@/components/Dropdown"; @@ -106,13 +106,13 @@ export default function RegenerateOption({ onDropdownVisibleChange, }: { selectedAssistant: Persona; - regenerate: (modelOverRide: LlmOverride) => Promise; + regenerate: (modelOverRide: LlmDescriptor) => Promise; overriddenModel?: string; onHoverChange: (isHovered: boolean) => void; onDropdownVisibleChange: (isVisible: boolean) => void; }) { const { llmProviders } = useChatContext(); - const llmOverrideManager = useLlmOverride(llmProviders); + const llmManager = useLlmManager(llmProviders); const [_, llmName] = getFinalLLM(llmProviders, selectedAssistant, null); @@ -148,7 +148,7 @@ export default function RegenerateOption({ ); const currentModelName = - llmOverrideManager?.llmOverride.modelName || + llmManager?.currentLlm.modelName || (selectedAssistant ? selectedAssistant.llm_model_version_override || llmName : llmName); diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx index 4010be443..f8a5c34aa 100644 --- a/web/src/app/chat/input/ChatInputBar.tsx +++ b/web/src/app/chat/input/ChatInputBar.tsx @@ -6,7 +6,7 @@ import { Persona } from "@/app/admin/assistants/interfaces"; import LLMPopover from "./LLMPopover"; import { InputPrompt } from "@/app/chat/interfaces"; -import { FilterManager, LlmOverrideManager } from "@/lib/hooks"; +import { FilterManager, LlmManager } from "@/lib/hooks"; import { useChatContext } from "@/components/context/ChatContext"; import { ChatFileType, FileDescriptor } from "../interfaces"; import { @@ -180,7 +180,7 @@ interface ChatInputBarProps { setMessage: (message: string) => void; stopGenerating: () => void; onSubmit: () => void; - llmOverrideManager: LlmOverrideManager; + llmManager: LlmManager; chatState: ChatState; alternativeAssistant: Persona | null; // assistants @@ -225,7 +225,7 @@ export function ChatInputBar({ availableSources, availableDocumentSets, availableTags, - llmOverrideManager, + llmManager, proSearchEnabled, setProSearchEnabled, }: ChatInputBarProps) { @@ -781,7 +781,7 @@ export function ChatInputBar({ diff --git a/web/src/app/chat/input/LLMPopover.tsx b/web/src/app/chat/input/LLMPopover.tsx index ad7e18e8e..1a4b6ab08 100644 --- a/web/src/app/chat/input/LLMPopover.tsx +++ b/web/src/app/chat/input/LLMPopover.tsx @@ -16,7 +16,7 @@ import { LLMProviderDescriptor, } from "@/app/admin/configuration/llm/interfaces"; import { Persona } from "@/app/admin/assistants/interfaces"; -import { LlmOverrideManager } from "@/lib/hooks"; +import { LlmManager } from "@/lib/hooks"; import { Tooltip, @@ -31,21 +31,19 @@ import { useUser } from "@/components/user/UserProvider"; interface LLMPopoverProps { llmProviders: LLMProviderDescriptor[]; - llmOverrideManager: LlmOverrideManager; + llmManager: LlmManager; requiresImageGeneration?: boolean; currentAssistant?: Persona; } export default function LLMPopover({ llmProviders, - llmOverrideManager, + llmManager, requiresImageGeneration, currentAssistant, }: LLMPopoverProps) { const [isOpen, setIsOpen] = useState(false); const { user } = useUser(); - const { llmOverride, updateLLMOverride } = llmOverrideManager; - const currentLlm = llmOverride.modelName; const llmOptionsByProvider: { [provider: string]: { @@ -93,19 +91,19 @@ export default function LLMPopover({ : null; const [localTemperature, setLocalTemperature] = useState( - llmOverrideManager.temperature ?? 0.5 + llmManager.temperature ?? 0.5 ); useEffect(() => { - setLocalTemperature(llmOverrideManager.temperature ?? 0.5); - }, [llmOverrideManager.temperature]); + setLocalTemperature(llmManager.temperature ?? 0.5); + }, [llmManager.temperature]); const handleTemperatureChange = (value: number[]) => { setLocalTemperature(value[0]); }; const handleTemperatureChangeComplete = (value: number[]) => { - llmOverrideManager.updateTemperature(value[0]); + llmManager.updateTemperature(value[0]); }; return ( @@ -120,15 +118,15 @@ export default function LLMPopover({ toggle flexPriority="stiff" name={getDisplayNameForModel( - llmOverrideManager?.llmOverride.modelName || + llmManager?.currentLlm.modelName || defaultModelDisplayName || "Models" )} Icon={getProviderIcon( - llmOverrideManager?.llmOverride.provider || + llmManager?.currentLlm.provider || defaultProvider?.provider || "anthropic", - llmOverrideManager?.llmOverride.modelName || + llmManager?.currentLlm.modelName || defaultProvider?.default_model_name || "claude-3-5-sonnet-20240620" )} @@ -147,12 +145,12 @@ export default function LLMPopover({