Move model config snapshot sync into store
Keep model config markup focused on setup and let the store own settings-object lifecycle hooks. This initializes UI-only kwargs fields after scope loads while preserving dirty tracking for reset/default changes.
Alessandro committed
Jun 2, 2026 at 00:29 UTC
b152030a951757e3575c529a7049dab58ce340d1
3 files changed
+44
-20
plugins/_model_config/webui/config.html
+2
-14
@@ -7,24 +7,12 @@
7
</head>
8
9
<body>
10
- <div x-data="{ lastConfig: null }"
11
- x-effect="
12
- if (config && config !== lastConfig) {
13
- lastConfig = config;
14
- const snapshotBeforeInit = typeof context !== 'undefined'
15
- ? context._toComparableJson(config)
16
- : null;
17
- $store.modelConfig.initConfigFields(config);
18
- if (typeof context !== 'undefined' && context.settingsSnapshotJson === snapshotBeforeInit) {
19
- context.settingsSnapshotJson = context._toComparableJson(config);
20
- }
21
- }
22
- "
10
+ <div x-data
11
x-init="
12
await $store.modelConfig.ensureLoaded();
13
$store.modelConfig.resetApiKeyDrafts();
14
await $store.modelConfig.refreshApiKeyStatus();
27
- $store.modelConfig.installSettingsHooks(context, config);
15
+ $store.modelConfig.installSettingsHooks(context);
16
">
17
<template x-if="config && $store.modelConfig._loaded">
18
<div class="model-config-sections">
plugins/_model_config/webui/model-config-store.js
+36
-4
@@ -235,6 +235,25 @@ export const store = createStore("modelConfig", {
235
if (config?.embedding_model) config.embedding_model._kwargs_text = kwargsToText(config.embedding_model.kwargs);
236
},
237
238
+ syncContextConfigFields(context, refreshCleanSnapshot = false) {
239
+ const config = context?.settings;
240
+ if (!config || typeof config !== 'object') return;
241
+
242
+ const snapshotBeforeInit = refreshCleanSnapshot && typeof context._toComparableJson === 'function'
243
+ ? context._toComparableJson(config)
244
+ : null;
245
+
246
+ this.initConfigFields(config);
247
+
248
+ if (
249
+ refreshCleanSnapshot &&
250
+ typeof context._toComparableJson === 'function' &&
251
+ context.settingsSnapshotJson === snapshotBeforeInit
252
+ ) {
253
+ context.settingsSnapshotJson = context._toComparableJson(config);
254
+ }
255
+ },
256
+
257
// Global presets
258
async loadGlobalPresets() {
259
try {
@@ -300,13 +319,25 @@ export const store = createStore("modelConfig", {
319
},
320
321
/**
303
- * Install save and reset hooks on the plugin settings context.
304
- * - Save: persists dirty API keys before the normal config save.
305
- * - Reset: reloads global presets when settings are reset to defaults.
322
+ * Install hooks on the plugin settings context.
323
+ * - Load/reset: initialize UI-only model fields when the settings object changes.
324
+ * - Save: persist dirty API keys before the normal config save.
325
+ * - Reset: reload global presets when settings are reset to defaults.
326
*/
307
- installSettingsHooks(context, config) {
327
+ installSettingsHooks(context) {
328
if (!context || context.__modelConfigHooksInstalled) return;
329
330
+ this.syncContextConfigFields(context, true);
331
+
332
+ const originalLoadSettings = context.loadSettings?.bind(context);
333
+ if (originalLoadSettings) {
334
+ context.loadSettings = async (...args) => {
335
+ const result = await originalLoadSettings(...args);
336
+ this.syncContextConfigFields(context, true);
337
+ return result;
338
+ };
339
+ }
340
+
341
const originalSave = context.save.bind(context);
342
context.save = async () => {
343
context.error = null;
@@ -324,6 +355,7 @@ export const store = createStore("modelConfig", {
355
const before = context.settings;
356
await originalReset();
357
if (context.settings !== before) {
358
+ this.syncContextConfigFields(context);
359
await this.resetGlobalPresets();
360
}
361
};
tests/test_model_config_api_keys.py
+6
-2
@@ -120,10 +120,14 @@ def test_model_config_frontend_tracks_inline_api_key_edits():
120
121
def test_model_config_snapshot_sync_only_adjusts_clean_loaded_configs():
122
config_path = PROJECT_ROOT / "plugins" / "_model_config" / "webui" / "config.html"
123
+ store_path = PROJECT_ROOT / "plugins" / "_model_config" / "webui" / "model-config-store.js"
124
config_content = config_path.read_text(encoding="utf-8")
125
+ store_content = store_path.read_text(encoding="utf-8")
126
125
- assert "const snapshotBeforeInit" in config_content
126
- assert "context.settingsSnapshotJson === snapshotBeforeInit" in config_content
127
+ assert "x-effect" not in config_content
128
+ assert "syncContextConfigFields(context, true)" in store_content
129
+ assert "context.loadSettings = async" in store_content
130
+ assert "context.settingsSnapshotJson === snapshotBeforeInit" in store_content
131
132
133
def test_model_switcher_frontend_renders_custom_overrides():