From 5bc8cba52222708e23ab4f43d502f0178f4f348b Mon Sep 17 00:00:00 2001 From: Akos Bontovics Date: Sun, 26 Apr 2026 14:00:47 +0200 Subject: [PATCH 1/3] fix(web): preserve selected language model --- .../chatBox/languageModelSelector.tsx | 6 +++ .../features/chat/useSelectedLanguageModel.ts | 53 ++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/packages/web/src/features/chat/components/chatBox/languageModelSelector.tsx b/packages/web/src/features/chat/components/chatBox/languageModelSelector.tsx index c8fc0196a..22ead7822 100644 --- a/packages/web/src/features/chat/components/chatBox/languageModelSelector.tsx +++ b/packages/web/src/features/chat/components/chatBox/languageModelSelector.tsx @@ -126,6 +126,12 @@ export const LanguageModelSelector = ({ return ( { selectModel(model) }} diff --git a/packages/web/src/features/chat/useSelectedLanguageModel.ts b/packages/web/src/features/chat/useSelectedLanguageModel.ts index a22b59400..cbc652c36 100644 --- a/packages/web/src/features/chat/useSelectedLanguageModel.ts +++ b/packages/web/src/features/chat/useSelectedLanguageModel.ts @@ -9,27 +9,66 @@ type Props = { languageModels: LanguageModelInfo[]; } +const getStoredSelectedLanguageModel = (): LanguageModelInfo | undefined => { + try { + const storedValue = window.localStorage.getItem("selectedLanguageModel"); + if (!storedValue) { + return undefined; + } + + return JSON.parse(storedValue) as LanguageModelInfo; + } catch { + return undefined; + } +}; + export const useSelectedLanguageModel = ({ languageModels, }: Props) => { const fallbackLanguageModel = languageModels.length > 0 ? languageModels[0] : undefined; const [selectedLanguageModel, setSelectedLanguageModel] = useLocalStorage( "selectedLanguageModel", - fallbackLanguageModel, + undefined, { initializeWithValue: false, } ); + const availableSelectedLanguageModel = selectedLanguageModel && languageModels.find( + (model) => getLanguageModelKey(model) === getLanguageModelKey(selectedLanguageModel) + ); + + const resolvedSelectedLanguageModel = availableSelectedLanguageModel ?? fallbackLanguageModel; + // Handle the case where the selected language model is no longer // available. Reset to the fallback language model in this case. useEffect(() => { - if (!selectedLanguageModel || !languageModels.find( - (model) => getLanguageModelKey(model) === getLanguageModelKey(selectedLanguageModel) - )) { - setSelectedLanguageModel(fallbackLanguageModel); + if (languageModels.length === 0) { + return; + } + + if (selectedLanguageModel && !availableSelectedLanguageModel) { + setSelectedLanguageModel((currentSelectedLanguageModel) => { + const storedSelectedLanguageModel = getStoredSelectedLanguageModel(); + const availableStoredSelectedLanguageModel = storedSelectedLanguageModel && languageModels.find( + (model) => getLanguageModelKey(model) === getLanguageModelKey(storedSelectedLanguageModel) + ); + + if (availableStoredSelectedLanguageModel) { + return availableStoredSelectedLanguageModel; + } + + if (currentSelectedLanguageModel && languageModels.find( + (model) => getLanguageModelKey(model) === getLanguageModelKey(currentSelectedLanguageModel) + )) { + return currentSelectedLanguageModel; + } + + return fallbackLanguageModel; + }); } }, [ + availableSelectedLanguageModel, fallbackLanguageModel, languageModels, selectedLanguageModel, @@ -37,7 +76,7 @@ export const useSelectedLanguageModel = ({ ]); return { - selectedLanguageModel, + selectedLanguageModel: resolvedSelectedLanguageModel, setSelectedLanguageModel, }; -} \ No newline at end of file +} From 357837050f62294a44d4a89b6a8cb94a91fbacf3 Mon Sep 17 00:00:00 2001 From: Akos Bontovics Date: Sun, 26 Apr 2026 14:37:44 +0200 Subject: [PATCH 2/3] fix(web): preserve raw language model selection --- .../src/features/chat/components/chatBox/chatBoxToolbar.tsx | 4 ++-- packages/web/src/features/chat/useSelectedLanguageModel.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/web/src/features/chat/components/chatBox/chatBoxToolbar.tsx b/packages/web/src/features/chat/components/chatBox/chatBoxToolbar.tsx index a0aae38cf..b7f3fce8b 100644 --- a/packages/web/src/features/chat/components/chatBox/chatBoxToolbar.tsx +++ b/packages/web/src/features/chat/components/chatBox/chatBoxToolbar.tsx @@ -27,7 +27,7 @@ export const ChatBoxToolbar = ({ isContextSelectorOpen, onContextSelectorOpenChanged, }: ChatBoxToolbarProps) => { - const { selectedLanguageModel, setSelectedLanguageModel } = useSelectedLanguageModel({ + const { resolvedSelectedLanguageModel, setSelectedLanguageModel } = useSelectedLanguageModel({ languageModels, }); @@ -48,7 +48,7 @@ export const ChatBoxToolbar = ({ ) diff --git a/packages/web/src/features/chat/useSelectedLanguageModel.ts b/packages/web/src/features/chat/useSelectedLanguageModel.ts index cbc652c36..8a8cbd583 100644 --- a/packages/web/src/features/chat/useSelectedLanguageModel.ts +++ b/packages/web/src/features/chat/useSelectedLanguageModel.ts @@ -76,7 +76,8 @@ export const useSelectedLanguageModel = ({ ]); return { - selectedLanguageModel: resolvedSelectedLanguageModel, + selectedLanguageModel, + resolvedSelectedLanguageModel, setSelectedLanguageModel, }; } From 323e262e3bb22d2167a16538a0ced2428ca732f2 Mon Sep 17 00:00:00 2001 From: Akos Bontovics Date: Sun, 26 Apr 2026 18:01:14 +0200 Subject: [PATCH 3/3] fix(web): validate stored language model --- .../features/chat/useSelectedLanguageModel.ts | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/web/src/features/chat/useSelectedLanguageModel.ts b/packages/web/src/features/chat/useSelectedLanguageModel.ts index 8a8cbd583..d42e1aa0d 100644 --- a/packages/web/src/features/chat/useSelectedLanguageModel.ts +++ b/packages/web/src/features/chat/useSelectedLanguageModel.ts @@ -16,7 +16,18 @@ const getStoredSelectedLanguageModel = (): LanguageModelInfo | undefined => { return undefined; } - return JSON.parse(storedValue) as LanguageModelInfo; + const parsedValue = JSON.parse(storedValue); + if ( + typeof parsedValue === "object" && + parsedValue !== null && + typeof parsedValue.provider === "string" && + typeof parsedValue.model === "string" && + typeof parsedValue.displayName === "string" + ) { + return parsedValue as LanguageModelInfo; + } + + return undefined; } catch { return undefined; } @@ -48,7 +59,7 @@ export const useSelectedLanguageModel = ({ } if (selectedLanguageModel && !availableSelectedLanguageModel) { - setSelectedLanguageModel((currentSelectedLanguageModel) => { + setSelectedLanguageModel(() => { const storedSelectedLanguageModel = getStoredSelectedLanguageModel(); const availableStoredSelectedLanguageModel = storedSelectedLanguageModel && languageModels.find( (model) => getLanguageModelKey(model) === getLanguageModelKey(storedSelectedLanguageModel) @@ -58,12 +69,6 @@ export const useSelectedLanguageModel = ({ return availableStoredSelectedLanguageModel; } - if (currentSelectedLanguageModel && languageModels.find( - (model) => getLanguageModelKey(model) === getLanguageModelKey(currentSelectedLanguageModel) - )) { - return currentSelectedLanguageModel; - } - return fallbackLanguageModel; }); }