From 276206ff4bcdbac43be081cdaecd95ef20c15c44 Mon Sep 17 00:00:00 2001 From: Alpha Date: Sat, 21 Mar 2026 11:20:01 +0800 Subject: [PATCH] fix: resolve bare alias to full model key in buildModelOptions When the agent config stored a model alias (e.g. 'k2p5') instead of the full qualified key (e.g. 'kimi-coding/k2p5'), buildModelOptions would add the bare alias as an option value. The backend would then resolve 'k2p5' using the default provider (minimax), producing 'minimax/k2p5' which is not in the allowed list. Now buildModelOptions reverse-matches the alias to the full key before using the value, ensuring the select always submits a complete provider/key pair. --- ui/src/ui/views/agents-utils.ts | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/ui/src/ui/views/agents-utils.ts b/ui/src/ui/views/agents-utils.ts index e0c06c41386..06a3514e8c3 100644 --- a/ui/src/ui/views/agents-utils.ts +++ b/ui/src/ui/views/agents-utils.ts @@ -576,9 +576,35 @@ export function buildModelOptions( current?: string | null, ) { const options = resolveConfiguredModels(configForm); - const hasCurrent = current ? options.some((option) => option.value === current) : false; - if (current && !hasCurrent) { - options.unshift({ value: current, label: `Current (${current})` }); + + // Resolve bare alias (e.g. "k2p5") → full key (e.g. "kimi-coding/k2p5") + // so the select value is always a complete provider/key, never a bare alias. + const resolvedCurrent = (() => { + if (!current) return null; + if (options.some((o) => o.value === current)) return current; + const cfg = configForm as ConfigSnapshot | null; + const models = cfg?.agents?.defaults?.models; + if (models && typeof models === "object") { + for (const [modelId, modelRaw] of Object.entries(models)) { + const alias = + modelRaw && typeof modelRaw === "object" && "alias" in modelRaw + ? typeof (modelRaw as { alias?: unknown }).alias === "string" + ? (modelRaw as { alias?: string }).alias?.trim() + : undefined + : undefined; + if (alias === current) { + return modelId.trim(); + } + } + } + return current; + })(); + + const hasCurrent = resolvedCurrent + ? options.some((option) => option.value === resolvedCurrent) + : false; + if (resolvedCurrent && !hasCurrent) { + options.unshift({ value: resolvedCurrent, label: `Current (${resolvedCurrent})` }); } if (options.length === 0) { return html`