diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 72c23a3ea..9659b1090 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -206,7 +206,11 @@ impl ModelClient { let reasoning = if model_family.supports_reasoning_summaries { Some(Reasoning { effort: self.effort.or(model_family.default_reasoning_effort), - summary: Some(self.summary), + summary: if self.summary == ReasoningSummaryConfig::None { + None + } else { + Some(self.summary) + }, }) } else { None diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index faa9801f8..4d47c9f21 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -22,6 +22,7 @@ use codex_core::protocol::Op; use codex_core::protocol::SessionSource; use codex_otel::otel_event_manager::OtelEventManager; use codex_protocol::ConversationId; +use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::Verbosity; use codex_protocol::models::ReasoningItemContent; use codex_protocol::models::ReasoningItemReasoningSummary; @@ -823,6 +824,81 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_fami Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> { + skip_if_no_network!(Ok(())); + let server = MockServer::start().await; + + let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let TestCodex { codex, .. } = test_codex() + .with_config(|config| { + config.model_reasoning_summary = ReasoningSummary::Concise; + }) + .build(&server) + .await?; + + codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "hello".into(), + }], + }) + .await + .unwrap(); + + wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; + + let request = resp_mock.single_request(); + let request_body = request.body_json(); + + pretty_assertions::assert_eq!( + request_body + .get("reasoning") + .and_then(|reasoning| reasoning.get("summary")) + .and_then(|value| value.as_str()), + Some("concise") + ); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> { + skip_if_no_network!(Ok(())); + let server = MockServer::start().await; + + let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let TestCodex { codex, .. } = test_codex() + .with_config(|config| { + config.model_reasoning_summary = ReasoningSummary::None; + }) + .build(&server) + .await?; + + codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "hello".into(), + }], + }) + .await + .unwrap(); + + wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; + + let request = resp_mock.single_request(); + let request_body = request.body_json(); + + pretty_assertions::assert_eq!( + request_body + .get("reasoning") + .and_then(|reasoning| reasoning.get("summary")), + None + ); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn includes_default_verbosity_in_request() -> anyhow::Result<()> { skip_if_no_network!(Ok(()));