Adding Per-Provider Model Selection to Account Settings

Why this setting needed to exist

The app already supported multiple AI providers, but model choice was still too hidden. I could switch providers, I could store API keys, and I could send requests through the right adapter. The missing piece was simple: each provider needed its own default model, controlled from account settings.

That sounds small, but it changes the way the tool feels. If I’m using OpenAI for one workflow, Anthropic for another, and a local model for quick drafts, I don’t want to pick the model every single time. I want the account to remember my preference per provider, then quietly use it whenever that provider is active.

So this build was about adding a per-provider model selector to the account settings screen. Not a flashy feature. More like one of those quality-of-life improvements that makes the whole app feel more finished.

The shape of the feature

I started by keeping the feature intentionally narrow. No advanced routing. No automatic benchmarking. No “smart” model switching. Just a clean setting that says, “For this provider, use this model by default.”

The first version needed to handle four things:

  • Show each connected provider in account settings.

  • Display a model dropdown for that provider.

  • Save the selected model to the user’s account settings.

  • Use that saved model whenever the provider is selected elsewhere in the app.

That last part matters. A settings screen that saves values but doesn’t affect the actual request flow is just decoration. I wanted the preference to travel all the way from the account UI into the message request payload.

Starting with a provider model registry

The first real decision was where the list of models should live. I did not want model names scattered across dropdown components, server handlers, and request builders. That always turns into cleanup debt later.

So I created a small provider model registry. Nothing fancy. Just one place where each provider maps to the models the app currently supports. The UI reads from it. Validation reads from it. Defaults read from it.

The structure looked roughly like this in my head:

ProviderExample model optionsDefault behavior
OpenAIGPT-style chat modelsPick the saved model or fall back to the app default
AnthropicClaude modelsPick the saved model or fall back to the app default
GoogleGemini modelsPick the saved model or fall back to the app default
LocalInstalled local modelsPick the saved model if it still exists

This gave me a single source of truth. When I add or remove a model later, I change it once and the settings page reflects it.

The local provider was the one that made me pause. Cloud providers usually have a known list, even if it changes over time. Local models can be different for every user. For this pass, I kept the local provider flexible. If the app can detect installed models, it uses that list. If not, it shows the currently saved model and lets the user keep it until the local model list is available.

Adding the setting to the account data

Next I needed a place to save the choices. The account settings already had a home for provider credentials and preferences, so I added a nested model preference object keyed by provider.

I went with this kind of shape:

  • Provider name as the key.

  • Selected model ID as the value.

  • No duplicate display labels in the saved data.

  • No provider-specific logic inside the database record.

That last one helped keep the data clean. The database should not need to know that one model is “fast” or another is “best for long context.” It just stores the user’s choice. The app can handle labels and descriptions at the display layer.

I also added a fallback path. If someone has no saved model for a provider yet, the app picks the provider’s default model from the registry. That meant existing users would not hit a blank dropdown or a broken request after the feature shipped.

The main rule for this build was simple: existing accounts should keep working even if they never touch the new setting.

Building the settings UI

The account settings screen already had provider sections, so I added the model selector inside each one. I wanted it to feel like a natural part of the provider setup, not a separate advanced panel buried somewhere else.

The first version was plain: a label, a dropdown, and a short helper line. Something like, “This model will be used by default when this provider is selected.” That’s enough. The user does not need a paragraph of explanation every time they change a dropdown.

I did spend a little time on the disabled states. If a provider is not connected, the model selector should not pretend everything is ready. It can still show the default model, but saving a model for a provider with no API key feels weird. So the selector only becomes active when the provider is available for that account.

That decision made the page easier to understand. Connected providers feel active. Unconnected ones feel informational. Small UI cue, big difference.

The first thing that broke

The first bug was a classic settings form mistake. I had multiple provider dropdowns on the page, but the local state update was too broad. Changing the OpenAI model briefly changed the visible value for another provider until the next render cleaned it up.

That told me the form state was not shaped like the data. The UI state needed to be keyed by provider too, not tracked as one loose selected model value.

Once I changed the form state to match the saved account structure, the bug disappeared. This is one of those small lessons I keep relearning: when state gets awkward, check whether it mirrors the thing you are actually editing.

Saving without making the page feel heavy

I tried two save patterns. The first was a page-level “Save settings” button. That worked, but it made each model change feel bigger than it really was. The second was saving on change with a small success state. That felt better for this feature.

I kept the feedback light. When the user picks a model, the app saves it, then shows a small saved indicator near the selector. If the request fails, the dropdown stays on the previous saved value and shows an error message.

I don’t like silent failures in settings screens. If the save fails and the UI pretends everything is fine, the user only finds out later when the wrong model gets used. That’s frustrating. Better to be honest right away.

