From 0af7e4a195237220e49f576f13c2adf7a04a8bc6 Mon Sep 17 00:00:00 2001 From: Anton Panasenko Date: Thu, 11 Dec 2025 11:59:40 -0800 Subject: [PATCH] fix: omit reasoning summary when ReasoningSummary::None (#7845) ``` { "error": { "message": "Invalid value: 'none'. Supported values are: 'concise', 'detailed', and 'auto'.", "type": "invalid_request_error", "param": "reasoning.summary", "code": "invalid_value" } } ``` --- codex-rs/core/src/client.rs | 6 ++- codex-rs/core/tests/suite/client.rs | 76 +++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) 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(()));