fix: fallback and auth issues when configuring a duplicate model id

This commit is contained in:
mingholy.lmh 2026-01-07 20:05:33 +08:00
parent afe6ba255e
commit ded1ebcdff
4 changed files with 47 additions and 49 deletions

View file

@ -66,12 +66,14 @@ function hasApiKeyForAuth(
const defaultEnvKey = DEFAULT_ENV_KEYS[authType];
if (defaultEnvKey) {
const hasKey = !!process.env[defaultEnvKey];
return { hasKey, checkedEnvKey: defaultEnvKey };
if (hasKey) {
return { hasKey, checkedEnvKey: defaultEnvKey };
}
}
// Also check settings.security.auth.apiKey as fallback
if (settings.security?.auth?.apiKey) {
return { hasKey: true, checkedEnvKey: undefined };
return { hasKey: true, checkedEnvKey: defaultEnvKey || undefined };
}
return { hasKey: false, checkedEnvKey: undefined };

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type { Config } from '@qwen-code/qwen-code-core';
import type { Config, ModelProvidersConfig } from '@qwen-code/qwen-code-core';
import {
AuthEvent,
AuthType,
@ -152,6 +152,29 @@ export const useAuthCommand = (
[config, handleAuthSuccess, handleAuthFailure],
);
const isProviderManagedModel = useCallback(
(authType: AuthType, modelId: string | undefined) => {
if (!modelId) {
return false;
}
const modelProviders = settings.merged.modelProviders as
| ModelProvidersConfig
| undefined;
if (!modelProviders) {
return false;
}
const providerModels = modelProviders[authType];
if (!Array.isArray(providerModels)) {
return false;
}
return providerModels.some(
(providerModel) => providerModel.id === modelId,
);
},
[settings],
);
const handleAuthSelect = useCallback(
async (authType: AuthType | undefined, credentials?: OpenAICredentials) => {
if (!authType) {
@ -160,6 +183,20 @@ export const useAuthCommand = (
return;
}
if (
authType === AuthType.USE_OPENAI &&
credentials?.model &&
isProviderManagedModel(authType, credentials.model)
) {
onAuthError(
t(
'Model "{{modelName}}" is managed via settings.modelProviders. Please complete the fields in settings, or use another model id.',
{ modelName: credentials.model },
),
);
return;
}
setPendingAuthType(authType);
setAuthError(null);
setIsAuthDialogOpen(false);
@ -179,7 +216,7 @@ export const useAuthCommand = (
await performAuth(authType);
},
[config, performAuth],
[config, performAuth, isProviderManagedModel, onAuthError],
);
const openAuthDialog = useCallback(() => {

View file

@ -255,26 +255,10 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
);
} catch (e) {
const baseErrorMessage = e instanceof Error ? e.message : String(e);
// Some auth types (notably openai without modelProviders configured) can present
// env-based "raw" model IDs in the list. These are not registry-backed and will
// fail switchModel(). Fall back to setModel() to keep UX functional.
const isNotFound =
baseErrorMessage.includes('not found for authType') ||
(baseErrorMessage.includes('Model') &&
baseErrorMessage.includes('not found'));
if (!isNotFound) {
setErrorMessage(
`Failed to switch model to '${modelId}'.\n\n${baseErrorMessage}`,
);
// Keep the dialog open so the user can choose another model.
return;
}
await config.setModel(modelId, {
reason: 'user_manual',
context: 'Model set via /model dialog (raw)',
});
setErrorMessage(
`Failed to switch model to '${modelId}'.\n\n${baseErrorMessage}`,
);
return;
}
const event = new ModelSlashCommandEvent(modelId);
logModelSlashCommand(config, event);