Validating model choices on the server

The client can show the right dropdown options, but the server still has to check the incoming value. I added validation that checks three things before saving:

  • The provider exists.

  • The model is allowed for that provider.

  • The account has access to that provider.

This protects against stale UI, manual request edits, and future changes where a model gets removed from the registry. If validation fails, the server returns a clear error instead of saving bad data.

I also added a soft fallback for older saved settings. If a user had a saved model that no longer appears in the registry, the app does not crash. It marks the saved model as unavailable and uses the provider default for new requests. That way the account settings page can still load and give the user a chance to pick something current.

Connecting the setting to the request flow

This was the part I cared about most. The model selector only matters if the request builder respects it.

The request flow now checks the active provider, looks up the user’s saved model for that provider, validates it against the available list, and passes that model into the provider adapter. If there is no saved model, it uses the provider default.

I kept the lookup close to the place where provider settings are already resolved. That made the change easier to reason about. The request builder should not need to know about dropdowns or account page details. It only needs the final provider configuration: key, provider, model, and any related options.

After wiring that in, I tested it the simplest way possible. Pick one model, send a request, inspect the payload. Pick another model, send another request, inspect again. Not glamorous, but it caught an issue right away: one provider adapter was still using a hardcoded model name.

That was the second bug. The UI and account settings were correct, but the adapter ignored them. I removed the hardcoded value and made the adapter require an explicit model from the resolved provider config. That made the contract cleaner.

Handling provider-specific labels

Model IDs are often ugly. Users should not need to decode them. So the registry stores both a model ID and a display label. The saved setting stores the ID, while the dropdown shows the label.

I also added short descriptions for a few models where it helped. Not every model needs a full explanation, but short hints can help people choose faster. Things like “good for everyday chat,” “better for long context,” or “lower cost option” are more useful than marketing language.

I kept those descriptions optional. If every dropdown option turns into a mini product page, the settings screen gets noisy fast.

What the final account settings behavior looks like

The finished flow feels pretty smooth now:

  • The user opens account settings.

  • Each connected provider shows a model selector.

  • The dropdown is prefilled with the saved model or the provider default.

  • Changing the model saves the preference for that provider only.

  • Future requests through that provider use the saved model.

The best part is that switching one provider does not affect the others. That was the whole point. OpenAI can stay on one model. Anthropic can stay on another. A local provider can keep using whatever is installed. The account remembers each choice separately.

A small migration for existing accounts

I added a lightweight migration path for accounts that existed before this feature. There was no need to backfill every account with a full model preference object. Instead, the app treats missing model preferences as normal and fills in defaults at runtime.

That kept the rollout simple. New accounts can save model preferences right away. Existing accounts get the same defaults they were already using until they choose something else.

I like this pattern because it avoids forcing a database update just to support a preference that can be resolved safely in code.

Testing the edge cases

I tested the happy path first, then moved into the weird stuff. The weird stuff is where settings features usually get messy.

  • What happens if a provider is disconnected after a model is saved?

  • What happens if a model is removed from the registry?

  • What happens if the save request fails?

  • What happens if two dropdowns are changed quickly?

  • What happens if the account settings page loads before provider data finishes loading?

The provider disconnect case was easy. Keep the saved preference, but disable the selector until the provider is connected again. That way the user does not lose their choice just because they rotated an API key or temporarily removed access.

The rapid-change case needed a little care. I made sure each provider save request only updates the saved state for that provider. A slow response from one dropdown should not overwrite a newer selection from another one.

What I’d improve next

This version does the job, but it opens up a few useful follow-up ideas.

  • Add model capability tags, like vision, long context, tool calling, or low cost.

  • Show pricing hints when the provider makes that information easy to maintain.

  • Let workspaces override account-level defaults for team setups.

  • Add a test prompt button beside each provider so users can confirm the selected model works.

  • Support user-added custom model IDs for providers that release new models before the app registry is updated.

The custom model ID idea is probably next. It gives advanced users room to move while the registry stays helpful for everyone else. I would still validate provider access, but I would allow the model name to be manually entered when a provider supports it.

The main lesson from this build

This feature reminded me that settings are part of the product experience, not just admin plumbing. A model selector sounds like a small control, but it touches data shape, UI state, validation, request building, fallbacks, and error handling.

The cleanest move was making the provider model registry early. Once that existed, the rest of the work had a steady center. The UI could read from it. The server could validate against it. The request flow could rely on it. One simple decision saved a lot of scattered logic.

The feature is now live in account settings. Each provider can have its own selected model, saved per account, used automatically during requests, and protected by fallback behavior when something changes. Small win, but a real one.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top