Skip to content

Bug: el botón "Guardar" en config de proveedor borra los modelos seleccionados al disparar un re-sync que sobrescribe provider.models #273

@Saul-Gomez-J

Description

@Saul-Gomez-J

Resumen

Al pulsar el botón Guardar en Configuración de Proveedor (Local Provider y Vercel Gateway), los modelos previamente seleccionados desaparecen del selector del chat. No es solo una percepción de UX — el bug es reproducible en código: el handler dispara un re-sync que, ante un fetch vacío o fallido, sobrescribe provider.models con un array vacío.

Reproducción (Local Provider)

  1. Abrir Configuración de ProveedorLocal Provider.
  2. Configurar URL Base (ej. http://localhost:11434 con Ollama corriendo). Pulsar Descubrir Modelos.
  3. En Modelos Disponibles, marcar algún modelo (ej. qwen3:6). La selección se autoguarda correctamente (selectedModelIds y model.isSelected se actualizan).
  4. Escribir una API Key (opcional) en el campo correspondiente.
  5. Pulsar el botón Guardar.
  6. Ir al chat → el apartado de modelos locales no aparece / el modelo seleccionado se ha perdido.

Mismo patrón ocurre en Vercel Gateway tras pulsar Guardar.

Causa raíz

1) handleSave dispara un re-sync inmediato tras guardar credenciales:

src/renderer/pages/ModelPage/ProviderConfigs.tsx:117-123 (GatewayConfig):

const handleSave = async () => {
  await updateProvider(provider.id, { apiKey, baseUrl });
  if (apiKey) {
    syncProviderModels(provider.id);   // ← re-fetch tras guardar
  }
};

src/renderer/pages/ModelPage/ProviderConfigs.tsx:189-197 (LocalConfig):

const handleSave = async () => {
  await updateProvider(provider.id, {
    baseUrl,
    apiKey: apiKey.trim() || undefined,
  });
  if (baseUrl) {
    syncProviderModels(provider.id);   // ← re-fetch tras guardar
  }
};

2) El re-sync sobrescribe provider.models sin proteger contra fetch vacío:

src/renderer/services/modelService.ts:748-770:

// Update provider models: combine synced models with user-defined models
provider.models = [...models, ...userDefinedModels];   // ← línea 750: reemplazo incondicional

// Only update selectedModelIds if we actually got new models from sync
// This prevents losing saved selections when sync fails or returns empty
if (models.length > 0) {
  provider.selectedModelIds = provider.models.filter(m => m.isSelected).map(m => m.id);
  this.syncedProvidersInSession.add(provider.id);
}
else if (userDefinedModels.length > 0) {
  // ...preserva selectedModelIds...
}
// If sync returned empty and no user-defined, preserve existing selectedModelIds
// (don't clear them - they'll be restored when sync succeeds later)

El comentario indica intención de "preservar selecciones" cuando el sync falla, y la lógica protege selectedModelIds. Pero la línea 750 ya ejecutó provider.models = [] antes de ese chequeo, por lo que el array de modelos disponibles queda vacío.

3) _saveProviders luego persiste el array vacío:

src/renderer/services/modelService.ts:815-837 filtra provider.models por selectedIdSet, y como provider.models ya está vacío, persiste models: [] en ui-preferences.json. El selector del chat lee provider.models, por lo que el usuario no ve ningún modelo.

Casos en que el fetch devuelve vacío / falla

  • Local provider con Ollama/LM Studio cuando se añade una API Key que el endpoint no espera (algunos devuelven 401, otros responden vacío).
  • Endpoint local momentáneamente no disponible cuando se pulsa Guardar.
  • Cualquier error transitorio de red en Vercel Gateway.

En todos estos casos el comportamiento esperado sería mantener el estado anterior, no vaciar provider.models.

Reportes de usuarios

"Recién me descargue la nueva versión, pero veo que no me guarda el modelo local cuando lo configuro, me reconoce los modelos, selecciono el que quiero pero no guarda esa información, y al ir al chat no me aparecen el apartado de modelos locales."

"Ahhh, ya vi el error, que cuando le doy a guardar me quita el modelo que tenía seleccionado."

Propuestas de fix

Por orden de preferencia:

  1. (Preferida) Eliminar el re-sync automático tras guardar credenciales. El usuario ya dispone del botón Descubrir Modelos / Sync explícito. Guardar credenciales no debería tener efectos colaterales sobre la lista de modelos.

  2. Si se mantiene el auto-sync, proteger provider.models en modelService.ts:750:

    // Solo reemplazar si el fetch devolvió modelos
    if (models.length > 0) {
      provider.models = [...models, ...userDefinedModels];
    } else if (userDefinedModels.length > 0) {
      // Mantener los modelos no-userDefined existentes y refrescar los user-defined
      const nonUserDefined = provider.models.filter(m => !m.userDefined);
      provider.models = [...nonUserDefined, ...userDefinedModels];
    }
    // si ambos están vacíos: no tocar provider.models
  3. Surface del error: cuando el fetch post-Guardar falla, mostrar un toast/alert explícito ("No se pudieron sincronizar modelos: [razón]. Tu selección anterior se mantiene.") en lugar de fallar silenciosamente.

  4. (UX, complementaria) Renombrar el botón a algo más específico — Guardar credenciales o Guardar API Key — para reforzar que su scope es solo URL/API Key. Añadir hint sobre que la selección de modelos se autoguarda.

Criterios de aceptación

  • Tras pulsar Guardar con un cambio en API Key/URL Base, los modelos previamente marcados siguen apareciendo en provider.models y en el selector del chat.
  • Si el fetch post-Guardar falla o devuelve vacío, el estado anterior (modelos + selecciones) se preserva.
  • Verificado en Local Provider (Ollama, LM Studio) y Vercel Gateway.
  • Verificado el caso de añadir API Key opcional a un Local Provider que no la requiere.
  • Texto del botón / hint clarifica que solo se guardan credenciales.

Archivos involucrados

  • src/renderer/pages/ModelPage/ProviderConfigs.tsx — handlers handleSave (líneas 117-123, 189-197).
  • src/renderer/services/modelService.ts_doSyncProviderModels (línea 750), _saveProviders (líneas 795-872).
  • src/renderer/stores/modelStore.tstoggleModelSelection, syncProviderModels.

Versión objetivo

v1.8.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv1.8.3Planned for v1.8.3 release

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions