From caab634c57a6feee21da6714bd8d1cb1ae5c6ce3 Mon Sep 17 00:00:00 2001 From: aibrahim-oai Date: Sun, 13 Jul 2025 23:39:56 -0700 Subject: [PATCH] feat(cli): show raw reasoning content --- codex-rs/config.md | 10 ++++++++++ codex-rs/core/src/codex.rs | 14 +++++++++++++- codex-rs/core/src/config.rs | 12 ++++++++++++ codex-rs/core/src/models.rs | 8 ++++++++ codex-rs/core/src/protocol.rs | 10 +++++++++- codex-rs/exec/src/event_processor.rs | 20 +++++++++++++++++++- codex-rs/exec/src/lib.rs | 7 +++++-- codex-rs/mcp-server/src/codex_tool_runner.rs | 1 + codex-rs/tui/src/chatwidget.rs | 8 ++++++++ 9 files changed, 85 insertions(+), 5 deletions(-) diff --git a/codex-rs/config.md b/codex-rs/config.md index 438b7e767d..5399513c85 100644 --- a/codex-rs/config.md +++ b/codex-rs/config.md @@ -452,6 +452,16 @@ Setting `hide_agent_reasoning` to `true` suppresses these events in **both** the hide_agent_reasoning = true # defaults to false ``` +## show_reasoning_content + +When enabled, Codex will display the raw chain-of-thought text returned by the +model for reasoning events. This content is not moderated and may contain +hallucinations or other undesirable text, so it is disabled by default. + +```toml +show_reasoning_content = true # defaults to false +``` + ## model_context_window The size of the context window for the model, in tokens. diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 52c37c51ee..c3b20ef7e1 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -56,12 +56,14 @@ use crate::mcp_tool_call::handle_mcp_tool_call; use crate::models::ContentItem; use crate::models::FunctionCallOutputPayload; use crate::models::LocalShellAction; +use crate::models::ReasoningItemContent; use crate::models::ReasoningItemReasoningSummary; use crate::models::ResponseInputItem; use crate::models::ResponseItem; use crate::models::ShellToolCallParams; use crate::project_doc::get_user_instructions; use crate::protocol::AgentMessageEvent; +use crate::protocol::AgentReasoningContentEvent; use crate::protocol::AgentReasoningEvent; use crate::protocol::ApplyPatchApprovalRequestEvent; use crate::protocol::AskForApproval; @@ -1196,7 +1198,7 @@ async fn handle_response_item( } None } - ResponseItem::Reasoning { id: _, summary } => { + ResponseItem::Reasoning { id: _, summary, content } => { for item in summary { let text = match item { ReasoningItemReasoningSummary::SummaryText { text } => text, @@ -1207,6 +1209,16 @@ async fn handle_response_item( }; sess.tx_event.send(event).await.ok(); } + for item in content { + let text = match item { + ReasoningItemContent::ReasoningText { text } => text, + }; + let event = Event { + id: sub_id.to_string(), + msg: EventMsg::AgentReasoningContent(AgentReasoningContentEvent { text }), + }; + sess.tx_event.send(event).await.ok(); + } None } ResponseItem::FunctionCall { diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index d67e692fc8..e6a094346c 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -57,6 +57,10 @@ pub struct Config { /// users are only interested in the final agent responses. pub hide_agent_reasoning: bool, + /// When `true`, the raw chain-of-thought text from reasoning events will be + /// displayed in the UI in addition to the reasoning summaries. + pub show_reasoning_content: bool, + /// Disable server-side response storage (sends the full conversation /// context with every request). Currently necessary for OpenAI customers /// who have opted into Zero Data Retention (ZDR). @@ -313,6 +317,10 @@ pub struct ConfigToml { /// UI/output. Defaults to `false`. pub hide_agent_reasoning: Option, + /// When set to `true`, raw chain-of-thought text from reasoning events will + /// be shown in the UI. + pub show_reasoning_content: Option, + pub model_reasoning_effort: Option, pub model_reasoning_summary: Option, @@ -477,6 +485,7 @@ impl Config { codex_linux_sandbox_exe, hide_agent_reasoning: cfg.hide_agent_reasoning.unwrap_or(false), + show_reasoning_content: cfg.show_reasoning_content.unwrap_or(false), model_reasoning_effort: config_profile .model_reasoning_effort .or(cfg.model_reasoning_effort) @@ -796,6 +805,7 @@ disable_response_storage = true tui: Tui::default(), codex_linux_sandbox_exe: None, hide_agent_reasoning: false, + show_reasoning_content: false, model_reasoning_effort: ReasoningEffort::High, model_reasoning_summary: ReasoningSummary::Detailed, model_supports_reasoning_summaries: false, @@ -842,6 +852,7 @@ disable_response_storage = true tui: Tui::default(), codex_linux_sandbox_exe: None, hide_agent_reasoning: false, + show_reasoning_content: false, model_reasoning_effort: ReasoningEffort::default(), model_reasoning_summary: ReasoningSummary::default(), model_supports_reasoning_summaries: false, @@ -903,6 +914,7 @@ disable_response_storage = true tui: Tui::default(), codex_linux_sandbox_exe: None, hide_agent_reasoning: false, + show_reasoning_content: false, model_reasoning_effort: ReasoningEffort::default(), model_reasoning_summary: ReasoningSummary::default(), model_supports_reasoning_summaries: false, diff --git a/codex-rs/core/src/models.rs b/codex-rs/core/src/models.rs index 6b392fb19d..864ab1d799 100644 --- a/codex-rs/core/src/models.rs +++ b/codex-rs/core/src/models.rs @@ -43,6 +43,8 @@ pub enum ResponseItem { Reasoning { id: String, summary: Vec, + #[serde(default)] + content: Vec, }, LocalShellCall { /// Set when using the chat completions API. @@ -128,6 +130,12 @@ pub enum ReasoningItemReasoningSummary { SummaryText { text: String }, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ReasoningItemContent { + ReasoningText { text: String }, +} + impl From> for ResponseInputItem { fn from(items: Vec) -> Self { Self::Message { diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs index fa25a2fe38..dd64b4c8a8 100644 --- a/codex-rs/core/src/protocol.rs +++ b/codex-rs/core/src/protocol.rs @@ -282,9 +282,12 @@ pub enum EventMsg { /// Agent text output message AgentMessage(AgentMessageEvent), - /// Reasoning event from agent. + /// Reasoning summary from agent. AgentReasoning(AgentReasoningEvent), + /// Raw chain-of-thought from agent. + AgentReasoningContent(AgentReasoningContentEvent), + /// Ack the client's configure message. SessionConfigured(SessionConfiguredEvent), @@ -345,6 +348,11 @@ pub struct AgentReasoningEvent { pub text: String, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct AgentReasoningContentEvent { + pub text: String, +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct McpToolCallBeginEvent { /// Identifier so this can be paired with the McpToolCallEnd event. diff --git a/codex-rs/exec/src/event_processor.rs b/codex-rs/exec/src/event_processor.rs index 540e014298..03100ce455 100644 --- a/codex-rs/exec/src/event_processor.rs +++ b/codex-rs/exec/src/event_processor.rs @@ -15,6 +15,7 @@ use codex_core::protocol::McpToolCallBeginEvent; use codex_core::protocol::McpToolCallEndEvent; use codex_core::protocol::PatchApplyBeginEvent; use codex_core::protocol::PatchApplyEndEvent; +use codex_core::protocol::AgentReasoningContentEvent; use codex_core::protocol::SessionConfiguredEvent; use codex_core::protocol::TokenUsage; use owo_colors::OwoColorize; @@ -50,10 +51,15 @@ pub(crate) struct EventProcessor { /// Whether to include `AgentReasoning` events in the output. show_agent_reasoning: bool, + show_agent_reasoning_content: bool, } impl EventProcessor { - pub(crate) fn create_with_ansi(with_ansi: bool, show_agent_reasoning: bool) -> Self { + pub(crate) fn create_with_ansi( + with_ansi: bool, + show_agent_reasoning: bool, + show_agent_reasoning_content: bool, + ) -> Self { let call_id_to_command = HashMap::new(); let call_id_to_patch = HashMap::new(); let call_id_to_tool_call = HashMap::new(); @@ -71,6 +77,7 @@ impl EventProcessor { cyan: Style::new().cyan(), call_id_to_tool_call, show_agent_reasoning, + show_agent_reasoning_content, } } else { Self { @@ -85,6 +92,7 @@ impl EventProcessor { cyan: Style::new(), call_id_to_tool_call, show_agent_reasoning, + show_agent_reasoning_content, } } } @@ -449,6 +457,16 @@ impl EventProcessor { ); } } + EventMsg::AgentReasoningContent(agent_reasoning_event) => { + if self.show_agent_reasoning && self.show_agent_reasoning_content { + ts_println!( + self, + "{}\n{}", + "thinking".style(self.italic).style(self.magenta), + agent_reasoning_event.text + ); + } + } EventMsg::SessionConfigured(session_configured_event) => { let SessionConfiguredEvent { session_id, diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 44dddd4d0f..85f154c5aa 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -115,8 +115,11 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> any }; let config = Config::load_with_cli_overrides(cli_kv_overrides, overrides)?; - let mut event_processor = - EventProcessor::create_with_ansi(stdout_with_ansi, !config.hide_agent_reasoning); + let mut event_processor = EventProcessor::create_with_ansi( + stdout_with_ansi, + !config.hide_agent_reasoning, + config.show_reasoning_content, + ); // Print the effective configuration and prompt so users can see what Codex // is using. event_processor.print_config_summary(&config, &prompt); diff --git a/codex-rs/mcp-server/src/codex_tool_runner.rs b/codex-rs/mcp-server/src/codex_tool_runner.rs index 7c3b02fe5e..26f0636ae6 100644 --- a/codex-rs/mcp-server/src/codex_tool_runner.rs +++ b/codex-rs/mcp-server/src/codex_tool_runner.rs @@ -175,6 +175,7 @@ pub async fn run_codex_tool_session( | EventMsg::TaskStarted | EventMsg::TokenCount(_) | EventMsg::AgentReasoning(_) + | EventMsg::AgentReasoningContent(_) | EventMsg::McpToolCallBegin(_) | EventMsg::McpToolCallEnd(_) | EventMsg::ExecCommandBegin(_) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 865e339763..f1923be1a1 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use codex_core::codex_wrapper::init_codex; use codex_core::config::Config; use codex_core::protocol::AgentMessageEvent; +use codex_core::protocol::AgentReasoningContentEvent; use codex_core::protocol::AgentReasoningEvent; use codex_core::protocol::ApplyPatchApprovalRequestEvent; use codex_core::protocol::ErrorEvent; @@ -251,6 +252,13 @@ impl ChatWidget<'_> { self.request_redraw(); } } + EventMsg::AgentReasoningContent(AgentReasoningContentEvent { text }) => { + if !self.config.hide_agent_reasoning && self.config.show_reasoning_content { + self.conversation_history + .add_agent_reasoning(&self.config, text); + self.request_redraw(); + } + } EventMsg::TaskStarted => { self.bottom_pane.clear_ctrl_c_quit_hint(); self.bottom_pane.set_task_running(true);