Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Kimi Code API-key setup.** `codewhale config set providers.moonshot.*`
now writes the Moonshot/Kimi provider table, and Kimi Code API-key
endpoints default to `kimi-for-coding` without using the Kimi CLI OAuth path.
A root DeepSeek `default_text_model` no longer overrides that Kimi Code
model when switching providers.

## [0.8.45] - 2026-05-25

Expand Down
2 changes: 2 additions & 0 deletions crates/tui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Kimi Code API-key setup.** `codewhale config set providers.moonshot.*`
now writes the Moonshot/Kimi provider table, and Kimi Code API-key
endpoints default to `kimi-for-coding` without using the Kimi CLI OAuth path.
A root DeepSeek `default_text_model` no longer overrides that Kimi Code
model when switching providers.

## [0.8.45] - 2026-05-25

Expand Down
62 changes: 49 additions & 13 deletions crates/tui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,19 @@ impl Config {
}
}
}
let moonshot_config = (provider == ApiProvider::Moonshot)
.then(|| self.provider_config())
.flatten();
let moonshot_uses_kimi_code = moonshot_config.is_some_and(|config| {
provider_config_uses_kimi_oauth(config)
|| config
.base_url
.as_deref()
.is_some_and(moonshot_base_url_uses_kimi_code)
});
if moonshot_uses_kimi_code {
return DEFAULT_KIMI_CODE_MODEL.to_string();
}
if let Some(model) = self.default_text_model.as_deref()
&& (provider_passes_model_through(provider)
|| self.active_provider_preserves_custom_base_url_model())
Expand All @@ -1570,19 +1583,6 @@ impl Config {
{
return model_for_provider(provider, normalized);
}
let moonshot_config = (provider == ApiProvider::Moonshot)
.then(|| self.provider_config())
.flatten();
let moonshot_uses_kimi_code = moonshot_config.is_some_and(|config| {
provider_config_uses_kimi_oauth(config)
|| config
.base_url
.as_deref()
.is_some_and(moonshot_base_url_uses_kimi_code)
});
if moonshot_uses_kimi_code {
return DEFAULT_KIMI_CODE_MODEL.to_string();
}

match provider {
ApiProvider::Deepseek | ApiProvider::DeepseekCN => DEFAULT_TEXT_MODEL,
Expand Down Expand Up @@ -6484,6 +6484,42 @@ base_url = "https://api.kimi.com/coding/v1"
Ok(())
}

#[test]
fn moonshot_kimi_code_model_overrides_root_deepseek_default() -> Result<()> {
let _lock = lock_test_env();
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let temp_root = env::temp_dir().join(format!(
"codewhale-tui-kimi-code-root-model-{}-{}",
std::process::id(),
nanos
));
fs::create_dir_all(&temp_root)?;
let _guard = EnvGuard::new(&temp_root);

let config_path = temp_root.join(".deepseek").join("config.toml");
ensure_parent_dir(&config_path)?;
fs::write(
&config_path,
r#"provider = "deepseek"
default_text_model = "deepseek-v4-pro"

[providers.moonshot]
api_key = "kimi-code-key"
base_url = "https://api.kimi.com/coding/v1"
"#,
)?;
unsafe { env::set_var("DEEPSEEK_PROVIDER", "moonshot") };

let config = Config::load(None, None)?;
assert_eq!(config.api_provider(), ApiProvider::Moonshot);
assert_eq!(config.deepseek_base_url(), DEFAULT_KIMI_CODE_BASE_URL);
assert_eq!(config.default_model(), DEFAULT_KIMI_CODE_MODEL);
Ok(())
}

#[test]
fn has_api_key_for_detects_env_and_config_per_provider() -> Result<()> {
let _lock = lock_test_env();
Expand Down
Loading