use super::*;
impl ProviderService {
pub(crate) fn normalize_claude_models_in_value(settings: &mut Value) -> bool {
let mut changed = false;
let env = match settings.get_mut("env") {
Some(v) if v.is_object() => v.as_object_mut().unwrap(),
_ => return changed,
};
let model = env
.get("ANTHROPIC_MODEL")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let small_fast = env
.get("ANTHROPIC_SMALL_FAST_MODEL")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let current_haiku = env
.get("ANTHROPIC_DEFAULT_HAIKU_MODEL")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let current_sonnet = env
.get("ANTHROPIC_DEFAULT_SONNET_MODEL")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let current_opus = env
.get("ANTHROPIC_DEFAULT_OPUS_MODEL")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let target_haiku = current_haiku
.or_else(|| small_fast.clone())
.or_else(|| model.clone());
let target_sonnet = current_sonnet
.or_else(|| model.clone())
.or_else(|| small_fast.clone());
let target_opus = current_opus
.or_else(|| model.clone())
.or_else(|| small_fast.clone());
if env.get("ANTHROPIC_DEFAULT_HAIKU_MODEL").is_none() {
if let Some(v) = target_haiku {
env.insert(
"ANTHROPIC_DEFAULT_HAIKU_MODEL".to_string(),
Value::String(v),
);
changed = true;
}
}
if env.get("ANTHROPIC_DEFAULT_SONNET_MODEL").is_none() {
if let Some(v) = target_sonnet {
env.insert(
"ANTHROPIC_DEFAULT_SONNET_MODEL".to_string(),
Value::String(v),
);
changed = true;
}
}
if env.get("ANTHROPIC_DEFAULT_OPUS_MODEL").is_none() {
if let Some(v) = target_opus {
env.insert("ANTHROPIC_DEFAULT_OPUS_MODEL".to_string(), Value::String(v));
changed = true;
}
}
if env.remove("ANTHROPIC_SMALL_FAST_MODEL").is_some() {
changed = true;
}
changed
}
pub(super) fn normalize_provider_if_claude(app_type: &AppType, provider: &mut Provider) {
if matches!(app_type, AppType::Claude) {
let mut v = provider.settings_config.clone();
if Self::normalize_claude_models_in_value(&mut v) {
provider.settings_config = v;
}
}
}
pub(super) fn prepare_switch_claude(
config: &mut MultiAppConfig,
provider_id: &str,
effective_current_provider: Option<&str>,
) -> Result<Provider, AppError> {
let provider = config
.get_manager(&AppType::Claude)
.ok_or_else(|| Self::app_not_found(&AppType::Claude))?
.providers
.get(provider_id)
.cloned()
.ok_or_else(|| {
AppError::localized(
"provider.not_found",
format!("供应商不存在: {provider_id}"),
format!("Provider not found: {provider_id}"),
)
})?;
Self::backfill_claude_current(config, provider_id, effective_current_provider)?;
if let Some(manager) = config.get_manager_mut(&AppType::Claude) {
manager.current = provider_id.to_string();
}
Ok(provider)
}
pub(super) fn backfill_claude_current(
config: &mut MultiAppConfig,
next_provider: &str,
effective_current_provider: Option<&str>,
) -> Result<(), AppError> {
let settings_path = get_claude_settings_path();
if !settings_path.exists() {
return Ok(());
}
let current_id = effective_current_provider.unwrap_or_default();
if current_id.is_empty() || current_id == next_provider {
return Ok(());
}
let current_provider = config
.get_manager(&AppType::Claude)
.and_then(|manager| manager.providers.get(current_id))
.cloned();
let Some(current_provider) = current_provider else {
return Ok(());
};
let mut live = read_json_file::<Value>(&settings_path)?;
let _ = Self::normalize_claude_models_in_value(&mut live);
live = common_config::strip_common_config_from_live_settings(
&AppType::Claude,
¤t_provider,
live,
config.common_config_snippets.claude.as_deref(),
);
if let Some(manager) = config.get_manager_mut(&AppType::Claude) {
if let Some(current) = manager.providers.get_mut(current_id) {
current.settings_config = live;
}
}
Ok(())
}
pub(super) fn migrate_claude_common_config_snippet(
config: &mut MultiAppConfig,
old_snippet: &str,
) -> Result<(), AppError> {
let old_snippet = old_snippet.trim();
if old_snippet.is_empty() {
return Ok(());
}
let Some(manager) = config.get_manager_mut(&AppType::Claude) else {
return Ok(());
};
for provider in manager.providers.values_mut() {
common_config::normalize_provider_common_config_for_storage(
&AppType::Claude,
provider,
Some(old_snippet),
)?;
}
Ok(())
}
pub(super) fn write_claude_live(
provider: &Provider,
common_config_snippet: Option<&str>,
apply_common_config: bool,
) -> Result<(), AppError> {
if !crate::sync_policy::should_sync_live(&AppType::Claude) {
return Ok(());
}
let settings_path = get_claude_settings_path();
let content_to_write = Self::build_effective_live_snapshot(
&AppType::Claude,
provider,
common_config_snippet,
apply_common_config,
)?;
write_json_file(&settings_path, &content_to_write)?;
Ok(())
}
}