From 08334b7a740f74f6d9b625ab4296a8f3322a16bd Mon Sep 17 00:00:00 2001 From: zhangyijun Date: Thu, 16 Jul 2026 18:05:08 +0800 Subject: [PATCH 1/3] feat(aionrs): expose GLM-5.2 thinking mode --- crates/aionui-ai-agent/src/factory/aionrs.rs | 23 ++- .../src/manager/aionrs/agent.rs | 150 ++++++++++++++++-- crates/aionui-ai-agent/src/types.rs | 2 + 3 files changed, 159 insertions(+), 16 deletions(-) diff --git a/crates/aionui-ai-agent/src/factory/aionrs.rs b/crates/aionui-ai-agent/src/factory/aionrs.rs index 758f0749e..7bed8e9e3 100644 --- a/crates/aionui-ai-agent/src/factory/aionrs.rs +++ b/crates/aionui-ai-agent/src/factory/aionrs.rs @@ -95,8 +95,9 @@ pub(super) async fn build( let provider = map_aionrs_provider(&row.platform, &model_id, row.model_protocols.as_deref())?; - let (base_url, compat_overrides) = + let (base_url, mut compat_overrides) = resolve_aionrs_url_and_compat(&row.platform, &row.base_url, &provider, row.is_full_url); + compat_overrides.thinking_mode = glm_52_default_thinking_mode(&model_id); let bedrock_config = if row.platform == "bedrock" { resolve_bedrock_config(row.bedrock_config.as_deref()) @@ -210,6 +211,11 @@ pub(super) async fn build( Ok(AgentInstance::Aionrs(Arc::new(agent))) } +fn glm_52_default_thinking_mode(model_id: &str) -> Option { + let normalized = model_id.trim().to_ascii_lowercase(); + (normalized == "glm-5.2" || normalized.starts_with("glm-5.2-")).then(|| "enabled".to_owned()) +} + /// Map AionUi DB platform/protocol settings to the aionrs provider identifier. pub(crate) fn map_aionrs_provider( platform: &str, @@ -1707,4 +1713,19 @@ mod tests { assert_eq!(overrides.system_prompt.as_deref(), Some("Be concise.")); } + + #[test] + fn glm_52_defaults_to_enabled_thinking() { + assert_eq!(glm_52_default_thinking_mode("glm-5.2").as_deref(), Some("enabled")); + assert_eq!( + glm_52_default_thinking_mode("GLM-5.2-20260701").as_deref(), + Some("enabled") + ); + } + + #[test] + fn non_glm_52_models_do_not_receive_a_thinking_override() { + assert!(glm_52_default_thinking_mode("glm-5.1").is_none()); + assert!(glm_52_default_thinking_mode("claude-sonnet-4").is_none()); + } } diff --git a/crates/aionui-ai-agent/src/manager/aionrs/agent.rs b/crates/aionui-ai-agent/src/manager/aionrs/agent.rs index 4f74f7eff..2ae14e90c 100644 --- a/crates/aionui-ai-agent/src/manager/aionrs/agent.rs +++ b/crates/aionui-ai-agent/src/manager/aionrs/agent.rs @@ -89,6 +89,8 @@ pub struct AionrsAgentManager { #[allow(dead_code)] // intentional: lifetime-extension only; see Drop impl mcp_managers: Vec>, approval_manager: Arc, + /// Request-level thinking mode for models that expose the option. + thinking_mode: Option>, confirmations: Arc>>, final_input_dump: Option, /// Signalled by `cancel()` to abort an in-flight `engine.run()` via @@ -118,6 +120,7 @@ impl AionrsAgentManager { let runtime = AgentRuntime::new(conversation_id.clone(), workspace.clone(), 128); let sink: Arc = Arc::new(BackendOutputSink::new(runtime.event_sender())); let runtime_env = config_extra.runtime_env.clone(); + let thinking_mode = config_extra.compat_overrides.thinking_mode.clone(); let final_input_dump = config_extra .prompt_dump_dir .clone() @@ -145,7 +148,7 @@ impl AionrsAgentManager { system_prompt: config_extra.system_prompt.clone(), profile: None, auto_approve: config_extra.session_mode.as_deref() == Some("yolo"), - thinking: None, + thinking: thinking_mode.clone(), thinking_budget: None, project_dir: Some(PathBuf::from(&workspace)), }; @@ -233,6 +236,7 @@ impl AionrsAgentManager { slash_commands, mcp_managers: result.mcp_managers, approval_manager, + thinking_mode: thinking_mode.map(Mutex::new), confirmations, final_input_dump, cancel_notify: Arc::new(Notify::new()), @@ -515,27 +519,54 @@ impl AionrsAgentManager { } pub async fn config_options(&self) -> Result { - Ok(GetConfigOptionsResponse { - config_options: vec![aionrs_mode_config_option(self.approval_manager.current_mode())], - }) + let mut config_options = vec![aionrs_mode_config_option(self.approval_manager.current_mode())]; + if let Some(thinking_mode) = &self.thinking_mode { + config_options.push(aionrs_thinking_config_option(thinking_mode.lock().await.clone())); + } + Ok(GetConfigOptionsResponse { config_options }) } pub async fn set_config_option(&self, option_id: &str, value: &str) -> Result { let option_id = option_id.trim(); let value = value.trim(); - if option_id != AIONRS_MODE_OPTION_ID { - return Err(AgentError::bad_request(format!( - "Config option '{option_id}' is not available" - ))); - } - if !is_aionrs_session_mode(value) { - return Err(AgentError::bad_request(format!( - "Value '{value}' is not selectable for config option '{option_id}'" - ))); + match option_id { + AIONRS_MODE_OPTION_ID => { + if !is_aionrs_session_mode(value) { + return Err(AgentError::bad_request(format!( + "Value '{value}' is not selectable for config option '{option_id}'" + ))); + } + self.set_mode(value).await?; + } + AIONRS_THINKING_OPTION_ID => { + if !is_aionrs_thinking_mode(value) { + return Err(AgentError::bad_request(format!( + "Value '{value}' is not selectable for config option '{option_id}'" + ))); + } + let Some(thinking_mode) = &self.thinking_mode else { + return Err(AgentError::bad_request(format!( + "Config option '{option_id}' is not available" + ))); + }; + self.engine + .lock() + .await + .apply_config_update(None, Some(value.to_owned()), None, None, None); + *thinking_mode.lock().await = value.to_owned(); + info!( + conversation_id = %self.runtime.conversation_id(), + thinking_mode = value, + "Aionrs thinking mode switched" + ); + } + _ => { + return Err(AgentError::bad_request(format!( + "Config option '{option_id}' is not available" + ))); + } } - - self.set_mode(value).await?; Ok(SetConfigOptionResponse { confirmation: ConfigOptionConfirmation::Observed, config_options: Some(self.config_options().await?.config_options), @@ -548,11 +579,16 @@ impl AionrsAgentManager { } const AIONRS_MODE_OPTION_ID: &str = "mode"; +const AIONRS_THINKING_OPTION_ID: &str = "thinking"; fn is_aionrs_session_mode(s: &str) -> bool { matches!(s, "default" | "auto_edit" | "yolo") } +fn is_aionrs_thinking_mode(s: &str) -> bool { + matches!(s, "enabled" | "disabled") +} + fn aionrs_mode_config_option(current_value: String) -> AcpConfigOptionDto { AcpConfigOptionDto { id: AIONRS_MODE_OPTION_ID.to_owned(), @@ -579,6 +615,22 @@ fn aionrs_mode_select_option(value: &str, name: &str) -> AcpConfigSelectOptionDt } } +fn aionrs_thinking_config_option(current_value: String) -> AcpConfigOptionDto { + AcpConfigOptionDto { + id: AIONRS_THINKING_OPTION_ID.to_owned(), + name: Some("Thinking".to_owned()), + label: None, + description: None, + category: Some("thought_level".to_owned()), + option_type: "select".to_owned(), + current_value: Some(current_value), + options: vec![ + aionrs_mode_select_option("enabled", "Enabled"), + aionrs_mode_select_option("disabled", "Disabled"), + ], + } +} + fn parse_session_mode(s: &str) -> SessionMode { match s { "auto_edit" => SessionMode::AutoEdit, @@ -626,6 +678,74 @@ mod tests { } } + fn make_glm_52_test_config() -> AionrsResolvedConfig { + let mut config = make_test_config(); + config.provider = "openai".into(); + config.model = "glm-5.2".into(); + config.base_url = Some("https://open.bigmodel.cn/api/paas/v4".into()); + config.compat_overrides.thinking_mode = Some("enabled".into()); + config + } + + #[tokio::test] + async fn glm_52_exposes_enabled_thinking_by_default() { + let agent = AionrsAgentManager::new("conv-glm".into(), "/project".into(), make_glm_52_test_config(), None) + .await + .unwrap(); + + let options = agent.config_options().await.unwrap().config_options; + let thinking = options + .iter() + .find(|option| option.id == AIONRS_THINKING_OPTION_ID) + .unwrap(); + + assert_eq!(thinking.category.as_deref(), Some("thought_level")); + assert_eq!(thinking.current_value.as_deref(), Some("enabled")); + assert_eq!( + thinking + .options + .iter() + .map(|option| option.value.as_str()) + .collect::>(), + vec!["enabled", "disabled"] + ); + } + + #[tokio::test] + async fn glm_52_thinking_can_be_disabled_for_the_next_request() { + let agent = AionrsAgentManager::new("conv-glm".into(), "/project".into(), make_glm_52_test_config(), None) + .await + .unwrap(); + + let response = agent + .set_config_option(AIONRS_THINKING_OPTION_ID, "disabled") + .await + .unwrap(); + assert_eq!(response.confirmation, ConfigOptionConfirmation::Observed); + let thinking = response + .config_options + .unwrap() + .into_iter() + .find(|option| option.id == AIONRS_THINKING_OPTION_ID) + .unwrap(); + + assert_eq!(thinking.current_value.as_deref(), Some("disabled")); + } + + #[tokio::test] + async fn unsupported_thinking_values_are_rejected() { + let agent = AionrsAgentManager::new("conv-glm".into(), "/project".into(), make_glm_52_test_config(), None) + .await + .unwrap(); + + let error = agent + .set_config_option(AIONRS_THINKING_OPTION_ID, "auto") + .await + .unwrap_err(); + + assert!(error.to_string().contains("is not selectable")); + } + #[test] fn aionrs_final_input_dump_value_contains_raw_split_input_and_context() { let mut mcp_env = std::collections::HashMap::new(); diff --git a/crates/aionui-ai-agent/src/types.rs b/crates/aionui-ai-agent/src/types.rs index d43c8abfc..dc5bd5a7a 100644 --- a/crates/aionui-ai-agent/src/types.rs +++ b/crates/aionui-ai-agent/src/types.rs @@ -102,6 +102,8 @@ impl RuntimeCapabilities { pub struct AionrsCompatOverrides { pub max_tokens_field: Option, pub api_path: Option, + /// Initial request-level thinking mode for models with a compatible API. + pub thinking_mode: Option, } /// Fully resolved Aionrs configuration passed to the agent manager. From 9a41c93282ee2fa481ad0358e71e63e112579c5f Mon Sep 17 00:00:00 2001 From: zhangyijun Date: Thu, 16 Jul 2026 19:21:39 +0800 Subject: [PATCH 2/3] fix(aionrs): support qualified GLM-5.2 model ids --- crates/aionui-ai-agent/src/factory/aionrs.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/aionui-ai-agent/src/factory/aionrs.rs b/crates/aionui-ai-agent/src/factory/aionrs.rs index 7bed8e9e3..43cf37ad9 100644 --- a/crates/aionui-ai-agent/src/factory/aionrs.rs +++ b/crates/aionui-ai-agent/src/factory/aionrs.rs @@ -213,7 +213,8 @@ pub(super) async fn build( fn glm_52_default_thinking_mode(model_id: &str) -> Option { let normalized = model_id.trim().to_ascii_lowercase(); - (normalized == "glm-5.2" || normalized.starts_with("glm-5.2-")).then(|| "enabled".to_owned()) + let model_name = normalized.rsplit('/').next().unwrap_or(&normalized); + (model_name == "glm-5.2" || model_name.starts_with("glm-5.2-")).then(|| "enabled".to_owned()) } /// Map AionUi DB platform/protocol settings to the aionrs provider identifier. @@ -1721,6 +1722,14 @@ mod tests { glm_52_default_thinking_mode("GLM-5.2-20260701").as_deref(), Some("enabled") ); + assert_eq!( + glm_52_default_thinking_mode("zhanlu/glm-5.2").as_deref(), + Some("enabled") + ); + assert_eq!( + glm_52_default_thinking_mode("z-ai/glm-5.2-20260701").as_deref(), + Some("enabled") + ); } #[test] From 2f548648d00834fadb1d6325a424e0942ef58d16 Mon Sep 17 00:00:00 2001 From: zhangyijun Date: Thu, 16 Jul 2026 20:10:14 +0800 Subject: [PATCH 3/3] fix(aionrs): emit disabled thinking for GLM Anthropic --- Cargo.lock | 46 ++++++++--------- Cargo.toml | 12 ++--- crates/aionui-ai-agent/src/factory/aionrs.rs | 27 +++++++++- .../src/manager/aionrs/agent.rs | 49 ++++++++++++++++--- crates/aionui-ai-agent/src/types.rs | 2 + 5 files changed, 99 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef292321b..a5ec59a0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,8 +117,8 @@ dependencies = [ [[package]] name = "aion-agent" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-compact", "aion-config", @@ -148,8 +148,8 @@ dependencies = [ [[package]] name = "aion-compact" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "regex", "serde", @@ -160,8 +160,8 @@ dependencies = [ [[package]] name = "aion-config" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-compact", "aion-process", @@ -185,8 +185,8 @@ dependencies = [ [[package]] name = "aion-mcp" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-config", "aion-protocol", @@ -206,8 +206,8 @@ dependencies = [ [[package]] name = "aion-memory" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-config", "chrono", @@ -220,8 +220,8 @@ dependencies = [ [[package]] name = "aion-process" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "libc", "tokio", @@ -231,8 +231,8 @@ dependencies = [ [[package]] name = "aion-protocol" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-types", "serde", @@ -244,8 +244,8 @@ dependencies = [ [[package]] name = "aion-providers" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-config", "aion-types", @@ -272,8 +272,8 @@ dependencies = [ [[package]] name = "aion-skills" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-config", "aion-mcp", @@ -299,8 +299,8 @@ dependencies = [ [[package]] name = "aion-tools" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "aion-config", "aion-process", @@ -320,8 +320,8 @@ dependencies = [ [[package]] name = "aion-types" -version = "0.2.3" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +version = "0.2.4" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "async-trait", "chrono", @@ -6547,7 +6547,7 @@ dependencies = [ [[package]] name = "workspace-hack" version = "0.1.0" -source = "git+https://github.com/iOfficeAI/aionrs.git?tag=v0.2.3#e884400a8d8629f22531f132facd6dca195701c6" +source = "git+https://github.com/GodfatherPacino/aionrs.git?rev=d91f2b2b70983ef4425f855543c627d4be9e3c0d#d91f2b2b70983ef4425f855543c627d4be9e3c0d" dependencies = [ "bitflags", "cc", diff --git a/Cargo.toml b/Cargo.toml index a1626286e..f83cc6392 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,12 +53,12 @@ aionui-cron = { path = "crates/aionui-cron" } aionui-assistant = { path = "crates/aionui-assistant" } aionui-app = { path = "crates/aionui-app" } -aion-agent = { git = "https://github.com/iOfficeAI/aionrs.git", tag = "v0.2.3" } -aion-providers = { git = "https://github.com/iOfficeAI/aionrs.git", tag = "v0.2.3" } -aion-types = { git = "https://github.com/iOfficeAI/aionrs.git", tag = "v0.2.3" } -aion-protocol = { git = "https://github.com/iOfficeAI/aionrs.git", tag = "v0.2.3" } -aion-config = { git = "https://github.com/iOfficeAI/aionrs.git", tag = "v0.2.3" } -aion-mcp = { git = "https://github.com/iOfficeAI/aionrs.git", tag = "v0.2.3" } +aion-agent = { git = "https://github.com/GodfatherPacino/aionrs.git", rev = "d91f2b2b70983ef4425f855543c627d4be9e3c0d" } +aion-providers = { git = "https://github.com/GodfatherPacino/aionrs.git", rev = "d91f2b2b70983ef4425f855543c627d4be9e3c0d" } +aion-types = { git = "https://github.com/GodfatherPacino/aionrs.git", rev = "d91f2b2b70983ef4425f855543c627d4be9e3c0d" } +aion-protocol = { git = "https://github.com/GodfatherPacino/aionrs.git", rev = "d91f2b2b70983ef4425f855543c627d4be9e3c0d" } +aion-config = { git = "https://github.com/GodfatherPacino/aionrs.git", rev = "d91f2b2b70983ef4425f855543c627d4be9e3c0d" } +aion-mcp = { git = "https://github.com/GodfatherPacino/aionrs.git", rev = "d91f2b2b70983ef4425f855543c627d4be9e3c0d" } # Core framework tokio = { version = "1", features = ["full"] } diff --git a/crates/aionui-ai-agent/src/factory/aionrs.rs b/crates/aionui-ai-agent/src/factory/aionrs.rs index 43cf37ad9..264409893 100644 --- a/crates/aionui-ai-agent/src/factory/aionrs.rs +++ b/crates/aionui-ai-agent/src/factory/aionrs.rs @@ -97,7 +97,7 @@ pub(super) async fn build( let (base_url, mut compat_overrides) = resolve_aionrs_url_and_compat(&row.platform, &row.base_url, &provider, row.is_full_url); - compat_overrides.thinking_mode = glm_52_default_thinking_mode(&model_id); + configure_glm_52_thinking_compat(&mut compat_overrides, &provider, &model_id); let bedrock_config = if row.platform == "bedrock" { resolve_bedrock_config(row.bedrock_config.as_deref()) @@ -217,6 +217,11 @@ fn glm_52_default_thinking_mode(model_id: &str) -> Option { (model_name == "glm-5.2" || model_name.starts_with("glm-5.2-")).then(|| "enabled".to_owned()) } +fn configure_glm_52_thinking_compat(compat_overrides: &mut AionrsCompatOverrides, provider: &str, model_id: &str) { + compat_overrides.thinking_mode = glm_52_default_thinking_mode(model_id); + compat_overrides.emit_disabled_thinking = provider == "anthropic" && compat_overrides.thinking_mode.is_some(); +} + /// Map AionUi DB platform/protocol settings to the aionrs provider identifier. pub(crate) fn map_aionrs_provider( platform: &str, @@ -1737,4 +1742,24 @@ mod tests { assert!(glm_52_default_thinking_mode("glm-5.1").is_none()); assert!(glm_52_default_thinking_mode("claude-sonnet-4").is_none()); } + + #[test] + fn anthropic_glm_52_emits_explicit_disabled_thinking() { + let mut compat = AionrsCompatOverrides::default(); + + configure_glm_52_thinking_compat(&mut compat, "anthropic", "zhanlu/glm-5.2-anthropic"); + + assert_eq!(compat.thinking_mode.as_deref(), Some("enabled")); + assert!(compat.emit_disabled_thinking); + } + + #[test] + fn openai_glm_52_keeps_default_disabled_serialization() { + let mut compat = AionrsCompatOverrides::default(); + + configure_glm_52_thinking_compat(&mut compat, "openai", "glm-5.2"); + + assert_eq!(compat.thinking_mode.as_deref(), Some("enabled")); + assert!(!compat.emit_disabled_thinking); + } } diff --git a/crates/aionui-ai-agent/src/manager/aionrs/agent.rs b/crates/aionui-ai-agent/src/manager/aionrs/agent.rs index 2ae14e90c..90259a73f 100644 --- a/crates/aionui-ai-agent/src/manager/aionrs/agent.rs +++ b/crates/aionui-ai-agent/src/manager/aionrs/agent.rs @@ -26,7 +26,7 @@ use crate::capability::backend_protocol_sink::BackendProtocolSink; use crate::error::AgentError; use crate::protocol::events::AgentStreamEvent; use crate::protocol::send_error::AgentSendError; -use crate::types::{AionrsResolvedConfig, SendMessageData}; +use crate::types::{AionrsCompatOverrides, AionrsResolvedConfig, SendMessageData}; use super::error::{aionrs_engine_error_to_send_error, aionrs_runtime_error_summary}; @@ -161,12 +161,7 @@ impl AionrsAgentManager { config.session.enabled = true; config.session.directory = config_extra.session_directory.to_string_lossy().into_owned(); - if let Some(field) = config_extra.compat_overrides.max_tokens_field { - config.compat.transport.max_tokens_field = Some(field); - } - if let Some(path) = config_extra.compat_overrides.api_path { - config.compat.transport.api_path = Some(path); - } + apply_aionrs_compat_overrides(&mut config, &config_extra.compat_overrides); if !config_extra.extra_mcp_servers.is_empty() { config.mcp.servers.extend(config_extra.extra_mcp_servers.clone()); @@ -313,6 +308,18 @@ impl AionrsAgentManager { } } +fn apply_aionrs_compat_overrides(config: &mut Config, overrides: &AionrsCompatOverrides) { + if let Some(field) = &overrides.max_tokens_field { + config.compat.transport.max_tokens_field = Some(field.clone()); + } + if let Some(path) = &overrides.api_path { + config.compat.transport.api_path = Some(path.clone()); + } + if overrides.emit_disabled_thinking { + config.compat.reasoning.emit_disabled_thinking = Some(true); + } +} + #[async_trait::async_trait] impl crate::agent_task::IAgentTask for AionrsAgentManager { fn agent_type(&self) -> AgentType { @@ -687,6 +694,34 @@ mod tests { config } + #[test] + fn glm_52_anthropic_compat_enables_explicit_disabled_thinking() { + let mut config = make_glm_52_test_config(); + config.provider = "anthropic".into(); + config.compat_overrides.emit_disabled_thinking = true; + + let cli_args = CliArgs { + provider: Some(config.provider.clone()), + api_key: Some(config.api_key.clone()), + base_url: config.base_url.clone(), + model: Some(config.model.clone()), + max_tokens: config.max_tokens, + max_turns: config.max_turns, + max_tool_call_malformed_turns: config.max_tool_call_malformed_turns, + max_tool_call_failure_turns: config.max_tool_call_failure_turns, + system_prompt: config.system_prompt.clone(), + profile: None, + auto_approve: false, + thinking: config.compat_overrides.thinking_mode.clone(), + thinking_budget: None, + project_dir: Some(PathBuf::from("/project")), + }; + let mut resolved = Config::resolve(&cli_args).unwrap(); + apply_aionrs_compat_overrides(&mut resolved, &config.compat_overrides); + + assert!(resolved.compat.emit_disabled_thinking()); + } + #[tokio::test] async fn glm_52_exposes_enabled_thinking_by_default() { let agent = AionrsAgentManager::new("conv-glm".into(), "/project".into(), make_glm_52_test_config(), None) diff --git a/crates/aionui-ai-agent/src/types.rs b/crates/aionui-ai-agent/src/types.rs index dc5bd5a7a..98a47c61e 100644 --- a/crates/aionui-ai-agent/src/types.rs +++ b/crates/aionui-ai-agent/src/types.rs @@ -104,6 +104,8 @@ pub struct AionrsCompatOverrides { pub api_path: Option, /// Initial request-level thinking mode for models with a compatible API. pub thinking_mode: Option, + /// Emit an explicit disabled thinking block instead of omitting it. + pub emit_disabled_thinking: bool, } /// Fully resolved Aionrs configuration passed to the agent manager.