feat: keep provider records on switch, only mark active; filter on save

This commit is contained in:
KimiSwitch Dev
2026-07-08 14:25:05 +08:00
parent 8c515f42dc
commit 155efdf9f3
6 changed files with 32 additions and 20 deletions
+19
View File
@@ -124,6 +124,7 @@ pub fn kimi_code_to_config(value: &TomlValue) -> Config {
official_url: None, official_url: None,
managed, managed,
enabled, enabled,
active: true,
raw_other, raw_other,
}, },
); );
@@ -225,8 +226,15 @@ pub fn config_to_kimi_code(config: &Config, existing: Option<&TomlValue>) -> Tom
.unwrap_or(TomlValue::String("".to_string())), .unwrap_or(TomlValue::String("".to_string())),
); );
let is_kimi_native = |p: &Provider| p.managed;
let mut providers_table = Table::new(); let mut providers_table = Table::new();
for (name, provider) in &config.providers { for (name, provider) in &config.providers {
// Skip inactive custom providers when writing to Kimi Code config.
// Kimi native (managed) providers are always preserved.
if !is_kimi_native(provider) && !provider.active {
continue;
}
let mut pt = Table::new(); let mut pt = Table::new();
pt.insert("type".to_string(), TomlValue::String(provider.provider_type.as_str().to_string())); pt.insert("type".to_string(), TomlValue::String(provider.provider_type.as_str().to_string()));
if let Some(base_url) = provider.base_url.clone().filter(|s| !s.is_empty()) { if let Some(base_url) = provider.base_url.clone().filter(|s| !s.is_empty()) {
@@ -261,8 +269,19 @@ pub fn config_to_kimi_code(config: &Config, existing: Option<&TomlValue>) -> Tom
} }
root.insert("providers".to_string(), TomlValue::Table(providers_table)); root.insert("providers".to_string(), TomlValue::Table(providers_table));
// Collect provider names that survived the filter above.
let active_provider_names: std::collections::HashSet<&str> = config
.providers
.values()
.filter(|p| is_kimi_native(p) || p.active)
.map(|p| p.name.as_str())
.collect();
let mut models_table = Table::new(); let mut models_table = Table::new();
for (alias, model) in &config.models { for (alias, model) in &config.models {
if !active_provider_names.contains(model.provider.as_str()) {
continue;
}
let mut mt = Table::new(); let mut mt = Table::new();
mt.insert("provider".to_string(), TomlValue::String(model.provider.clone())); mt.insert("provider".to_string(), TomlValue::String(model.provider.clone()));
mt.insert("model".to_string(), TomlValue::String(model.model.clone())); mt.insert("model".to_string(), TomlValue::String(model.model.clone()));
+3
View File
@@ -83,6 +83,8 @@ pub struct Provider {
pub managed: bool, pub managed: bool,
#[serde(default = "default_true")] #[serde(default = "default_true")]
pub enabled: bool, pub enabled: bool,
#[serde(default)]
pub active: bool,
#[serde(default, skip_serializing_if = "Value::is_null")] #[serde(default, skip_serializing_if = "Value::is_null")]
pub raw_other: Value, pub raw_other: Value,
} }
@@ -98,6 +100,7 @@ impl PartialEq for Provider {
&& self.official_url == other.official_url && self.official_url == other.official_url
&& self.managed == other.managed && self.managed == other.managed
&& self.enabled == other.enabled && self.enabled == other.enabled
&& self.active == other.active
&& self.raw_other == other.raw_other && self.raw_other == other.raw_other
} }
} }
+1
View File
@@ -201,6 +201,7 @@ pub fn pi_file_to_config(file: &PiModelsFile) -> Config {
official_url: None, official_url: None,
managed: false, managed: false,
enabled: pi_provider.enabled, enabled: pi_provider.enabled,
active: true,
raw_other: pi_provider.extra.clone(), raw_other: pi_provider.extra.clone(),
}; };
+6 -16
View File
@@ -194,37 +194,27 @@ export default function App() {
const handleSwitchProvider = (name: string) => { const handleSwitchProvider = (name: string) => {
updateConfig((cfg) => { updateConfig((cfg) => {
const target = cfg.providers[name]; const target = cfg.providers[name];
if (!target) return cfg; if (!target || target.managed) return cfg;
const isKimiNative = (p: Provider) => p.managed === true; const isKimiNative = (p: Provider) => p.managed === true;
// Keep Kimi native (managed) providers and the selected provider only. // Mark the selected provider as active and deactivate other custom providers.
// Kimi native providers are left untouched. No provider/model records are deleted.
const providers: Record<string, Provider> = {}; const providers: Record<string, Provider> = {};
for (const [key, p] of Object.entries(cfg.providers)) { for (const [key, p] of Object.entries(cfg.providers)) {
if (isKimiNative(p)) { providers[key] = { ...p, active: isKimiNative(p) ? undefined : key === name };
providers[key] = p;
}
}
providers[name] = { ...target, enabled: true };
// Drop models whose provider was removed.
const models: Record<string, Model> = {};
for (const [key, m] of Object.entries(cfg.models)) {
if (providers[m.provider]) {
models[key] = m;
}
} }
// Set default model to the first model of the selected provider. // Set default model to the first model of the selected provider.
let default_model: string | null = null; let default_model: string | null = null;
for (const [key, m] of Object.entries(models)) { for (const [key, m] of Object.entries(cfg.models)) {
if (m.provider === name) { if (m.provider === name) {
default_model = key; default_model = key;
break; break;
} }
} }
return { ...cfg, providers, models, default_model }; return { ...cfg, providers, default_model };
}); });
}; };
+1 -4
View File
@@ -93,10 +93,7 @@ export function ProviderList({
? models[defaultModel]?.display_name || defaultModel ? models[defaultModel]?.display_name || defaultModel
: null; : null;
const isKimiNative = provider.managed === true; const isKimiNative = provider.managed === true;
const isActive = const isActive = !isKimiNative && provider.active === true;
!isKimiNative &&
defaultModel !== null &&
models[defaultModel]?.provider === provider.name;
return ( return (
<div <div
+2
View File
@@ -22,6 +22,8 @@ export interface Provider {
managed?: boolean; managed?: boolean;
/** Whether the provider is enabled/activated. */ /** Whether the provider is enabled/activated. */
enabled?: boolean; enabled?: boolean;
/** Whether this provider is the currently active one for Kimi Code. */
active?: boolean;
/** Extra agent-specific provider fields preserved across edits. */ /** Extra agent-specific provider fields preserved across edits. */
raw_other?: unknown; raw_other?: unknown;
} }