Documentsdify
Provider Model & Credential Management
Provider Model & Credential Management
Type
Topic
Status
Published
Created
Jul 9, 2026
Updated
Jul 9, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Provider Model & Credential Management#

Overview#

Provider model and credential management in Dify is handled by two core modules:

Key DB Models#

ModelPurpose
ProviderTracks the active credential (credential_id) for a provider
ProviderCredentialA saved provider-level credential (e.g., an API key for OpenAI)
ProviderModelThe active custom model record, pointing to a ProviderModelCredential via credential_id
ProviderModelCredentialA saved model-scoped credential for a custom model
LoadBalancingModelConfigLoad-balancing entry referencing either a ProviderCredential or ProviderModelCredential


Credential Lifecycle#

Provider-Level Credentials#

Provider credentials are saved as ProviderCredential rows. The active one is referenced by Provider.credential_id.

Key operations on ProviderConfiguration:

  • Add: validates credentials, writes a ProviderCredential row, optionally creates a Provider row.
  • Update (update_provider_credential): encrypts and saves credentials; clears PROVIDER cache if the updated credential is currently active.
  • Delete (delete_provider_credential): removes the ProviderCredential, cascades to associated LoadBalancingModelConfig rows (clearing their cache entries), and deletes the Provider row if no credentials remain.
  • Switch (switch_provider_credential): updates Provider.credential_id, clears the PROVIDER cache, and sets the preferred provider type to CUSTOM.

Custom Model Credentials#

Custom models have their own model-scoped ProviderModelCredential rows. The active credential is stored in ProviderModel.credential_id.

Key operations:

  • Add (add_custom_model_credential): creates a ProviderModelCredential; if no ProviderModel row exists for this model, creates one pointing to the new credential. Clears the MODEL cache for the active record.
  • Update (update_custom_model_credential): saves new encrypted config; clears MODEL cache if this credential is the active one; syncs any LoadBalancingModelConfig rows referencing it.
  • Delete credential (delete_custom_model_credential): removes the ProviderModelCredential and linked LoadBalancingModelConfig rows; if it was the last credential, deletes the ProviderModel record; otherwise nullifies ProviderModel.credential_id.
  • Switch (switch_custom_model_credential): updates ProviderModel.credential_id and clears the MODEL cache.
  • Add credential to existing model (add_model_credential_to_model): if the ProviderModel record exists, switches to the new credential; if not, creates a new ProviderModel record.
  • Delete model (delete_custom_model): removes the ProviderModel record and clears the MODEL cache.

Cache Architecture#

There are two separate Redis cache layers.

1. Per-Credential Credentials Cache (model_provider_cache.py)#

ProviderCredentialsCache stores decrypted credential dicts, keyed by {cache_type}_credentials:tenant_id:{tenant_id}:id:{identity_id} with a 24-hour TTL.

Three cache types exist :

ProviderCredentialsCacheTypeIdentity ID
PROVIDERProvider.id
MODELProviderModel.id
LOAD_BALANCING_MODELLoadBalancingModelConfig.id

Cache is explicitly delete()d on every write or switch operation. Misuse of the wrong cache type leaves stale entries — see Known Bugs below.

2. Provider Configuration Source Cache (provider_manager.py)#

_ProviderConfigurationSourceCache caches DB row projections in Redis with a 5-minute TTL, keyed by source type and a monotonic version counter.

Six sources are tracked :

SourceContent
provider_modelsProviderModel rows
preferred_model_providersTenantPreferredModelProvider rows
provider_model_settingsProviderModelSetting rows
provider_model_credentialsProviderModelCredential rows
provider_credentialsProviderCredential rows
provider_load_balancing_configsLoadBalancingModelConfig rows

Invalidation is version-based: invalidate_tenant() increments the version counter for the affected source(s) via Redis INCR, rendering any cached payload under the old version key unreachable.

ProviderConfiguration._invalidate_provider_configuration_cache() calls _ProviderConfigurationSourceCache.invalidate_tenant() with the appropriate subset of sources after every mutating operation.


Known Bugs & Fixes#

1. Orphaned Credentials After delete_custom_model (Stale Reappearance)#

Root cause: The pre-fix delete_custom_model deleted only the ProviderModel row but left ProviderModelCredential records intact. Because the provider configuration can be rebuilt from those credentials, deleted models reappeared after a page refresh.

Fix (PR #38389): Delete all ProviderModelCredential rows and related LoadBalancingModelConfig rows when removing a custom model.

2. Wrong Cache Type on Credential Deletion ✅ FIXED#

Root cause: The pre-fix delete_custom_model_credential had two cache clearing issues:

  1. When the active credential was deleted and a sibling credential still existed, the code cleared cache using ProviderCredentialsCacheType.PROVIDER instead of MODEL, leaving the MODEL cache entry stale.
  2. When the last credential was deleted (deleting the ProviderModel record), no cache clearing occurred at all.

Fix (PR #38577): The fix ensures ProviderCredentialsCache is now properly cleared with cache_type=ProviderCredentialsCacheType.MODEL in both cases:

  • When the last credential is deleted (and the ProviderModel record is deleted)
  • When a non-last credential is deleted (and ProviderModel.credential_id is set to None)

The fix captures model_credentials_cache_identity_id (the ProviderModel.id) before any deletion logic, so the cache can be properly cleared regardless of which path is taken.

3. Duplicate Custom Models from Provider Aliases#

Root cause: _get_custom_model_record resolves both the canonical provider name and legacy alias names. The same custom model can appear twice if credentials were saved under both names.

Fix (PR #38389): Deduplicate custom model entries resolved through canonical and legacy provider aliases in provider_manager.py.

4. Credential Switch Fails When ProviderModel Record Is Missing#

Root cause: switch_custom_model_credential requires an existing ProviderModel record and raises "The custom model record not found" if it is absent — which can happen after model deletion leaves orphaned credentials.

Fix (PR #38389): add_model_credential_to_model handles missing ProviderModel records by creating one; callers should prefer this method for restore flows.


Key Entry Points#

FileRole
api/core/entities/provider_configuration.pyAll credential/model CRUD and cache invalidation
api/core/provider_manager.pyTenant configuration assembly, DB row cache, provider alias handling
api/core/helper/model_provider_cache.pyProviderCredentialsCache — per-credential Redis cache
PR #38389Fix: orphaned credentials, alias deduplication, restore on missing record
PR #38577Fix: cache clearing on credential deletion (wrong cache type, missing invalidation)
Issue #38402Bug report tracking all four issues above
Issue #38381Duplicate provider entries report; root causes traced to #38402
Provider Model & Credential Management | Dosu