feat: unify provider active logic and add env field support for kimi_code_io
This commit is contained in:
@@ -87,12 +87,13 @@ fn active_provider_and_model(config: &Config) -> Option<(String, String)> {
|
||||
}
|
||||
|
||||
fn build_active_config(config: &Config) -> Config {
|
||||
let is_kimi_native = |p: &Provider| p.managed;
|
||||
|
||||
// Only the provider explicitly marked as active is written to the agent's
|
||||
// native config. This ensures Kimi Code / Pi follow Pi Switch's choice
|
||||
// instead of falling back to a managed/native provider.
|
||||
let providers: IndexMap<String, Provider> = config
|
||||
.providers
|
||||
.iter()
|
||||
.filter(|(_, p)| is_kimi_native(p) || p.active)
|
||||
.filter(|(_, p)| p.active)
|
||||
.map(|(k, p)| (k.clone(), p.clone()))
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -258,6 +258,7 @@ fn set_setting_tx(tx: &rusqlite::Transaction, key: &str, value: &str) -> DbResul
|
||||
fn provider_type_for_str(s: &str) -> ProviderType {
|
||||
match s {
|
||||
"anthropic" => ProviderType::Anthropic,
|
||||
"openai" => ProviderType::Openai,
|
||||
"openai_responses" => ProviderType::OpenaiResponses,
|
||||
"google-genai" => ProviderType::GoogleGenai,
|
||||
"vertexai" => ProviderType::Vertexai,
|
||||
|
||||
@@ -16,9 +16,10 @@ use crate::models::{Agent, Config, Model, Provider, ProviderType};
|
||||
pub type KimiResult<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn kimi_code_config_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.map(|h| h.join(".kimi-code"))
|
||||
.expect("failed to resolve home directory")
|
||||
std::env::var_os("KIMI_CODE_HOME")
|
||||
.map(PathBuf::from)
|
||||
.or_else(|| dirs::home_dir().map(|h| h.join(".kimi-code")))
|
||||
.expect("failed to resolve Kimi Code config directory")
|
||||
}
|
||||
|
||||
pub fn kimi_code_config_path() -> PathBuf {
|
||||
@@ -101,6 +102,16 @@ pub fn kimi_code_to_config(value: &TomlValue) -> Config {
|
||||
|| table.get("managed").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let enabled = table.get("enabled").and_then(|v| v.as_bool()).unwrap_or(true);
|
||||
|
||||
let env: IndexMap<String, String> = table
|
||||
.get("env")
|
||||
.and_then(|v| v.as_table())
|
||||
.map(|t| {
|
||||
t.iter()
|
||||
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let raw_other = {
|
||||
let mut rest = table.clone();
|
||||
rest.remove("type");
|
||||
@@ -109,6 +120,7 @@ pub fn kimi_code_to_config(value: &TomlValue) -> Config {
|
||||
rest.remove("managed");
|
||||
rest.remove("enabled");
|
||||
rest.remove("oauth");
|
||||
rest.remove("env");
|
||||
toml_value_to_json(&TomlValue::Table(rest))
|
||||
};
|
||||
|
||||
@@ -119,7 +131,7 @@ pub fn kimi_code_to_config(value: &TomlValue) -> Config {
|
||||
provider_type,
|
||||
base_url: base_url.filter(|s| !s.is_empty()),
|
||||
api_key: api_key.filter(|s| !s.is_empty()),
|
||||
env: IndexMap::new(),
|
||||
env,
|
||||
note: None,
|
||||
official_url: None,
|
||||
managed,
|
||||
@@ -226,13 +238,11 @@ pub fn config_to_kimi_code(config: &Config, existing: Option<&TomlValue>) -> Tom
|
||||
.unwrap_or(TomlValue::String("".to_string())),
|
||||
);
|
||||
|
||||
let is_kimi_native = |p: &Provider| p.managed;
|
||||
|
||||
let mut providers_table = Table::new();
|
||||
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 {
|
||||
// Only write the active provider to Kimi Code config so the agent
|
||||
// follows Pi Switch's selection.
|
||||
if !provider.active {
|
||||
continue;
|
||||
}
|
||||
let mut pt = Table::new();
|
||||
@@ -244,6 +254,13 @@ pub fn config_to_kimi_code(config: &Config, existing: Option<&TomlValue>) -> Tom
|
||||
pt.insert("api_key".to_string(), TomlValue::String(api_key));
|
||||
}
|
||||
pt.insert("enabled".to_string(), TomlValue::Boolean(provider.enabled));
|
||||
if !provider.env.is_empty() {
|
||||
let mut env_table = Table::new();
|
||||
for (k, v) in &provider.env {
|
||||
env_table.insert(k.clone(), TomlValue::String(v.clone()));
|
||||
}
|
||||
pt.insert("env".to_string(), TomlValue::Table(env_table));
|
||||
}
|
||||
if provider.managed {
|
||||
// Preserve existing oauth config if present, otherwise create a default entry.
|
||||
let oauth = provider
|
||||
@@ -258,9 +275,10 @@ pub fn config_to_kimi_code(config: &Config, existing: Option<&TomlValue>) -> Tom
|
||||
});
|
||||
pt.insert("oauth".to_string(), oauth);
|
||||
}
|
||||
// Merge remaining raw fields (oauth is handled explicitly above).
|
||||
// Merge remaining raw fields (oauth and env are handled explicitly above).
|
||||
if let TomlValue::Table(mut extra) = json_to_toml(&provider.raw_other).unwrap_or(TomlValue::Table(Table::new())) {
|
||||
extra.remove("oauth");
|
||||
extra.remove("env");
|
||||
for (k, v) in extra {
|
||||
pt.insert(k, v);
|
||||
}
|
||||
@@ -273,7 +291,7 @@ pub fn config_to_kimi_code(config: &Config, existing: Option<&TomlValue>) -> Tom
|
||||
let active_provider_names: std::collections::HashSet<&str> = config
|
||||
.providers
|
||||
.values()
|
||||
.filter(|p| is_kimi_native(p) || p.active)
|
||||
.filter(|p| p.active)
|
||||
.map(|p| p.name.as_str())
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@ pub type PiResult<T> = anyhow::Result<T>;
|
||||
|
||||
/// Returns the default Pi agent config directory for the current user.
|
||||
pub fn pi_agent_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.map(|h| h.join(".pi").join("agent"))
|
||||
.expect("failed to resolve home directory")
|
||||
std::env::var_os("PI_CODING_AGENT_DIR")
|
||||
.map(PathBuf::from)
|
||||
.or_else(|| dirs::home_dir().map(|h| h.join(".pi").join("agent")))
|
||||
.expect("failed to resolve Pi agent config directory")
|
||||
}
|
||||
|
||||
/// Returns the path to Pi's `models.json` file.
|
||||
|
||||
+5
-6
@@ -195,15 +195,14 @@ export default function App() {
|
||||
const handleSwitchProvider = async (name: string) => {
|
||||
updateConfig((cfg) => {
|
||||
const target = cfg.providers[name];
|
||||
if (!target || target.managed) return cfg;
|
||||
if (!target) return cfg;
|
||||
|
||||
const isKimiNative = (p: Provider) => p.managed === true;
|
||||
|
||||
// Mark the selected provider as active and deactivate other custom providers.
|
||||
// Kimi native providers are left untouched. No provider/model records are deleted.
|
||||
// Only the selected provider is active. All other providers (including
|
||||
// Kimi native/managed and custom) are deactivated so the target agent
|
||||
// follows Pi Switch's choice exactly.
|
||||
const providers: Record<string, Provider> = {};
|
||||
for (const [key, p] of Object.entries(cfg.providers)) {
|
||||
providers[key] = { ...p, active: isKimiNative(p) ? undefined : key === name };
|
||||
providers[key] = { ...p, active: key === name };
|
||||
}
|
||||
|
||||
// Set default model to the first model of the selected provider.
|
||||
|
||||
@@ -94,8 +94,7 @@ export function ProviderList({
|
||||
const defaultModelName = defaultModel
|
||||
? models[defaultModel]?.display_name || defaultModel
|
||||
: null;
|
||||
const isKimiNative = provider.managed === true;
|
||||
const isActive = !isKimiNative && provider.active === true;
|
||||
const isActive = provider.active === true;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user