Clear provider-specific kwargs on provider change

Reset additional parameters together with the custom API base when a model provider changes, preventing stale provider-specific options from leaking into the new provider. Update the model-config contract and README, and extend the focused frontend regression check.

Alessandro committed Jul 10, 2026 at 19:11 UTC 948409c2449e5d1998b0561e5ab640bdc9369776
4 files changed +7 -4
plugins/_model_config/AGENTS.md
+1
@@ -19,6 +19,7 @@
19 - Coordinate OAuth-backed providers with `_oauth` instead of hardcoding provider-specific auth here.
20 - `model_config_get` exposes `model_configured` as a derived chat-model readiness flag from provider, model name, and API-key availability.
21 - Applying a model preset may inherit durable tuning such as context windows and rate limits, but must replace or clear per-slot `kwargs` so provider-specific extra params never leak across model providers.
22 +- Changing a model provider in the settings UI must clear `api_base` and `kwargs` because both may be provider-specific.
23 - Repair provider-specific model-config aliases at the model-config read/build boundary; keep provider-specific repairs out of provider-agnostic core wrappers such as `models.py`.
24 - `modelConfig.createPresetEditor()` owns local preset drafts, row actions, and stable UI-only row keys so deletion or renaming cannot rebind nested model fields.
25 - Model-name catalogs open below the input from either a field click or the embedded magnifier.
plugins/_model_config/README.md
+1 -1
@@ -77,7 +77,7 @@ The project preset file uses the same plain YAML list schema as global presets.
77 name: openai/gpt-5.4-mini
78 ```
79
80 -Preset slots are partial overlays. Missing fields inherit from the current effective config, so a preset can switch only the model identity while preserving tuned context windows, rate limits, and nested `kwargs`. The `utility` and `embedding` slots are optional and only apply when they declare a provider or model name; otherwise those configured models are inherited. Selecting a preset for a project writes the merged result into the project's `config.json`.
80 +Preset slots are partial overlays. Missing fields inherit from the current effective config, so a preset can switch only the model identity while preserving tuned context windows and rate limits. Provider-specific `kwargs` are replaced or cleared. The `utility` and `embedding` slots are optional and only apply when they declare a provider or model name; otherwise those configured models are inherited. Selecting a preset for a project writes the merged result into the project's `config.json`.
81
82 ## Plugin Metadata
83
plugins/_model_config/webui/model-field.html
+1 -1
@@ -28,7 +28,7 @@
28 <div class="field-control">
29 <select x-model="model.provider"
30 x-effect="$nextTick(() => { if (providers.length) $el.value = model.provider })"
31 - @change="model.api_base = ''">
31 + @change="model.api_base = ''; model.kwargs = {}; model._kwargs_text = ''">
32 <option value="">&mdash; select &mdash;</option>
33 <template x-for="p in providers" :key="p.value">
34 <option :value="p.value" x-text="p.label"></option>
tests/test_model_config_api_keys.py
+4 -2
@@ -231,7 +231,7 @@ def test_connector_model_switcher_notifies_state_sync(monkeypatch):
231 assert calls == [("ctx-1", "a0_connector.model_switcher")]
232
233
234 -def test_model_config_provider_switch_resets_custom_api_base():
234 +def test_model_config_provider_switch_resets_provider_specific_fields():
235 model_field_path = PROJECT_ROOT / "plugins" / "_model_config" / "webui" / "model-field.html"
236 content = model_field_path.read_text(encoding="utf-8")
237 select_start = content.index('<select x-model="model.provider"')
@@ -239,7 +239,9 @@ def test_model_config_provider_switch_resets_custom_api_base():
239 provider_select = content[select_start:select_end]
240
241 assert 'x-model="model.provider"' in provider_select
242 - assert '@change="model.api_base = \'\'"' in provider_select
242 + assert "model.api_base = ''" in provider_select
243 + assert "model.kwargs = {}" in provider_select
244 + assert "model._kwargs_text = ''" in provider_select
245
246
247 def test_model_config_model_field_opens_search_on_click():