+ {providerName === LLMFactory.AIMLAPI && (
+
+ )}
+
(null);
// Mirror of the per-instance model list - written by ModelsSection
// via `setModelInfo`, read by the payload builder.
+ const { t } = useTranslation();
const modelInfoRef = useRef([]);
// Provider-specific config: carries `verifyTransform` / `submitTransform`
@@ -189,13 +193,21 @@ const GenericProviderInstanceCard = forwardRef<
// catch it). For both drafts and saved cards, run the form's
// own validation so errors surface in the UI.
if (isDraft && !draftName.trim()) return false;
+ // List-model providers (list picker) require at least one selected model.
+ if (
+ LIST_MODEL_PROVIDERS.has(providerName) &&
+ modelInfoRef.current.length === 0
+ ) {
+ message.error(t('setting.selectModelBeforeVerify'));
+ return false;
+ }
const isValid = await formRef.current?.trigger();
return !!isValid;
},
getSavePayload,
markSaved,
}),
- [isDraft, draftName, getSavePayload, markSaved],
+ [isDraft, draftName, getSavePayload, markSaved, providerName, t],
);
return (
diff --git a/web/src/pages/user-setting/setting-model/provider-schema/constants.ts b/web/src/pages/user-setting/setting-model/provider-schema/constants.ts
index 23c194c260..b14ad76a16 100644
--- a/web/src/pages/user-setting/setting-model/provider-schema/constants.ts
+++ b/web/src/pages/user-setting/setting-model/provider-schema/constants.ts
@@ -30,6 +30,10 @@ import { LLMFactory } from '@/constants/llm';
* 4 model_* fields directly.
*/
export const LIST_MODEL_PROVIDERS = new Set([
+ // aimlapi.com serves its full /v1/models catalog (700+) via the AIMLAPI
+ // ModelMeta on the backend, so the dialog uses the list picker (mirror of
+ // that registry) instead of a single manual model_name field.
+ LLMFactory.AIMLAPI,
LLMFactory.Ollama,
LLMFactory.OpenRouter,
LLMFactory.VLLM,
diff --git a/web/src/pages/user-setting/setting-model/provider-schema/field-config/local-llm-configs.ts b/web/src/pages/user-setting/setting-model/provider-schema/field-config/local-llm-configs.ts
index 218e45aafb..ed0ffe91c2 100644
--- a/web/src/pages/user-setting/setting-model/provider-schema/field-config/local-llm-configs.ts
+++ b/web/src/pages/user-setting/setting-model/provider-schema/field-config/local-llm-configs.ts
@@ -24,6 +24,28 @@ import { buildModelInfoFromValues } from './utils';
* Used for scenarios after OllamaModal merge
*/
export const LocalLlmConfigs: Record = {
+ // aimlapi.com is a cloud OpenAI-compatible aggregator (like OpenRouter): it
+ // uses the list picker, so it needs a config whose submitTransform forwards
+ // `model_info` (the picker selection). Falling back to GenericApiKeyConfig
+ // dropped that field, so selected models were never persisted.
+ [LLMFactory.AIMLAPI]: buildLocalConfig(
+ LLMFactory.AIMLAPI,
+ 'aimlapi.com',
+ ['chat', 'embedding', 'image2text', 'tts', 'speech2text'],
+ undefined,
+ false,
+ [
+ {
+ name: 'base_url',
+ label: 'addLlmBaseUrl',
+ type: 'inputSelect',
+ required: false,
+ placeholder: 'baseUrlNameMessage',
+ shouldRender: 'hideWhenInstanceExists',
+ },
+ ],
+ 'https://docs.aimlapi.com/quickstart/simple-model',
+ ),
[LLMFactory.Ollama]: buildLocalConfig(
LLMFactory.Ollama,
'Ollama',
diff --git a/web/src/services/llm-service.ts b/web/src/services/llm-service.ts
index 228a6ade36..9d9fb84552 100644
--- a/web/src/services/llm-service.ts
+++ b/web/src/services/llm-service.ts
@@ -19,6 +19,8 @@ const {
patchInstanceModel,
deleteInstanceModels,
updateProviderInstance,
+ aimlapiAuthorizeStart,
+ aimlapiAuthorizePoll,
} = api;
const methods = {
@@ -94,6 +96,14 @@ const methods = {
url: updateProviderInstance,
method: 'put',
},
+ aimlapiAuthorizeStart: {
+ url: aimlapiAuthorizeStart,
+ method: 'post',
+ },
+ aimlapiAuthorizePoll: {
+ url: aimlapiAuthorizePoll,
+ method: 'post',
+ },
} as const;
const llmService = registerNextServer(methods);
diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts
index 1365be46fd..4e637a3613 100644
--- a/web/src/utils/api.ts
+++ b/web/src/utils/api.ts
@@ -26,6 +26,9 @@ export default {
// llm model
listAllAddedModels: `${restAPIv1}/models`,
defaultModel: `${restAPIv1}/models/default`,
+ // AIMLAPI agent-authorization (OAuth device grant) — obtain a key from the provider dialog
+ aimlapiAuthorizeStart: `${restAPIv1}/llm/aimlapi/authorize/start`,
+ aimlapiAuthorizePoll: `${restAPIv1}/llm/aimlapi/authorize/poll`,
listProviders: `${restAPIv1}/providers`,
addProvider: `${restAPIv1}/providers`,
addProviderInstance: ({ llm_factory }: { llm_factory: string }) =>
diff --git a/web/src/utils/common-util.ts b/web/src/utils/common-util.ts
index 8fb85f28ce..918bcc669f 100644
--- a/web/src/utils/common-util.ts
+++ b/web/src/utils/common-util.ts
@@ -43,6 +43,7 @@ export const formatNumberWithThousandsSeparator = (numberStr: string) => {
};
const orderFactoryList = [
+ LLMFactory.AIMLAPI,
LLMFactory.OpenAI,
LLMFactory.Moonshot,
LLMFactory.PPIO,