From de6375ebc5e44e5d1441839d5c5506b60e42ba13 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Fri, 24 Jul 2026 21:12:36 -0500 Subject: [PATCH] fix(desktop): persist the whole discovered model list when saving an endpoint Test enumerates a custom provider's catalogue and the panel holds the result in discoveredModels, but the save payload never carried it, so only the one model the user hand-typed reached providers..models. Every downstream picker reads that map straight from config.yaml with no live probe, which is why a proxy serving 18 models offered exactly one. Send the discovered list and merge it onto the entry, so models already known keep their context lengths. Fixes #69988 Co-authored-by: asorry75 <33794789+asorry75@users.noreply.github.com> --- .../app/settings/custom-endpoints-settings.tsx | 7 ++++--- apps/desktop/src/types/hermes.ts | 1 + hermes_cli/web_server.py | 18 +++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/desktop/src/app/settings/custom-endpoints-settings.tsx b/apps/desktop/src/app/settings/custom-endpoints-settings.tsx index bea02e2bce79..b74b30a29090 100644 --- a/apps/desktop/src/app/settings/custom-endpoints-settings.tsx +++ b/apps/desktop/src/app/settings/custom-endpoints-settings.tsx @@ -58,7 +58,7 @@ function formFromEndpoint(endpoint: CustomEndpoint): EndpointForm { } } -function toPayload(form: EndpointForm): CustomEndpointUpdate { +function toPayload(form: EndpointForm, models?: string[]): CustomEndpointUpdate { const contextLength = Number.parseInt(form.contextLength, 10) return { @@ -69,7 +69,8 @@ function toPayload(form: EndpointForm): CustomEndpointUpdate { api_key: form.apiKey.trim() || undefined, context_length: Number.isFinite(contextLength) && contextLength > 0 ? contextLength : undefined, discover_models: form.discoverModels, - make_default: form.makeDefault + make_default: form.makeDefault, + models: models?.length ? models : undefined } } @@ -125,7 +126,7 @@ export function CustomEndpointsSettings({ onConfigSaved, onMainModelChanged }: C async function handleSave() { try { setSaving(true) - const response = await saveCustomEndpoint(toPayload(form)) + const response = await saveCustomEndpoint(toPayload(form, discoveredModels)) setEndpoints(response.endpoints) const saved = response.endpoints.find(endpoint => endpoint.id === response.id) diff --git a/apps/desktop/src/types/hermes.ts b/apps/desktop/src/types/hermes.ts index d811e07ed57c..cd62848d7387 100644 --- a/apps/desktop/src/types/hermes.ts +++ b/apps/desktop/src/types/hermes.ts @@ -182,6 +182,7 @@ export interface CustomEndpointUpdate { id?: string make_default?: boolean model: string + models?: string[] name: string } diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 1e5cf1655178..4c2690be1624 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -1271,6 +1271,7 @@ class CustomEndpointUpdate(BaseModel): context_length: Optional[int] = None discover_models: bool = True make_default: bool = False + models: Optional[List[str]] = None class MessagingPlatformUpdate(BaseModel): @@ -7700,13 +7701,20 @@ def _write_custom_endpoint(cfg: Dict[str, Any], body: CustomEndpointUpdate) -> T "model": model, "discover_models": bool(body.discover_models), }) - # Same for the model map: the panel names one default model, it does not - # enumerate the provider's catalogue. Keep the other models (and their - # context lengths) and just ensure this one is present. + # Same for the model map: merge rather than replace, so existing models + # keep their context lengths. ``body.models`` is the catalogue the panel's + # Test button already discovered — without it only the one hand-typed + # model survived Save, and every picker showed a single-entry list for a + # provider serving dozens (#69988). A payload with no ``models`` (older + # UI) still just ensures the named default is present. existing_models = entry.get("models") models_map: Dict[str, Any] = dict(existing_models) if isinstance(existing_models, dict) else {} - current_model_entry = models_map.get(model) - models_map[model] = dict(current_model_entry) if isinstance(current_model_entry, dict) else {} + for candidate in (*(body.models or ()), model): + model_id = str(candidate).strip() + if not model_id: + continue + current = models_map.get(model_id) + models_map[model_id] = dict(current) if isinstance(current, dict) else {} entry["models"] = models_map if body.context_length and body.context_length > 0: entry["context_length"] = int(body.context_length)