Restructure the Model providers settings page around providers instead of
per-model connections (OpenHands/OpenHands#15492):
- Connect a provider once with a single API key; the key is held on the
provider as a named secret server-side and never returned to the client
(only an api_key_set boolean is exposed).
- Models are nested under a provider card and are individually editable and
removable, with a + affordance to add a model to the same provider.
- Add-provider preset picker (Custom endpoint, OpenAI, Anthropic, Azure
OpenAI, Foundry Local, Microsoft Foundry) opens a provider form.
Renames provider-connections -> model-providers across service, hooks,
constants, components, mocks and tests. Simplifies onboarding to the
raw-key profile save path. Adds provider i18n keys across all languages.
Co-authored-by: openhands <openhands@all-hands.dev>
- Clear/deselect in the wizard picker was immediately undone because the
default-selection effect re-selected recommended models whenever the
selection became empty. Run the default selection exactly once per
pick-step entry via a ref instead.
- Refresh/Test connection now send live=true so validation performs a real
key probe and the backend's verified flag is meaningful, matching the
intended 'honest validation' behavior.
- Add a regression test for the Clear bulk action.
Co-authored-by: openhands <openhands@all-hands.dev>
Implements the full Connect-a-Provider flow the issue describes.
Wizard (4-step):
- Step 1: vendor dropdown + masked key + helper line (save once, reuse,
rotate later).
- Step 2: test connection on key-blur or button; green-check success with
model count; inline error with Try again / Use a different key actions;
defensive couldnt-fetch-catalog path.
- Step 3: model picker with checkboxes, Recommended (verified) tag, default
selection = all recommended pre-checked, bulk actions (Select all
recommended / Select all / Clear), and a More from vendor collapsible for
non-recommended models.
- Step 4: save creates one LLM profile per selected model via the backend
POST /connections/{id}/profiles endpoint (key bound by reference), with a
confirmation summary and toast.
Connections section row now matches the wireframe:
Provider - N models - last refreshed X ago, with Refresh, Rotate key,
Disconnect actions.
- Renamed the action from Delete to Disconnect (issue label).
- Added Refresh (re-validate, live) and Rotate key (inline key-rotation
input that PATCHes the connection key) actions.
- Renders last-refreshed relative time from lastValidatedAt.
Service/mutations/i18n:
- ProviderConnectionsService.createProfileFromConnection + types + normalizer.
- useCreateProfileFromConnection mutation hook.
- 22 new i18n keys across all 15 locales; declaration regenerated.
- MSW handler for POST /connections/{id}/profiles.
Tests: wizard (7), service (13), local-view (24) - all green; lint + prettier
+ typecheck + translation-completeness hooks pass.
Co-authored-by: openhands <openhands@all-hands.dev>
- Connect wizard no longer leaves a half-connected record behind: it tracks the
connection it created, rotates that same record's key on retry (instead of
creating a duplicate every attempt), and deletes it on cancel/dismiss when
validation never succeeded
- service/UI carry the backend's `verified` flag so a catalog-only lookup is not
presented as an authenticated key; validateConnection gains a { live } option
that forwards ?live=true
- deleteConnection returns the affected profiles; the connections section warns
when disconnecting a key that profiles still reference
- MSW handlers updated to the new delete/validate response shapes
- remove the leftover draft scaffold test placeholder
Co-authored-by: openhands <openhands@all-hands.dev>
Consolidates the three frontend PRs (service, UI, i18n) into one.
- provider-connections-service.ts: raw-fetch service (local-only; throws
ProviderConnectionsNotOnCloudError on cloud), snake/camel normalization,
key never echoed back (only api_key_set boolean)
- use-provider-connections query hook + create/update/delete/validate
mutation hooks, query keys
- MSW handlers for all 6 endpoints (in-memory Map, key stripped)
- ConnectProviderWizard modal (provider select -> key -> label -> validate
shows catalog the key grants)
- ProviderConnectionsSection (list + re-validate + delete-confirm, cloud
auto-hides via assertConnectionsSupportedLocally)
- Wired into llm-settings-local-view above LlmProfilesManager
- 24 i18n keys, fully translated across all 15 languages
- Path constants in constants/provider-connections.ts (kept out of the
service file to satisfy no-direct-agent-server-calls guard)
- 10 service tests + 3 wizard tests; existing LlmSettingsLocalView test
mocks the new hooks/wizard
Co-authored-by: openhands <openhands@all-hands.dev>