Provider Model & Credential Management#
Overview#
Provider model and credential management in Dify is handled by two core modules:
api/core/entities/provider_configuration.py—ProviderConfigurationis the central entity for all CRUD and switch operations on provider credentials and custom model credentials.api/core/provider_manager.py—ProviderManagerassembles tenant-scopedProviderConfigurationsobjects and manages a Redis-backed DB row cache.
Key DB Models#
| Model | Purpose |
|---|---|
Provider | Tracks the active credential (credential_id) for a provider |
ProviderCredential | A saved provider-level credential (e.g., an API key for OpenAI) |
ProviderModel | The active custom model record, pointing to a ProviderModelCredential via credential_id |
ProviderModelCredential | A saved model-scoped credential for a custom model |
LoadBalancingModelConfig | Load-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
ProviderCredentialrow, optionally creates aProviderrow. - Update (
update_provider_credential): encrypts and saves credentials; clearsPROVIDERcache if the updated credential is currently active. - Delete (
delete_provider_credential): removes theProviderCredential, cascades to associatedLoadBalancingModelConfigrows (clearing their cache entries), and deletes theProviderrow if no credentials remain. - Switch (
switch_provider_credential): updatesProvider.credential_id, clears thePROVIDERcache, and sets the preferred provider type toCUSTOM.
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 aProviderModelCredential; if noProviderModelrow exists for this model, creates one pointing to the new credential. Clears theMODELcache for the active record. - Update (
update_custom_model_credential): saves new encrypted config; clearsMODELcache if this credential is the active one; syncs anyLoadBalancingModelConfigrows referencing it. - Delete credential (
delete_custom_model_credential): removes theProviderModelCredentialand linkedLoadBalancingModelConfigrows; if it was the last credential, deletes theProviderModelrecord; otherwise nullifiesProviderModel.credential_id. - Switch (
switch_custom_model_credential): updatesProviderModel.credential_idand clears theMODELcache. - Add credential to existing model (
add_model_credential_to_model): if theProviderModelrecord exists, switches to the new credential; if not, creates a newProviderModelrecord. - Delete model (
delete_custom_model): removes theProviderModelrecord and clears theMODELcache.
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 :
ProviderCredentialsCacheType | Identity ID |
|---|---|
PROVIDER | Provider.id |
MODEL | ProviderModel.id |
LOAD_BALANCING_MODEL | LoadBalancingModelConfig.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 :
| Source | Content |
|---|---|
provider_models | ProviderModel rows |
preferred_model_providers | TenantPreferredModelProvider rows |
provider_model_settings | ProviderModelSetting rows |
provider_model_credentials | ProviderModelCredential rows |
provider_credentials | ProviderCredential rows |
provider_load_balancing_configs | LoadBalancingModelConfig 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:
- When the active credential was deleted and a sibling credential still existed, the code cleared cache using
ProviderCredentialsCacheType.PROVIDERinstead ofMODEL, leaving theMODELcache entry stale. - When the last credential was deleted (deleting the
ProviderModelrecord), 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
ProviderModelrecord is deleted) - When a non-last credential is deleted (and
ProviderModel.credential_idis set toNone)
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#
| File | Role |
|---|---|
api/core/entities/provider_configuration.py | All credential/model CRUD and cache invalidation |
api/core/provider_manager.py | Tenant configuration assembly, DB row cache, provider alias handling |
api/core/helper/model_provider_cache.py | ProviderCredentialsCache — per-credential Redis cache |
| PR #38389 | Fix: orphaned credentials, alias deduplication, restore on missing record |
| PR #38577 | Fix: cache clearing on credential deletion (wrong cache type, missing invalidation) |
| Issue #38402 | Bug report tracking all four issues above |
| Issue #38381 | Duplicate provider entries report; root causes traced to #38402 |