Merge branch 'codex/add-raw-chain-of-thought-in-codex-cli' into dfyp9w-codex/implement-delta-based-streaming-rendering

This commit is contained in:
aibrahim-oai
2025-07-31 21:35:08 -07:00
committed by GitHub
9 changed files with 75 additions and 3 deletions
+10
View File
@@ -481,6 +481,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.
+25 -2
View File
@@ -55,6 +55,7 @@ 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;
@@ -63,6 +64,7 @@ use crate::plan_tool::handle_update_plan;
use crate::project_doc::get_user_instructions;
use crate::protocol::AgentMessageDeltaEvent;
use crate::protocol::AgentMessageEvent;
use crate::protocol::AgentReasoningContentEvent;
use crate::protocol::AgentReasoningDeltaEvent;
use crate::protocol::AgentReasoningEvent;
use crate::protocol::ApplyPatchApprovalRequestEvent;
@@ -224,6 +226,7 @@ pub(crate) struct Session {
state: Mutex<State>,
codex_linux_sandbox_exe: Option<PathBuf>,
user_shell: shell::Shell,
show_reasoning_content: bool,
}
impl Session {
@@ -765,6 +768,7 @@ async fn submission_loop(
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
disable_response_storage,
user_shell: default_shell,
show_reasoning_content: config.show_reasoning_content,
}));
// Patch restored state into the newly created session.
@@ -1048,6 +1052,7 @@ async fn run_task(sess: Arc<Session>, sub_id: String, input: Vec<InputItem>) {
id,
summary,
encrypted_content,
content,
},
None,
) => {
@@ -1055,6 +1060,7 @@ async fn run_task(sess: Arc<Session>, sub_id: String, input: Vec<InputItem>) {
id: id.clone(),
summary: summary.clone(),
encrypted_content: encrypted_content.clone(),
content: content.clone(),
});
}
_ => {
@@ -1228,7 +1234,6 @@ async fn try_run_turn(
};
let mut stream = sess.client.clone().stream(&prompt).await?;
let mut output = Vec::new();
loop {
// Poll the next item from the model stream. We must inspect *both* Ok and Err
@@ -1312,7 +1317,12 @@ async fn handle_response_item(
}
None
}
ResponseItem::Reasoning { summary, .. } => {
ResponseItem::Reasoning {
id: _,
summary,
content,
encrypted_content: _,
} => {
for item in summary {
let text = match item {
ReasoningItemReasoningSummary::SummaryText { text } => text,
@@ -1323,6 +1333,19 @@ async fn handle_response_item(
};
sess.tx_event.send(event).await.ok();
}
if sess.show_reasoning_content && content.is_some() {
let content = content.unwrap();
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 {
+12
View File
@@ -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).
@@ -322,6 +326,10 @@ pub struct ConfigToml {
/// UI/output. Defaults to `false`.
pub hide_agent_reasoning: Option<bool>,
/// When set to `true`, raw chain-of-thought text from reasoning events will
/// be shown in the UI.
pub show_reasoning_content: Option<bool>,
pub model_reasoning_effort: Option<ReasoningEffort>,
pub model_reasoning_summary: Option<ReasoningSummary>,
@@ -509,6 +517,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)
@@ -880,6 +889,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,
@@ -929,6 +939,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,
@@ -993,6 +1004,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,
+8
View File
@@ -45,6 +45,8 @@ pub enum ResponseItem {
Reasoning {
id: String,
summary: Vec<ReasoningItemReasoningSummary>,
#[serde(default, skip_serializing_if = "Option::is_none")]
content: Option<Vec<ReasoningItemContent>>,
encrypted_content: Option<String>,
},
LocalShellCall {
@@ -136,6 +138,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<Vec<InputItem>> for ResponseInputItem {
fn from(items: Vec<InputItem>) -> Self {
Self::Message {
+8
View File
@@ -309,6 +309,9 @@ pub enum EventMsg {
/// Agent reasoning delta event from agent.
AgentReasoningDelta(AgentReasoningDeltaEvent),
/// Raw chain-of-thought from agent.
AgentReasoningContent(AgentReasoningContentEvent),
/// Ack the client's configure message.
SessionConfigured(SessionConfiguredEvent),
@@ -409,6 +412,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 AgentReasoningDeltaEvent {
pub delta: String,
-1
View File
@@ -40,7 +40,6 @@ pub(crate) fn create_config_summary_entries(config: &Config) -> Vec<(&'static st
config.model_reasoning_summary.to_string(),
));
}
entries
}
@@ -4,6 +4,7 @@ use codex_core::config::Config;
use codex_core::plan_tool::UpdatePlanArgs;
use codex_core::protocol::AgentMessageDeltaEvent;
use codex_core::protocol::AgentMessageEvent;
use codex_core::protocol::AgentReasoningContentEvent;
use codex_core::protocol::AgentReasoningDeltaEvent;
use codex_core::protocol::BackgroundEventEvent;
use codex_core::protocol::ErrorEvent;
@@ -204,6 +205,11 @@ impl EventProcessor for EventProcessorWithHumanOutput {
#[allow(clippy::expect_used)]
std::io::stdout().flush().expect("could not flush stdout");
}
EventMsg::AgentReasoningContent(AgentReasoningContentEvent { text }) => {
print!("{text}");
#[allow(clippy::expect_used)]
std::io::stdout().flush().expect("could not flush stdout");
}
EventMsg::AgentMessage(AgentMessageEvent { message }) => {
// if answer_started is false, this means we haven't received any
// delta. Thus, we need to print the message as a new answer.
@@ -255,6 +255,7 @@ async fn run_codex_tool_session_inner(
EventMsg::TaskStarted
| EventMsg::TokenCount(_)
| EventMsg::AgentReasoning(_)
| EventMsg::AgentReasoningContent(_)
| EventMsg::McpToolCallBegin(_)
| EventMsg::McpToolCallEnd(_)
| EventMsg::ExecCommandBegin(_)
+5
View File
@@ -8,6 +8,7 @@ use codex_core::codex_wrapper::init_codex;
use codex_core::config::Config;
use codex_core::protocol::AgentMessageDeltaEvent;
use codex_core::protocol::AgentMessageEvent;
use codex_core::protocol::AgentReasoningContentEvent;
use codex_core::protocol::AgentReasoningDeltaEvent;
use codex_core::protocol::AgentReasoningEvent;
use codex_core::protocol::ApplyPatchApprovalRequestEvent;
@@ -318,6 +319,10 @@ impl ChatWidget<'_> {
self.request_redraw();
}
}
EventMsg::AgentReasoningContent(AgentReasoningContentEvent { text }) => {
self.add_to_history(HistoryCell::new_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);