fix(config): model_list inherits api_key/api_base from providers#1786
fix(config): model_list inherits api_key/api_base from providers#1786yinwm merged 1 commit intosipeed:mainfrom
Conversation
When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
yinwm
left a comment
There was a problem hiding this comment.
Review Summary
This PR cleanly solves the credential inheritance problem (#1635) where users had to duplicate / in every entry even when already configured in .
What I checked:
Pass 1 (Critical):
- ✅ No SQL/data safety concerns (config parsing only)
- ✅ No race conditions (single-pass config loading)
- ✅ No LLM output trust boundary issues
- ✅ Enum completeness: covers all 25 supported protocols
Pass 2 (Informational):
- ✅ No conditional side effects missing
- ✅ No magic numbers or string coupling issues
- ✅ No dead code
- ✅ Test coverage: 6 new tests covering:
- Credential inheritance from providers
- Explicit values taking precedence
- Multiple models with mixed configs
- Unknown protocol prefix (no error, no inheritance)
- Empty providers (no crash)
- Request timeout inheritance
Design highlights:
- Backward compatible: Purely additive, existing configs unaffected
- DRY principle: Explicit values always win
- Clean implementation: ~80 lines of straightforward logic with good comments
Verdict: LGTM, ready to merge. 🚀
…eed#1786) When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
|
@sliverp Solid work on the credential inheritance. Having model_list entries silently fail because they don't inherit api_key from the matching provider is a frustrating gotcha for users. The protocol-to-provider mapping covering all 25 protocols is thorough, and the 6 test cases cover the edge cases well. Clean fix for #1635. We're running a PicoClaw Dev Group on Discord for contributors to collaborate more directly. If you'd like to join, send an email to |
…eed#1786) When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
…eed#1786) When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
…eed#1786) When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
…eed#1786) When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
…eed#1786) When both providers and model_list are configured, model_list entries with empty api_key or api_base now automatically inherit from the matching provider (matched by protocol prefix in the Model field). Example: a model_list entry with model='deepseek/deepseek-chat' and no api_key will inherit from providers.deepseek.api_key. Explicit model_list values always take precedence. Changes: - Add InheritProviderCredentials() in migration.go - Call it in LoadConfig() after provider-to-model-list conversion - Add protocolProviderMapping for all 25 supported protocols - 6 new tests covering inheritance, precedence, and edge cases Closes sipeed#1635
Summary
When users configure credentials in the
providerssection and define models inmodel_list, the model entries do not inheritapi_key/api_basefrom the matching provider. Users must duplicate credentials in everymodel_listentry, violating DRY and causing silent failures when credentials are only set inproviders.Problem
As reported in #1635:
{ "providers": { "deepseek": { "api_key": "sk-my-key", "api_base": "https://api.deepseek.com/v1" } }, "model_list": [ { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat" } ] }The model entry has no
api_key— even thoughproviders.deepseekhas one.CreateProviderFromConfigsees an emptycfg.APIKeyand returns an error. Users expect the protocol prefix (deepseek/) to match the provider automatically.Solution
Add
InheritProviderCredentials()that runs duringLoadConfig, after the existingConvertProvidersToModelListmigration. For eachmodel_listentry:Modelfield (e.g.deepseekfromdeepseek/deepseek-chat)ProvidersConfigvia a protocol→provider mappingapi_key,api_base,proxy, andrequest_timeoutfrom the providerExplicit
model_listvalues always take precedence — only empty fields are filled.What changed
migration.go: NewInheritProviderCredentials()function +protocolProviderMappingcovering all 25 supported protocolsconfig.go:LoadConfig()callsInheritProviderCredentials()after provider migrationmigration_test.go: 6 new testsWhat did NOT change
ConvertProvidersToModelListlogic is unchangedCreateProviderFromConfigfactory is unchangedTests
6 new tests added:
FillsMissingAPIKeyExplicitValuesTakePrecedenceMultipleModelsNoMatchingProviderEmptyProvidersInheritsRequestTimeoutBreaking Changes
None. This is purely additive. Existing configs that already set
api_keyon everymodel_listentry are unaffected (explicit values always win).Closes #1635