Add Oh My Pi support through the OpenAI Responses API

Discover CPA models dynamically and route the managed provider to /v1/responses.
This commit is contained in:
lzt404 committed 2026-10-08 18:36:58 +08:00
1 parent aba62fa6bf
commit d7e39e8fdc
23 files changed
+414 -20

No files matched your search

+2
View File
@@ -87,6 +87,7 @@ fn clients() -> Vec<AgentClient> {
"workbuddy",
"kimi-code",
"grok-build",
"omp",
]
.into_iter()
.map(|id| AgentClient::parse(id).unwrap())
@@ -417,6 +418,7 @@ fn clear_integration_needs_no_backup_and_removes_state_for_each_client() {
AgentClient::WorkBuddy,
AgentClient::KimiCode,
AgentClient::GrokBuild,
AgentClient::Omp,
]
.into_iter()
.filter(|client| client.supported_platform())
+1
View File
@@ -33,6 +33,7 @@ pub(crate) fn inspect_agent_config_statuses(
AgentStatusDetectionTarget::Client(AgentClient::AntigravityCli),
AgentStatusDetectionTarget::Client(AgentClient::KimiCode),
AgentStatusDetectionTarget::Client(AgentClient::GrokBuild),
AgentStatusDetectionTarget::Client(AgentClient::Omp),
AgentStatusDetectionTarget::PiProvider,
];
let queue = Mutex::new(targets.into_iter().enumerate());
+194 -1
View File
@@ -232,6 +232,23 @@ pub(crate) fn build_agent_updates_with_oauth(
after,
}])
}
AgentClient::Omp => {
if paths.len() != 2 {
return Err("Invalid number of Oh My Pi configuration paths".to_string());
}
let models_before = read_optional_text(&paths[0])?;
let settings_before = read_optional_text(&paths[1])?;
Ok(vec![
AgentFileUpdate {
path: paths[0].clone(),
after: build_omp_models_config(models_before.as_deref(), &openai_base, api_key)?,
},
AgentFileUpdate {
path: paths[1].clone(),
after: build_omp_settings_config(settings_before.as_deref(), model)?,
},
])
}
}
}
@@ -1649,6 +1666,7 @@ pub(crate) fn prepare_agent_managed_removal(
AgentClient::AntigravityCli => prepare_antigravity_removal(client, paths),
AgentClient::KimiCode => prepare_kimi_code_managed_removal(paths),
AgentClient::GrokBuild => prepare_grok_build_managed_removal(paths),
AgentClient::Omp => prepare_omp_managed_removal(paths),
}
}
@@ -2624,7 +2642,7 @@ pub(crate) fn agent_config_semantically_equal(
json5::from_str::<serde_json::Value>(actual).ok()
== json5::from_str::<serde_json::Value>(expected).ok()
}
AgentClient::Hermes => {
AgentClient::Hermes | AgentClient::Omp => {
serde_norway::from_str::<serde_norway::Value>(actual).ok()
== serde_norway::from_str::<serde_norway::Value>(expected).ok()
}
@@ -2703,6 +2721,17 @@ pub(crate) fn build_agent_session_restored_bytes_with_preference(
AgentClient::AntigravityCli => restore_antigravity_config(client, path, current, original)?,
AgentClient::KimiCode => build_restored_kimi_code_config(current, original)?,
AgentClient::GrokBuild => build_restored_grok_build_config(current, original)?,
AgentClient::Omp => {
let index = paths
.iter()
.position(|candidate| candidate == path)
.ok_or_else(|| "Oh My Pi restoration path mismatch".to_string())?;
match index {
0 => build_restored_omp_models(current, original)?,
1 => build_restored_omp_settings(current, original)?,
_ => return Err("Invalid Oh My Pi restoration path index".to_string()),
}
}
};
if prefer_exact_original {
if let (Some(restored), Some(original), Some(original_bytes)) =
@@ -3263,6 +3292,170 @@ pub(crate) fn managed_model_alias(model: &str) -> String {
format!("{MANAGED_AGENT_PROVIDER_ID}/{model}")
}
pub(crate) fn build_omp_models_config(
existing: Option<&str>,
base_url: &str,
api_key: &str,
) -> Result<String, String> {
let _ = api_key;
render_agent_yaml_mapping_update(existing, "Oh My Pi models.yml", |root| {
let providers = root
.entry(yaml_key("providers"))
.or_insert_with(|| serde_norway::Value::Mapping(serde_norway::Mapping::new()));
let providers = providers
.as_mapping_mut()
.ok_or_else(|| "Oh My Pi providers must be a YAML mapping".to_string())?;
let mut provider = serde_norway::Mapping::new();
provider.insert(yaml_key("baseUrl"), serde_norway::Value::String(base_url.to_string()));
provider.insert(
yaml_key("apiKey"),
serde_norway::Value::String(OMP_AGENT_API_KEY_ENV.to_string()),
);
provider.insert(
yaml_key("api"),
serde_norway::Value::String(OMP_AGENT_API.to_string()),
);
let mut discovery = serde_norway::Mapping::new();
discovery.insert(
yaml_key("type"),
serde_norway::Value::String("openai-models-list".to_string()),
);
provider.insert(yaml_key("discovery"), serde_norway::Value::Mapping(discovery));
providers.insert(
yaml_key(OMP_AGENT_PROVIDER_ID),
serde_norway::Value::Mapping(provider),
);
Ok(())
})
}
pub(crate) fn build_omp_settings_config(
existing: Option<&str>,
model: &str,
) -> Result<String, String> {
render_agent_yaml_mapping_update(existing, "Oh My Pi config.yml", |root| {
let roles = root
.entry(yaml_key("modelRoles"))
.or_insert_with(|| serde_norway::Value::Mapping(serde_norway::Mapping::new()));
let roles = roles
.as_mapping_mut()
.ok_or_else(|| "Oh My Pi modelRoles must be a YAML mapping".to_string())?;
roles.insert(
yaml_key("default"),
serde_norway::Value::String(format!("{OMP_AGENT_PROVIDER_ID}/{model}")),
);
Ok(())
})
}
pub(crate) fn prepare_omp_managed_removal(paths: &[PathBuf]) -> Result<Images, String> {
if paths.len() != 2 {
return Err("Invalid number of Oh My Pi configuration paths".to_string());
}
let mut updates = Vec::new();
if paths[0].is_file() {
let content = fs::read_to_string(&paths[0]).map_err(|error| {
format!("Failed to read Oh My Pi models.yml {}: {error}", path_to_string(&paths[0]))
})?;
let rendered = render_agent_yaml_mapping_update(Some(&content), "Oh My Pi models.yml", |root| {
let mut remove_providers = false;
if let Some(providers) = root
.get_mut(yaml_key("providers"))
.and_then(serde_norway::Value::as_mapping_mut)
{
providers.remove(yaml_key(OMP_AGENT_PROVIDER_ID));
remove_providers = providers.is_empty();
}
if remove_providers {
root.remove(yaml_key("providers"));
}
Ok(())
})?;
let root = parse_agent_yaml_mapping(Some(&rendered), "Oh My Pi models.yml")?;
updates.push((
paths[0].clone(),
(!root.is_empty()).then(|| rendered.into_bytes()),
));
}
if paths[1].is_file() {
let content = fs::read_to_string(&paths[1]).map_err(|error| {
format!("Failed to read Oh My Pi config.yml {}: {error}", path_to_string(&paths[1]))
})?;
let rendered = render_agent_yaml_mapping_update(Some(&content), "Oh My Pi config.yml", |root| {
let mut remove_roles = false;
if let Some(roles) = root
.get_mut(yaml_key("modelRoles"))
.and_then(serde_norway::Value::as_mapping_mut)
{
if roles
.get(yaml_key("default"))
.and_then(serde_norway::Value::as_str)
.is_some_and(|value| value.starts_with(&format!("{OMP_AGENT_PROVIDER_ID}/")))
{
roles.remove(yaml_key("default"));
}
remove_roles = roles.is_empty();
}
if remove_roles {
root.remove(yaml_key("modelRoles"));
}
Ok(())
})?;
let root = parse_agent_yaml_mapping(Some(&rendered), "Oh My Pi config.yml")?;
updates.push((
paths[1].clone(),
(!root.is_empty()).then(|| rendered.into_bytes()),
));
}
Ok(updates.into_iter().collect())
}
pub(crate) fn build_restored_omp_models(
current: &str,
original: Option<&str>,
) -> Result<Option<String>, String> {
let original_root = parse_agent_yaml_mapping(original, "Original Oh My Pi models.yml")?;
let rendered = render_agent_yaml_mapping_update(Some(current), "Current Oh My Pi models.yml", |root| {
let original_provider = yaml_mapping_value(&original_root, "providers")
.and_then(serde_norway::Value::as_mapping)
.and_then(|providers| yaml_mapping_value(providers, OMP_AGENT_PROVIDER_ID))
.cloned();
let providers = root
.entry(yaml_key("providers"))
.or_insert_with(|| serde_norway::Value::Mapping(serde_norway::Mapping::new()));
let providers = providers
.as_mapping_mut()
.ok_or_else(|| "Oh My Pi providers must be a YAML mapping".to_string())?;
if let Some(provider) = original_provider {
providers.insert(yaml_key(OMP_AGENT_PROVIDER_ID), provider);
} else {
providers.remove(yaml_key(OMP_AGENT_PROVIDER_ID));
}
if providers.is_empty()
&& yaml_mapping_value(&original_root, "providers").is_none()
{
root.remove(yaml_key("providers"));
}
Ok(())
})?;
let root = parse_agent_yaml_mapping(Some(&rendered), "Restored Oh My Pi models.yml")?;
Ok((!root.is_empty() || original.is_some()).then_some(rendered))
}
pub(crate) fn build_restored_omp_settings(
current: &str,
original: Option<&str>,
) -> Result<Option<String>, String> {
let original_root = parse_agent_yaml_mapping(original, "Original Oh My Pi config.yml")?;
let rendered = render_agent_yaml_mapping_update(Some(current), "Current Oh My Pi config.yml", |root| {
restore_yaml_key(root, Some(&original_root), "modelRoles");
Ok(())
})?;
let root = parse_agent_yaml_mapping(Some(&rendered), "Restored Oh My Pi config.yml")?;
Ok((!root.is_empty() || original.is_some()).then_some(rendered))
}
pub(crate) fn build_kimi_code_agent_config(
existing: Option<&str>,
base_url: &str,
+89
View File
@@ -52,6 +52,10 @@ pub(crate) fn agent_config_paths(client: AgentClient, home: &Path) -> Vec<PathBu
AgentClient::ZCode => vec![home.join(".zcode/v2").join(ZCODE_PROVIDER_CONFIG_FILE)],
AgentClient::KimiCode => vec![kimi_code_home(home).join(KIMI_CODE_CONFIG_FILE)],
AgentClient::GrokBuild => vec![grok_build_home(home).join(GROK_BUILD_CONFIG_FILE)],
AgentClient::Omp => {
let directory = omp_agent_directory(home);
vec![directory.join(OMP_MODELS_FILE), directory.join(OMP_SETTINGS_FILE)]
}
}
}
@@ -107,6 +111,10 @@ pub(crate) fn grok_build_home(home: &Path) -> PathBuf {
agent_configuration_environment("GROK_HOME").unwrap_or_else(|| home.join(".grok"))
}
pub(crate) fn omp_agent_directory(home: &Path) -> PathBuf {
agent_configuration_environment("PI_CODING_AGENT_DIR").unwrap_or_else(|| home.join(".omp/agent"))
}
pub(crate) fn deepseek_harness_home(home: &Path) -> PathBuf {
agent_configuration_environment("DSH_HOME").unwrap_or_else(|| home.join(".dsh"))
}
@@ -1326,6 +1334,8 @@ pub(crate) fn inspect_agent_managed_config(
.map(|(configured, model)| (configured, model, false)),
AgentClient::GrokBuild => inspect_grok_build_agent_config(&paths[0], port, api_key)
.map(|(configured, model)| (configured, model, false)),
AgentClient::Omp => inspect_omp_agent_config(paths, port, api_key)
.map(|(configured, model)| (configured, model, false)),
}
}
@@ -1561,6 +1571,7 @@ pub(crate) fn agent_has_managed_marker(
Some("models"),
"default",
),
AgentClient::Omp => omp_has_managed_marker(paths),
}
}
@@ -2452,6 +2463,9 @@ pub(crate) fn find_agent_executable(client: AgentClient, home: &Path) -> Option<
if client == AgentClient::GrokBuild {
return find_grok_build_executable(home);
}
if client == AgentClient::Omp {
return find_omp_executable(home);
}
if client == AgentClient::OpenCode {
return find_opencode_executable(home);
}
@@ -3453,6 +3467,81 @@ pub(crate) fn inspect_zcode_agent_config(
Ok((configured, model))
}
pub(crate) fn omp_provider_mapping(root: &serde_norway::Mapping) -> Option<&serde_norway::Mapping> {
yaml_mapping_value(root, "providers")
.and_then(serde_norway::Value::as_mapping)
.and_then(|providers| yaml_mapping_value(providers, OMP_AGENT_PROVIDER_ID))
.and_then(serde_norway::Value::as_mapping)
}
pub(crate) fn omp_selected_model(root: &serde_norway::Mapping) -> Option<String> {
let selector = yaml_mapping_value(root, "modelRoles")
.and_then(serde_norway::Value::as_mapping)
.and_then(|roles| yaml_mapping_value(roles, "default"))
.and_then(serde_norway::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())?;
let prefix = format!("{OMP_AGENT_PROVIDER_ID}/");
selector.strip_prefix(&prefix).map(str::to_string)
}
pub(crate) fn inspect_omp_agent_config(
paths: &[PathBuf],
port: u16,
api_key: &str,
) -> Result<(bool, Option<String>), String> {
if paths.len() != 2 || !paths[0].is_file() || !paths[1].is_file() {
return Ok((false, None));
}
let models = read_agent_yaml_mapping_or_empty(&paths[0], "Oh My Pi models.yml")?;
let settings = read_agent_yaml_mapping_or_empty(&paths[1], "Oh My Pi config.yml")?;
let provider = omp_provider_mapping(&models);
let model = omp_selected_model(&settings);
let expected = format!("{}/v1", managed_core_loopback_origin(port));
let configured = provider
.and_then(|provider| yaml_mapping_value(provider, "baseUrl"))
.and_then(serde_norway::Value::as_str)
== Some(expected.as_str())
&& provider
.and_then(|provider| yaml_mapping_value(provider, "apiKey"))
.and_then(serde_norway::Value::as_str)
== Some(OMP_AGENT_API_KEY_ENV)
&& provider
.and_then(|provider| yaml_mapping_value(provider, "api"))
.and_then(serde_norway::Value::as_str)
.is_some_and(|api| api == OMP_AGENT_API || api == "openai-completions")
&& provider
.and_then(|provider| yaml_mapping_value(provider, "discovery"))
.and_then(serde_norway::Value::as_mapping)
.and_then(|discovery| yaml_mapping_value(discovery, "type"))
.and_then(serde_norway::Value::as_str)
== Some("openai-models-list")
&& model.is_some();
let _ = api_key;
Ok((configured, model))
}
pub(crate) fn omp_has_managed_marker(paths: &[PathBuf]) -> Result<bool, String> {
if paths.first().is_none_or(|path| !path.is_file()) {
return Ok(false);
}
let models = read_agent_yaml_mapping_or_empty(&paths[0], "Oh My Pi models.yml")?;
Ok(omp_provider_mapping(&models).is_some())
}
pub(crate) fn find_omp_executable(home: &Path) -> Option<PathBuf> {
let bun = home.join(".bun/bin");
#[cfg(target_os = "windows")]
let managed = [bun.join("omp.exe"), bun.join("omp.cmd")];
#[cfg(not(target_os = "windows"))]
let managed = [bun.join("omp")];
managed
.into_iter()
.find(|path| agent_cli_executable(path))
.or_else(|| find_named_agent_executable(home, &["omp"]))
}
pub(crate) fn inspect_kimi_code_agent_config(
path: &Path,
port: u16,
+30 -2
View File
@@ -161,6 +161,7 @@ pub(crate) fn launch_agent(
&launch_directory,
&[],
&[],
&[],
&terminal,
);
}
@@ -188,7 +189,7 @@ pub(crate) fn launch_agent(
}
let executable = env::current_exe().map_err(|_| "Unable to locate CPA launch adapter")?;
let directory = resolve_launch_directory(working_directory.as_deref(), &home)?;
launch_cli_agent(&executable, client.name(), &directory, &antigravity_cli_helper_arguments(&home), &[], &terminal)
launch_cli_agent(&executable, client.name(), &directory, &antigravity_cli_helper_arguments(&home), &[], &[], &terminal)
}
(AgentClient::WorkBuddy, "app") => {
let executable = find_workbuddy_desktop_executable(&home)
@@ -233,12 +234,14 @@ pub(crate) fn launch_agent(
&[]
};
let arguments = agent_cli_launch_arguments(client, deepseek_harness_options.as_ref())?;
let environment_to_set = omp_launch_environment(client, effective_agent_api_key(&config));
launch_cli_agent(
&executable,
client.name(),
&launch_directory,
&arguments,
environment_to_remove,
&environment_to_set,
&terminal,
)
}
@@ -247,6 +250,15 @@ pub(crate) fn launch_agent(
}
}
fn omp_launch_environment(client: AgentClient, api_key: &str) -> Vec<(&str, &str)> {
if client == AgentClient::Omp {
vec![(OMP_AGENT_API_KEY_ENV, api_key)]
} else {
Vec::new()
}
}
fn deepseek_harness_launch_mode(
options: Option<&DeepSeekHarnessLaunchOptions>,
) -> Result<String, String> {
@@ -1186,6 +1198,7 @@ fn launch_cli_agent(
working_directory: &Path,
arguments: &[String],
environment_to_remove: &[&str],
environment_to_set: &[(&str, &str)],
terminal: &str,
) -> Result<(), String> {
let removals = environment_to_remove
@@ -1193,6 +1206,11 @@ fn launch_cli_agent(
.map(|key| format!("-u {}", shell_single_quote(key)))
.collect::<Vec<_>>()
.join(" ");
let assignments = environment_to_set
.iter()
.map(|(key, value)| format!("{}={}", shell_single_quote(key), shell_single_quote(value)))
.collect::<Vec<_>>()
.join(" ");
let invocation = std::iter::once(shell_single_quote(&path_to_string(executable)))
.chain(
arguments
@@ -1202,9 +1220,10 @@ fn launch_cli_agent(
.collect::<Vec<_>>()
.join(" ");
let command_line = format!(
"cd {} && exec env {} {}",
"cd {} && exec env {} {} {}",
shell_single_quote(&path_to_string(working_directory)),
removals,
assignments,
invocation,
);
let script = if terminal == "iterm2" {
@@ -1245,6 +1264,7 @@ fn launch_cli_agent(
working_directory: &Path,
command_arguments: &[String],
environment_to_remove: &[&str],
environment_to_set: &[(&str, &str)],
terminal: &str,
) -> Result<(), String> {
let definitions = linux_terminal_definitions();
@@ -1269,6 +1289,9 @@ fn launch_cli_agent(
for key in environment_to_remove {
command.env_remove(key);
}
for (key, value) in environment_to_set {
command.env(key, value);
}
match command.spawn() {
Ok(_) => return Ok(()),
Err(error) => last_error = Some(error.to_string()),
@@ -1347,6 +1370,7 @@ fn launch_cli_agent(
working_directory: &Path,
arguments: &[String],
environment_to_remove: &[&str],
environment_to_set: &[(&str, &str)],
terminal: &str,
) -> Result<(), String> {
use std::os::windows::process::CommandExt;
@@ -1413,6 +1437,9 @@ fn launch_cli_agent(
for key in environment_to_remove {
command.env_remove(key);
}
for (key, value) in environment_to_set {
command.env(key, value);
}
command
.spawn()
.map(|_| ())
@@ -1426,6 +1453,7 @@ fn launch_cli_agent(
_working_directory: &Path,
_arguments: &[String],
_environment_to_remove: &[&str],
_environment_to_set: &[(&str, &str)],
_terminal: &str,
) -> Result<(), String> {
Err(format!("The current platform does not support launching {label}"))
+5 -1
View File
@@ -748,6 +748,10 @@ pub(crate) fn fresh_agent_contents_with_oauth(
model,
models,
)?]),
AgentClient::Omp => Ok(vec![
build_omp_models_config(None, &openai_base, api_key)?,
build_omp_settings_config(None, model)?,
]),
}
}
@@ -770,7 +774,7 @@ pub(crate) fn agent_contents_equal(client: AgentClient, actual: &str, expected:
serde_yaml::from_str::<serde_yaml::Value>(actual).ok()
== serde_yaml::from_str::<serde_yaml::Value>(expected).ok()
}
AgentClient::DeepSeekHarness => {
AgentClient::Omp | AgentClient::DeepSeekHarness => {
serde_norway::from_str::<serde_norway::Value>(actual).ok()
== serde_norway::from_str::<serde_norway::Value>(expected).ok()
}
+1
View File
@@ -78,6 +78,7 @@ fn tracked_configuration_paths(app: &tauri::AppHandle) -> Result<Vec<PathBuf>, S
AgentClient::AntigravityCli,
AgentClient::KimiCode,
AgentClient::GrokBuild,
AgentClient::Omp,
] {
paths.extend(agent_managed_paths(client, &home));
}
+10
View File
@@ -174,6 +174,11 @@ const DEEPSEEK_HARNESS_SETTINGS_FILE: &str = "settings.yaml";
const DEEPSEEK_HARNESS_CREDENTIALS_FILE: &str = ".credentials.yaml";
const PI_AGENT_ID: &str = "pi";
const PI_AGENT_NAME: &str = "Pi";
const OMP_AGENT_PROVIDER_ID: &str = "easy-cliproxyapi";
const OMP_AGENT_API_KEY_ENV: &str = "EASYCLIPROXYAPI_API_KEY";
const OMP_AGENT_API: &str = "openai-responses";
const OMP_MODELS_FILE: &str = "models.yml";
const OMP_SETTINGS_FILE: &str = "config.yml";
const PI_CLIPROXYAPI_PACKAGE: &str = "npm:@router-for-me/pi-cliproxyapi-provider";
const PI_CLIPROXYAPI_NPM_LATEST_URL: &str =
"https://registry.npmjs.org/@router-for-me%2Fpi-cliproxyapi-provider/latest";
@@ -1358,6 +1363,7 @@ enum AgentClient {
AntigravityCli,
KimiCode,
GrokBuild,
Omp,
}
#[derive(Clone, Debug)]
@@ -1382,6 +1388,7 @@ impl AgentClient {
"antigravity-cli" => Ok(Self::AntigravityCli),
"kimi-code" => Ok(Self::KimiCode),
"grok-build" => Ok(Self::GrokBuild),
"omp" => Ok(Self::Omp),
_ => Err(format!("Unsupported agent client: {value}")),
}
}
@@ -1400,6 +1407,7 @@ impl AgentClient {
Self::AntigravityCli => "antigravity-cli",
Self::KimiCode => "kimi-code",
Self::GrokBuild => "grok-build",
Self::Omp => "omp",
}
}
@@ -1417,6 +1425,7 @@ impl AgentClient {
Self::AntigravityCli => "Antigravity CLI",
Self::KimiCode => "Kimi Code",
Self::GrokBuild => "Grok Build",
Self::Omp => "Oh My Pi",
}
}
@@ -1443,6 +1452,7 @@ impl AgentClient {
Self::AntigravityCli => &["agy"],
Self::KimiCode => &["kimi"],
Self::GrokBuild => &["grok"],
Self::Omp => &["omp"],
}
}
}
@@ -1549,6 +1549,53 @@ async fn codex_configuration_uses_public_v1_model_ids_for_selection_and_files()
assert_eq!(ids, written);
}
#[test]
fn omp_configuration_discovers_models_without_storing_the_api_key() {
let home = agent_test_home("omp-provider");
let existing_models = "providers:\n other:\n baseUrl: https://example.test/v1\n apiKey: KEEP_OTHER\n";
let existing_settings = "shellPath: E:/software/toolchain/w64devkit/bin/bash.exe\nmodelRoles:\n tiny: local/lfm2.5-230m\n";
let models = build_omp_models_config(Some(existing_models), "http://127.0.0.1:8317/v1", "secret-value").unwrap();
let settings = build_omp_settings_config(Some(existing_settings), "gpt-test").unwrap();
let models_value: serde_norway::Value = serde_norway::from_str(&models).unwrap();
let settings_value: serde_norway::Value = serde_norway::from_str(&settings).unwrap();
let provider = &models_value["providers"]["easy-cliproxyapi"];
assert_eq!(provider["baseUrl"].as_str(), Some("http://127.0.0.1:8317/v1"));
assert_eq!(provider["apiKey"].as_str(), Some("EASYCLIPROXYAPI_API_KEY"));
assert_eq!(provider["api"].as_str(), Some("openai-responses"));
assert_eq!(provider["discovery"]["type"].as_str(), Some("openai-models-list"));
assert!(provider.get("models").is_none());
assert!(!models.contains("secret-value"));
assert_eq!(models_value["providers"]["other"]["apiKey"].as_str(), Some("KEEP_OTHER"));
assert_eq!(settings_value["shellPath"].as_str(), Some("E:/software/toolchain/w64devkit/bin/bash.exe"));
assert_eq!(settings_value["modelRoles"]["tiny"].as_str(), Some("local/lfm2.5-230m"));
assert_eq!(settings_value["modelRoles"]["default"].as_str(), Some("easy-cliproxyapi/gpt-test"));
let paths = agent_config_paths(AgentClient::Omp, &home);
fs::create_dir_all(paths[0].parent().unwrap()).unwrap();
fs::write(&paths[0], models).unwrap();
fs::write(&paths[1], settings).unwrap();
let (configured, model) = inspect_omp_agent_config(&paths, 8317, "secret-value").unwrap();
assert!(configured);
assert_eq!(model.as_deref(), Some("gpt-test"));
assert!(omp_has_managed_marker(&paths).unwrap());
let removed = prepare_omp_managed_removal(&paths).unwrap();
for (path, bytes) in removed {
match bytes {
Some(bytes) => fs::write(&path, bytes).unwrap(),
None => fs::write(&path, "").unwrap(),
}
}
let restored_models = fs::read_to_string(&paths[0]).unwrap();
let restored_settings = fs::read_to_string(&paths[1]).unwrap();
assert!(!restored_models.contains("easy-cliproxyapi"));
assert!(restored_models.contains("KEEP_OTHER"));
assert!(!restored_settings.contains("easy-cliproxyapi"));
assert!(restored_settings.contains("shellPath"));
assert!(restored_settings.contains("local/lfm2.5-230m"));
fs::remove_dir_all(home).unwrap();
}
#[test]
fn kimi_grok_and_zcode_use_cpa_runtime_context_windows() {
for client in [
@@ -1566,6 +1613,7 @@ fn kimi_grok_and_zcode_use_cpa_runtime_context_windows() {
AgentClient::OpenClaw,
AgentClient::Hermes,
AgentClient::DeepSeekHarness,
AgentClient::Omp,
] {
assert!(!agent_uses_cpa_runtime_context_windows(client));
}
+1
View File
@@ -12,6 +12,7 @@ fn installation_detection_separates_executables_from_version_metadata() {
AgentClient::DeepSeekHarness,
AgentClient::KimiCode,
AgentClient::GrokBuild,
AgentClient::Omp,
] {
assert!(agent_installation_detected(client, None, true, false));
assert!(!agent_installation_detected(client, None, false, false));