fix(web): use stored API key when fetching models for saved providers (#2910)

When editing an existing model, the edit form initializes apiKey as
empty for security. This caused "Fetch Available Models" to reject with
"please enter API Key first" even though the key is saved server-side.

Add model_index support: the frontend passes the model's index to the
backend, which looks up the stored key from config. The key never leaves
the backend. Provider and API base are validated to prevent a stored key
from being sent to an unrelated endpoint.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guoguo
2026-05-21 15:51:45 +08:00
committed by GitHub
parent e7e21df354
commit 30938df40b
5 changed files with 153 additions and 9 deletions
@@ -804,6 +804,7 @@ export function EditModelSheet({
provider={canonicalProvider}
apiKey={form.apiKey}
apiBase={effectiveApiBase}
modelIndex={model?.index}
backendOptions={providerOptions}
/>
</>
@@ -30,6 +30,7 @@ interface FetchModelsDialogProps {
provider: string
apiKey: string
apiBase: string
modelIndex?: number
backendOptions?: ModelProviderOption[]
}
@@ -40,6 +41,7 @@ export function FetchModelsDialog({
provider,
apiKey,
apiBase,
modelIndex,
backendOptions,
}: FetchModelsDialogProps) {
const { t } = useTranslation()
@@ -52,6 +54,7 @@ export function FetchModelsDialog({
const canonicalProvider = getCanonicalProviderKey(provider, backendOptions)
const providerDef = getProviderCatalogMap(backendOptions).get(canonicalProvider)
const needsKey = providerDef?.requiresApiKey !== false
const hasKey = !!apiKey || modelIndex !== undefined
const handleFetch = useCallback(async () => {
setFetching(true)
@@ -63,6 +66,7 @@ export function FetchModelsDialog({
provider: canonicalProvider,
api_key: apiKey,
api_base: apiBase,
model_index: modelIndex,
})
setModels(res.models)
// Auto-select all by default
@@ -72,14 +76,14 @@ export function FetchModelsDialog({
} finally {
setFetching(false)
}
}, [canonicalProvider, apiKey, apiBase, t])
}, [canonicalProvider, apiKey, apiBase, modelIndex, t])
// Auto-fetch when dialog opens (skip if provider requires API key but none is set)
useEffect(() => {
if (open && provider && !(needsKey && !apiKey)) {
if (open && provider && !(needsKey && !hasKey)) {
handleFetch()
}
}, [open, provider, apiKey, needsKey, handleFetch])
}, [open, provider, hasKey, needsKey, handleFetch])
const handleFill = () => {
onFill(Array.from(selected))
@@ -140,7 +144,7 @@ export function FetchModelsDialog({
</DialogHeader>
<div className="space-y-3">
{needsKey && !apiKey && (
{needsKey && !hasKey && (
<div className="rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3 text-sm text-yellow-700 dark:text-yellow-400">
{t("models.fetch.needApiKey")}
</div>