fix(config): add separate siliconflow_cn provider config field with fallback#2895
fix(config): add separate siliconflow_cn provider config field with fallback#2895idling11 wants to merge 1 commit into
Conversation
|
Thanks @idling11 for taking the time to contribute. This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in Please read |
There was a problem hiding this comment.
idling11 has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
Code Review
This pull request introduces support for the SiliconFlow China-hosted DeepSeek V4 (siliconflow-CN) provider, implementing fallback logic to the standard siliconflow configuration for unset fields. Feedback on the changes highlights critical compilation issues due to the use of the unstable let_chains feature on stable Rust. Additionally, the reviewer suggests adding lowercase siliconflow-cn deserialization aliases for robustness and refactoring the fallback resolution in crates/config to pre-resolve configuration values, which would significantly simplify downstream logic.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| #[serde(default)] | ||
| pub siliconflow: ProviderConfigToml, | ||
| #[serde(default)] | ||
| #[serde(alias = "siliconflow-CN", alias = "siliconflow_cn")] |
There was a problem hiding this comment.
Since the codebase uses both siliconflow-CN (uppercase) and siliconflow-cn (lowercase) in various places (e.g., in save_api_key_for and CLI commands), we should add siliconflow-cn as an alias to ensure robust deserialization regardless of the case used in the TOML file.
#[serde(alias = "siliconflow-CN", alias = "siliconflow-cn", alias = "siliconflow_cn")]| pub fireworks: ProviderConfig, | ||
| #[serde(default)] | ||
| pub siliconflow: ProviderConfig, | ||
| #[serde(default, alias = "siliconflow-CN", alias = "siliconflow_cn")] |
| if model_from_config.is_none() | ||
| && let ApiProvider::SiliconflowCn = provider | ||
| { | ||
| model_from_config = self | ||
| .providers | ||
| .as_ref() | ||
| .and_then(|p| p.siliconflow.model.as_deref()); | ||
| } |
There was a problem hiding this comment.
Using the unstable let_chains feature (&& let ...) is unnecessary here and will fail to compile on stable Rust. Since ApiProvider is a standard enum, we can use a simple == comparison instead.
if model_from_config.is_none() && provider == ApiProvider::SiliconflowCn {
model_from_config = self
.providers
.as_ref()
.and_then(|p| p.siliconflow.model.as_deref());
}| if provider_base.is_none() | ||
| && let ApiProvider::SiliconflowCn = provider | ||
| { | ||
| provider_base = self | ||
| .providers | ||
| .as_ref() | ||
| .and_then(|p| p.siliconflow.base_url.clone()); | ||
| } |
There was a problem hiding this comment.
| if let ApiProvider::SiliconflowCn = provider { | ||
| if let Some(configured) = self | ||
| .providers | ||
| .as_ref() | ||
| .and_then(|p| p.siliconflow.api_key.clone()) | ||
| && !configured.trim().is_empty() | ||
| { | ||
| return Ok(configured); | ||
| } | ||
| } |
There was a problem hiding this comment.
Avoid using the unstable let_chains feature (&& !configured.trim().is_empty()) which will fail to compile on stable Rust. We can use .as_deref() and .filter() to achieve this cleanly and efficiently without unnecessary cloning.
if provider == ApiProvider::SiliconflowCn {
if let Some(configured) = self
.providers
.as_ref()
.and_then(|p| p.siliconflow.api_key.as_deref())
.filter(|key| !key.trim().is_empty())
{
return Ok(configured.to_string());
}
}| let provider_cfg = self.providers.for_provider(provider); | ||
| // siliconflow-CN falls back to siliconflow for each field when unset, | ||
| // so users can configure one or both sections independently. | ||
| let siliconflow_fallback = (provider == ProviderKind::SiliconflowCN) | ||
| .then(|| self.providers.siliconflow.clone()) | ||
| .map(|cfg| ProviderConfigToml { | ||
| api_key: provider_cfg.api_key.clone().or(cfg.api_key), | ||
| base_url: provider_cfg.base_url.clone().or(cfg.base_url), | ||
| model: provider_cfg.model.clone().or(cfg.model), | ||
| ..provider_cfg.clone() | ||
| }); |
There was a problem hiding this comment.
Instead of creating a separate siliconflow_fallback variable and performing inline fallbacks in multiple places downstream, we can clone and pre-resolve provider_cfg with the fallback values right here. This simplifies the downstream logic significantly and keeps the code clean.
let mut provider_cfg = self.providers.for_provider(provider).clone();
if provider == ProviderKind::SiliconflowCN {
let fallback = &self.providers.siliconflow;
if provider_cfg.api_key.is_none() {
provider_cfg.api_key = fallback.api_key.clone();
}
if provider_cfg.base_url.is_none() {
provider_cfg.base_url = fallback.base_url.clone();
}
if provider_cfg.model.is_none() {
provider_cfg.model = fallback.model.clone();
}
}| let from_file = provider_cfg.api_key.clone().or(root_deepseek_api_key); | ||
| let from_file = if let Some(ref fb) = siliconflow_fallback { | ||
| from_file.or(fb.api_key.clone()) | ||
| } else { | ||
| from_file | ||
| }; | ||
| let explicit_api_key_for_endpoint = cli.api_key.as_deref().or(from_file.as_deref()); | ||
| let provider_base_url = if let Some(ref fb) = siliconflow_fallback { | ||
| provider_cfg.base_url.clone().or(fb.base_url.clone()) | ||
| } else { | ||
| provider_cfg.base_url.clone() | ||
| }; | ||
| let configured_base_url = cli | ||
| .base_url | ||
| .clone() | ||
| .or_else(|| env.base_url_for(provider)) | ||
| .or_else(|| provider_cfg.base_url.clone()) | ||
| .or_else(|| provider_base_url) | ||
| .or(root_deepseek_base_url); |
There was a problem hiding this comment.
With provider_cfg pre-resolved with fallback values, we can revert this block back to its original, simpler form.
let from_file = provider_cfg.api_key.clone().or(root_deepseek_api_key);
let explicit_api_key_for_endpoint = cli.api_key.as_deref().or(from_file.as_deref());
let configured_base_url = cli
.base_url
.clone()
.or_else(|| env.base_url_for(provider))
.or_else(|| provider_cfg.base_url.clone())
.or(root_deepseek_base_url);| .or_else(|| { | ||
| provider_cfg.model.clone().or_else(|| { | ||
| siliconflow_fallback | ||
| .as_ref() | ||
| .and_then(|fb| fb.model.clone()) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
idling11 has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
idling11 has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
idling11 has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
idling11 has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
idling11 has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
/lgtm |
…allback (Hmbown#2893) Split SiliconflowCN into its own [providers.siliconflow_cn] TOML section instead of silently ignoring [providers.siliconflow-CN] config. - ProvidersToml / ProvidersConfig: add siliconflow_cn field with serde alias - for_provider / for_provider_mut / provider_config_for: route SiliconflowCN to the new field - resolve_runtime_options_with_secrets: fallback siliconflow_cn → siliconflow for api_key / base_url / model when unset - deepseek_api_key: add config-file fallback for SiliconflowCn - provider_config_key: update metadata to "siliconflow_cn" - save_api_key_for: write SiliconflowCn keys to providers.siliconflow_cn - docs/PROVIDERS.md, config.example.toml, scripts/check-provider-registry.py
7f323e7 to
c17734b
Compare
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
The
ProviderKind::SiliconflowCNvariant existed but mapped to the same[providers.siliconflow]TOML section — setting only[providers.siliconflow-CN]was silently ignored.
Changes:
ProvidersToml/ProvidersConfig: addsiliconflow_cnfield with#[serde(alias = "siliconflow-CN")]so both sections work independentlyfor_provider()/for_provider_mut()/provider_config_for(): routeSiliconflowCNto the new field instead of sharingsiliconflowresolve_runtime_options_with_secrets(): add fallback chain —siliconflow_cn→siliconflowfor api_key / base_url / modeldeepseek_api_key()/deepseek_base_url()/default_model(): samefallback on the TUI side
save_api_key_for(): writeSiliconflowCNkeys to[providers.siliconflow-CN]config.example.toml: add[providers.siliconflow-CN]section with docsClose #2893
Testing
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-featurescargo test --workspace --all-featuresChecklist