feat: support authType on AI providers for IAM role authentication (#19016)

## Summary
- Adds an `authType` field (`'api_key' | 'access_key' | 'iam_role'`) to
AI provider config
- Providers like Amazon Bedrock that authenticate via IAM role (instance
profile) can now be registered without explicit API keys or access keys
- Backend: `isProviderConfigured()` checks `apiKey || accessKeyId ||
authType`
- Frontend admin panel: shows green "Configured" badge and "IAM role"
description for providers with `authType: "iam_role"`
- Provider detail page shows "IAM role (instance profile)" in the
credentials row

## Companion PR
- twentyhq/twenty-infra#528 — patches `authType: "iam_role"` into
dev/staging Bedrock catalogs

## Changed files
- **Backend**: new `AiProviderAuthType` type, `isProviderConfigured`
util, updated registry + resolver
- **Frontend**: new `AiProviderAuthType` type, updated provider list
card + detail page

## Test plan
- [ ] Deploy with a Bedrock catalog that includes `"authType":
"iam_role"` — verify Bedrock shows "Configured" in admin AI panel
- [ ] Verify OpenAI/Anthropic with `apiKey` still show "Configured"
- [ ] Verify a provider with no credentials and no `authType` still
shows "No credentials"

Made with [Cursor](https://cursor.com)
This commit is contained in:
Félix Malfait
2026-03-26 16:43:09 +01:00
committed by GitHub
parent 1c297e5ace
commit 7a341c6475
9 changed files with 26 additions and 5 deletions
@@ -26,6 +26,7 @@ import { isAutoSelectModelId } from 'twenty-shared/utils';
import { DEFAULT_MAX_OUTPUT_TOKENS } from 'src/engine/metadata-modules/ai/ai-models/types/default-max-output-tokens.const';
import { buildCompositeModelId } from 'src/engine/metadata-modules/ai/ai-models/utils/composite-model-id.util';
import { inferModelFamily } from 'src/engine/metadata-modules/ai/ai-models/utils/infer-model-family.util';
import { isProviderConfigured } from 'src/engine/metadata-modules/ai/ai-models/utils/is-provider-configured.util';
import {
isModelAllowedByWorkspace,
type WorkspaceModelAvailabilitySettings,
@@ -84,9 +85,7 @@ export class AiModelRegistryService {
continue;
}
const isConfigured = !!(config.apiKey || config.accessKeyId);
const sdkInstance = isConfigured
const sdkInstance = isProviderConfigured(config)
? this.sdkProviderFactory.createProvider(providerKey, config)
: undefined;
@@ -0,0 +1 @@
export type AiProviderAuthType = 'key' | 'credentials' | 'role';
@@ -1,5 +1,6 @@
import { type AiSdkPackage, type DataResidency } from 'twenty-shared/ai';
import { type AiProviderAuthType } from 'src/engine/metadata-modules/ai/ai-models/types/ai-provider-auth-type.type';
import { type AiProviderModelConfig } from 'src/engine/metadata-modules/ai/ai-models/types/ai-provider-model-config.type';
export type AiProviderConfig = {
@@ -7,6 +8,7 @@ export type AiProviderConfig = {
// Optional provider display/catalog name (e.g. models.dev label). Not a model name; per-model names live on `models[].name`.
name?: string;
label?: string;
authType?: AiProviderAuthType;
apiKey?: string;
baseUrl?: string;
region?: string;
@@ -0,0 +1,4 @@
import { type AiProviderConfig } from 'src/engine/metadata-modules/ai/ai-models/types/ai-provider-config.type';
export const isProviderConfigured = (config: AiProviderConfig): boolean =>
!!(config.apiKey || config.accessKeyId || config.authType);