hermes-agent/apps/desktop/src/lib/model-options.ts
Brooklyn Nicholson d5decee5ac fix(desktop): reseed composer when a sticky manual pick was removed
A manual composer pick stays sticky across new chats (intended), but if that
model later disappears from the provider/config, every new chat kept trying the
dead model and 404'd. Fall back to the profile default in that one case only.

refreshCurrentModel now consults the model-options cache the composer already
populates (no extra fetch): a manual pick is preserved unless manualPickRemoved()
proves it's gone — provider present, non-empty catalog, model absent. An
unknown/absent provider, an empty (re-auth/unconfigured) list, or a not-yet-loaded
catalog all preserve the pick, so a still-valid selection is never clobbered.
2026-07-18 23:44:40 -04:00

72 lines
2 KiB
TypeScript

import { getGlobalModelOptions, type HermesGateway, type ModelOptionsResponse } from '@/hermes'
import type { ModelOptionProvider } from '@/types/hermes'
/**
* True only when a persisted **manual** composer pick has been removed from the
* catalog (its provider still ships models, but no longer this one) — so a new
* chat would keep 404'ing the dead model. Deliberately conservative to never
* clobber a still-valid pick: an unknown/absent provider, an empty model list
* (re-auth / unconfigured), or a not-yet-loaded catalog all return false.
*/
export function manualPickRemoved(
providers: ModelOptionProvider[] | undefined,
provider: string,
model: string
): boolean {
if (!providers?.length || !provider || !model) {
return false
}
const row = providers.find(p => p.slug === provider || p.name === provider)
if (!row) {
return false
}
const models = row.models ?? []
// Empty list means the provider is present but unconfigured / awaiting
// re-auth, not that the model was dropped — leave the pick alone.
if (models.length === 0) {
return false
}
return !models.includes(model)
}
interface ModelOptionsRequest {
/** When false, include ambient/unconfigured providers (onboarding/setup
* surfaces). Chat pickers default to true so only explicitly configured
* providers are listed (#56974). */
explicitOnly?: boolean
gateway?: HermesGateway
refresh?: boolean
sessionId?: null | string
}
export function requestModelOptions({
explicitOnly = true,
gateway,
refresh = false,
sessionId
}: ModelOptionsRequest): Promise<ModelOptionsResponse> {
if (gateway) {
const params: Record<string, unknown> = {}
if (sessionId) {
params.session_id = sessionId
}
if (refresh) {
params.refresh = true
}
if (explicitOnly) {
params.explicit_only = true
}
return gateway.request<ModelOptionsResponse>('model.options', params)
}
return getGlobalModelOptions({ explicitOnly, ...(refresh ? { refresh: true } : {}) })
}