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 {
|
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
|
let providers: IndexMap<String, Provider> = config
|
||||||
.providers
|
.providers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, p)| is_kimi_native(p) || p.active)
|
.filter(|(_, p)| p.active)
|
||||||
.map(|(k, p)| (k.clone(), p.clone()))
|
.map(|(k, p)| (k.clone(), p.clone()))
|
||||||
.collect();
|
.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 {
|
fn provider_type_for_str(s: &str) -> ProviderType {
|
||||||
match s {
|
match s {
|
||||||
"anthropic" => ProviderType::Anthropic,
|
"anthropic" => ProviderType::Anthropic,
|
||||||
|
"openai" => ProviderType::Openai,
|
||||||
"openai_responses" => ProviderType::OpenaiResponses,
|
"openai_responses" => ProviderType::OpenaiResponses,
|
||||||
"google-genai" => ProviderType::GoogleGenai,
|
"google-genai" => ProviderType::GoogleGenai,
|
||||||
"vertexai" => ProviderType::Vertexai,
|
"vertexai" => ProviderType::Vertexai,
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ use crate::models::{Agent, Config, Model, Provider, ProviderType};
|
|||||||
pub type KimiResult<T> = anyhow::Result<T>;
|
pub type KimiResult<T> = anyhow::Result<T>;
|
||||||
|
|
||||||
pub fn kimi_code_config_dir() -> PathBuf {
|
pub fn kimi_code_config_dir() -> PathBuf {
|
||||||
dirs::home_dir()
|
std::env::var_os("KIMI_CODE_HOME")
|
||||||
.map(|h| h.join(".kimi-code"))
|
.map(PathBuf::from)
|
||||||
.expect("failed to resolve home directory")
|
.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 {
|
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);
|
|| 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 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 raw_other = {
|
||||||
let mut rest = table.clone();
|
let mut rest = table.clone();
|
||||||
rest.remove("type");
|
rest.remove("type");
|
||||||
@@ -109,6 +120,7 @@ pub fn kimi_code_to_config(value: &TomlValue) -> Config {
|
|||||||
rest.remove("managed");
|
rest.remove("managed");
|
||||||
rest.remove("enabled");
|
rest.remove("enabled");
|
||||||
rest.remove("oauth");
|
rest.remove("oauth");
|
||||||
|
rest.remove("env");
|
||||||
toml_value_to_json(&TomlValue::Table(rest))
|
toml_value_to_json(&TomlValue::Table(rest))
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,7 +131,7 @@ pub fn kimi_code_to_config(value: &TomlValue) -> Config {
|
|||||||
provider_type,
|
provider_type,
|
||||||
base_url: base_url.filter(|s| !s.is_empty()),
|
base_url: base_url.filter(|s| !s.is_empty()),
|
||||||
api_key: api_key.filter(|s| !s.is_empty()),
|
api_key: api_key.filter(|s| !s.is_empty()),
|
||||||
env: IndexMap::new(),
|
env,
|
||||||
note: None,
|
note: None,
|
||||||
official_url: None,
|
official_url: None,
|
||||||
managed,
|
managed,
|
||||||
@@ -226,13 +238,11 @@ 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.
|
// Only write the active provider to Kimi Code config so the agent
|
||||||
// Kimi native (managed) providers are always preserved.
|
// follows Pi Switch's selection.
|
||||||
if !is_kimi_native(provider) && !provider.active {
|
if !provider.active {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut pt = Table::new();
|
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("api_key".to_string(), TomlValue::String(api_key));
|
||||||
}
|
}
|
||||||
pt.insert("enabled".to_string(), TomlValue::Boolean(provider.enabled));
|
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 {
|
if provider.managed {
|
||||||
// Preserve existing oauth config if present, otherwise create a default entry.
|
// Preserve existing oauth config if present, otherwise create a default entry.
|
||||||
let oauth = provider
|
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);
|
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())) {
|
if let TomlValue::Table(mut extra) = json_to_toml(&provider.raw_other).unwrap_or(TomlValue::Table(Table::new())) {
|
||||||
extra.remove("oauth");
|
extra.remove("oauth");
|
||||||
|
extra.remove("env");
|
||||||
for (k, v) in extra {
|
for (k, v) in extra {
|
||||||
pt.insert(k, v);
|
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
|
let active_provider_names: std::collections::HashSet<&str> = config
|
||||||
.providers
|
.providers
|
||||||
.values()
|
.values()
|
||||||
.filter(|p| is_kimi_native(p) || p.active)
|
.filter(|p| p.active)
|
||||||
.map(|p| p.name.as_str())
|
.map(|p| p.name.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ pub type PiResult<T> = anyhow::Result<T>;
|
|||||||
|
|
||||||
/// Returns the default Pi agent config directory for the current user.
|
/// Returns the default Pi agent config directory for the current user.
|
||||||
pub fn pi_agent_dir() -> PathBuf {
|
pub fn pi_agent_dir() -> PathBuf {
|
||||||
dirs::home_dir()
|
std::env::var_os("PI_CODING_AGENT_DIR")
|
||||||
.map(|h| h.join(".pi").join("agent"))
|
.map(PathBuf::from)
|
||||||
.expect("failed to resolve home directory")
|
.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.
|
/// Returns the path to Pi's `models.json` file.
|
||||||
|
|||||||
+5
-6
@@ -195,15 +195,14 @@ export default function App() {
|
|||||||
const handleSwitchProvider = async (name: string) => {
|
const handleSwitchProvider = async (name: string) => {
|
||||||
updateConfig((cfg) => {
|
updateConfig((cfg) => {
|
||||||
const target = cfg.providers[name];
|
const target = cfg.providers[name];
|
||||||
if (!target || target.managed) return cfg;
|
if (!target) return cfg;
|
||||||
|
|
||||||
const isKimiNative = (p: Provider) => p.managed === true;
|
// Only the selected provider is active. All other providers (including
|
||||||
|
// Kimi native/managed and custom) are deactivated so the target agent
|
||||||
// Mark the selected provider as active and deactivate other custom providers.
|
// follows Pi Switch's choice exactly.
|
||||||
// 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)) {
|
||||||
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.
|
// Set default model to the first model of the selected provider.
|
||||||
|
|||||||
@@ -94,8 +94,7 @@ export function ProviderList({
|
|||||||
const defaultModelName = defaultModel
|
const defaultModelName = defaultModel
|
||||||
? models[defaultModel]?.display_name || defaultModel
|
? models[defaultModel]?.display_name || defaultModel
|
||||||
: null;
|
: null;
|
||||||
const isKimiNative = provider.managed === true;
|
const isActive = provider.active === true;
|
||||||
const isActive = !isKimiNative && provider.active === true;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user