fix: model picker in chat no longer overwrites the global default model#713
Conversation
Selecting a model from the bottom-panel picker in a chat session previously
called setModelConfig(), which wrote to config.yaml and permanently changed
the user's global default — even though the intent was only to override the
model for that conversation.
The fix separates session-scoped model selection from persistent (settings)
selection:
- `useModelConfig.selectModel` accepts a new `{ persist?: boolean }` option
(default true). When `persist: false` the local React state is updated
immediately but `setModelConfig` / `getModelConfig` IPC calls are skipped,
so config.yaml stays untouched.
- The chat-screen picker now calls `selectModel(..., { persist: false })`,
while the Settings screen retains the existing persisting behaviour.
- A `modelOverride` parameter is threaded through the full send pipeline
(`useChatActions` → IPC preload → IPC handler → hermes.ts export →
`sendMessageViaBestApi*` → `sendMessageViaNonGatewayApi` →
`sendMessageViaRuns` / `sendMessageViaApi`, and the CLI fallback
`sendMessageViaCli`) so the gateway receives the session model on every
message instead of re-reading the now-unchanged config.yaml value.
Fixes fathah#688.
Greptile SummaryThis PR fixes issue #688 where picking a model from the in-chat bottom panel was permanently overwriting the user's global default model in
Confidence Score: 4/5Safe to merge for local-mode users, but the dashboard (SSH) transport path does not forward the session model override, so the fix is incomplete for that deployment scenario. The local-mode IPC chain is correctly updated end-to-end and the TUI gateway bypass is a sound workaround for its lack of per-request model support. However, in useChatActions.sendToAgent, when sendViaDashboard handles the message and returns true, execution returns before window.hermesAPI.sendMessage is reached — the sessionModelRef.current value is never sent to the backend. SSH/remote users with dashboard transport enabled will find that picking a session model in the chat picker has no effect, the same silent-ignore behaviour the PR is trying to eliminate. src/renderer/src/screens/Chat/hooks/useChatActions.ts — the sendViaDashboard early-return path needs to accept and forward modelOverride. Important Files Changed
|
Two issues raised in the Greptile review of fathah#713: 1. TUI gateway silently drops modelOverride (P1) sendMessageViaBestApi tried the TUI gateway first, which reads its model from config.yaml and has no per-request override mechanism. A one-line `!modelOverride` guard now bypasses the TUI path when a session model is active, routing directly to sendMessageViaNonGatewayApi which already propagates modelOverride correctly. 2. Background reload() can clobber session-scoped model selection When selectModel is called with persist:false, loadSeqRef was not advanced, so a concurrent reload() triggered by onConnectionConfigChanged or onModelLibraryChanged could race and overwrite the in-session choice with the persisted value. Incrementing loadSeqRef on the non-persist path cancels any in-flight reload, consistent with how the persist path already handles seq races.
|
Hey @fathah 👋 Quería tomarte un momento para decirte lo mucho que aprecio lo que has construido con Hermes Desktop. Mis 12 colegas y yo lo usamos todos los días — ha cambiado genuinamente la forma en que trabajamos. Me siento muy orgulloso de poder aportar algo de vuelta al proyecto. Mantengo la traducción al español LATAM porque para nosotros es importante tener la app disponible en nuestro idioma, y reviso las actualizaciones diariamente para mantenerla al día con los cambios del README. También abrí este PR para corregir el bug del model picker (#688) — todos en mi equipo lo teníamos: cada vez que cambiábamos el modelo en el chat, se sobreescribía el modelo por defecto en Settings sin querer. Imagino que todos los usuarios que usan el picker lo sufren también. Gracias por construir algo tan bueno y por dejarme ser parte de esto. Significa mucho para nosotros. 🙏 |
Passing modelConfig.currentModel as sessionModel caused modelOverride to always be non-empty after the initial config load, permanently bypassing the TUI gateway for all users regardless of whether they had explicitly picked a session model. Use a separate sessionModelOverride state variable (undefined by default) that is only set when the user explicitly selects a model from the chat picker. The TUI gateway bypass now triggers only for sessions where the user made an explicit in-chat model change, which is the intended behaviour.
The fallbackToChatCompletions closure inside sendMessageViaRuns called sendMessageViaApi without forwarding the modelOverride captured in the outer scope. If the Runs transport failed mid-flight, the session model selection was silently dropped on the fallback path.
Problem
Selecting a model from the bottom-panel picker in a chat session permanently changes the user's global default model in Settings — even when the intent is only to try a different model for that one conversation. Closes #688.
Steps to reproduce:
Root cause
useModelConfig.selectModelalways calledsetModelConfig(), which writes toconfig.yaml. The chat-screen picker and the Settings screen shared the same code path with no distinction between "use for this session" and "save as my default".Fix
Renderer side (
useModelConfig,Chat.tsx,useChatActions):selectModelgains a{ persist?: boolean }option (defaulttrue— existing callers are unaffected).persist: false, the local React state updates immediately (so the picker UI reflects the choice) butsetModelConfig/getModelConfigIPC calls are skipped —config.yamlstays untouched.ModelPickernow passespersist: false; the Settings screen keeps using the default (persist: true).useChatActionsaccepts asessionModelprop and tracks it via a ref sosendToAgentalways reads the latest session-scoped model without adding it to the callback's dependency array.IPC pipeline (
preload,index.ts,hermes.ts):modelOverride?: stringparameter is added to every link in the send chain:sendMessageexport →sendMessageViaBestApi*→sendMessageViaNonGatewayApi→sendMessageViaRuns/sendMessageViaApi/sendMessageViaCli.modelOverride || mc.model || "hermes-agent"so the session choice reaches the gateway without touching persisted config.No behaviour change for existing callers — all new parameters are optional and default to the current behaviour.
Test plan
claude-3-5-sonnet).gpt-4o).gpt-4o(check the session tag in the sidebar).claude-3-5-sonnet. ✅claude-3-5-sonnet. ✅