mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-27 17:58:07 +00:00
fix(desktop): keep model picker aligned with session state
This commit is contained in:
parent
fffa661223
commit
c943787bf6
5 changed files with 54 additions and 15 deletions
|
|
@ -124,6 +124,26 @@ describe('ModelMenuPanel MoA presets', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('ModelMenuPanel current selection', () => {
|
||||
it('keeps the checkmark on the live SessionView model when a stale options response disagrees', async () => {
|
||||
$currentProvider.set('google')
|
||||
$currentModel.set('gemini-3.1-pro')
|
||||
getGlobalModelOptions.mockResolvedValue({
|
||||
model: 'deepseek-chat',
|
||||
provider: 'deepseek',
|
||||
providers: MOCK_PROVIDERS
|
||||
})
|
||||
|
||||
const { content } = renderPanel()
|
||||
|
||||
const currentRow = (await content.findByText(/Gemini 3\.1 Pro/i)).closest('[role="menuitem"]')
|
||||
const staleRow = content.getByText('Deepseek Chat').closest('[role="menuitem"]')
|
||||
|
||||
expect(currentRow?.querySelector('.codicon-check')).not.toBeNull()
|
||||
expect(staleRow?.querySelector('.codicon-check')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('ModelMenuPanel provider collapse', () => {
|
||||
it('shows all provider models by default (none collapsed)', async () => {
|
||||
const { content } = renderPanel()
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ export function ModelMenuPanel({ gateway, onSelectModel, profile = 'default', re
|
|||
})
|
||||
|
||||
const { model: optionsModel, provider: optionsProvider } = currentPickerSelection(
|
||||
!!activeSessionId,
|
||||
{ model: currentModel, provider: currentProvider },
|
||||
modelOptions.data
|
||||
)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ export function ModelPickerDialog({
|
|||
const providers = modelOptions.data?.providers ?? []
|
||||
|
||||
const { model: optionsModel, provider: optionsProvider } = currentPickerSelection(
|
||||
!!sessionId,
|
||||
{ model: currentModel, provider: currentProvider },
|
||||
modelOptions.data
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,19 +48,23 @@ describe('model-status-label', () => {
|
|||
const options = { model: 'hermes-4', provider: 'nous' }
|
||||
|
||||
it('prefers the sticky composer pick over the profile default pre-session', () => {
|
||||
expect(currentPickerSelection(false, store, options)).toEqual(store)
|
||||
expect(currentPickerSelection(store, options)).toEqual(store)
|
||||
})
|
||||
|
||||
it('lets the live session model.options win when a session exists', () => {
|
||||
expect(currentPickerSelection(true, store, options)).toEqual(options)
|
||||
it('keeps the SessionView selection when a stale options response disagrees', () => {
|
||||
expect(currentPickerSelection(store, options)).toEqual(store)
|
||||
})
|
||||
|
||||
it('falls back to options when the store is empty', () => {
|
||||
expect(currentPickerSelection(false, { model: '', provider: '' }, options)).toEqual(options)
|
||||
expect(currentPickerSelection({ model: '', provider: '' }, options)).toEqual(options)
|
||||
})
|
||||
|
||||
it('uses the complete options pair instead of mixing a partial store selection', () => {
|
||||
expect(currentPickerSelection({ model: 'opus', provider: '' }, options)).toEqual(options)
|
||||
})
|
||||
|
||||
it('falls back to the store while options are still loading', () => {
|
||||
expect(currentPickerSelection(true, store, undefined)).toEqual(store)
|
||||
expect(currentPickerSelection(store, undefined)).toEqual(store)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,19 +21,36 @@ export function reasoningEffortLabel(effort: string): string {
|
|||
return REASONING_LABELS[key] ?? effort
|
||||
}
|
||||
|
||||
/** Which model/provider a picker should mark "current". With a live session the
|
||||
* gateway's `model.options` is authoritative; pre-session there is no server
|
||||
* "current", so the sticky composer pick wins over the profile default the
|
||||
* global options query returns — else the checkmark snaps back to the default
|
||||
* and the pick looks ignored. */
|
||||
/** Which model/provider pair a picker should mark "current". SessionView state
|
||||
* also drives the composer label, so a complete pair there wins over an older
|
||||
* `model.options` response. During initial hydration (or pre-session startup),
|
||||
* options remain the fallback. Pick one complete pair before mixing fields so
|
||||
* a model is never shown under a different provider. */
|
||||
export function currentPickerSelection(
|
||||
hasSession: boolean,
|
||||
store: { model: string; provider: string },
|
||||
options?: { model?: string; provider?: string }
|
||||
): { model: string; provider: string } {
|
||||
const storeSelection = {
|
||||
model: String(store.model || ''),
|
||||
provider: String(store.provider || '')
|
||||
}
|
||||
|
||||
const optionsSelection = {
|
||||
model: String(options?.model || ''),
|
||||
provider: String(options?.provider || '')
|
||||
}
|
||||
|
||||
if (storeSelection.model && storeSelection.provider) {
|
||||
return storeSelection
|
||||
}
|
||||
|
||||
if (optionsSelection.model && optionsSelection.provider) {
|
||||
return optionsSelection
|
||||
}
|
||||
|
||||
return {
|
||||
model: String((hasSession && options?.model) || store.model || options?.model || ''),
|
||||
provider: String((hasSession && options?.provider) || store.provider || options?.provider || '')
|
||||
model: storeSelection.model || optionsSelection.model,
|
||||
provider: storeSelection.provider || optionsSelection.provider
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue