feat: include "reasoning" messages in Rust TUI (#892)
As shown in the screenshot, we now include reasoning messages from the model in the TUI under the heading "codex reasoning":  To ensure these are visible by default when using `o4-mini`, this also changes the default value for `summary` (formerly `generate_summary`, which is deprecated in favor of `summary` according to the docs) from unset to `"auto"`.
This commit is contained in:
@@ -26,6 +26,7 @@ use crate::client_common::Prompt;
|
||||
use crate::client_common::Reasoning;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::client_common::ResponseStream;
|
||||
use crate::client_common::Summary;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result;
|
||||
use crate::flags::CODEX_RS_SSE_FIXTURE;
|
||||
@@ -173,7 +174,7 @@ impl ModelClient {
|
||||
parallel_tool_calls: false,
|
||||
reasoning: Some(Reasoning {
|
||||
effort: "high",
|
||||
generate_summary: None,
|
||||
summary: Some(Summary::Auto),
|
||||
}),
|
||||
previous_response_id: prompt.prev_id.clone(),
|
||||
store: prompt.store,
|
||||
|
||||
@@ -36,7 +36,19 @@ pub enum ResponseEvent {
|
||||
pub(crate) struct Reasoning {
|
||||
pub(crate) effort: &'static str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) generate_summary: Option<bool>,
|
||||
pub(crate) summary: Option<Summary>,
|
||||
}
|
||||
|
||||
/// A summary of the reasoning performed by the model. This can be useful for
|
||||
/// debugging and understanding the model's reasoning process.
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub(crate) enum Summary {
|
||||
Auto,
|
||||
#[allow(dead_code)] // Will go away once this is configurable.
|
||||
Concise,
|
||||
#[allow(dead_code)] // Will go away once this is configurable.
|
||||
Detailed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
||||
@@ -49,6 +49,7 @@ use crate::mcp_connection_manager::try_parse_fully_qualified_tool_name;
|
||||
use crate::mcp_tool_call::handle_mcp_tool_call;
|
||||
use crate::models::ContentItem;
|
||||
use crate::models::FunctionCallOutputPayload;
|
||||
use crate::models::ReasoningItemReasoningSummary;
|
||||
use crate::models::ResponseInputItem;
|
||||
use crate::models::ResponseItem;
|
||||
use crate::models::ShellToolCallParams;
|
||||
@@ -934,6 +935,18 @@ async fn handle_response_item(
|
||||
}
|
||||
}
|
||||
}
|
||||
ResponseItem::Reasoning { id: _, summary } => {
|
||||
for item in summary {
|
||||
let text = match item {
|
||||
ReasoningItemReasoningSummary::SummaryText { text } => text,
|
||||
};
|
||||
let event = Event {
|
||||
id: sub_id.to_string(),
|
||||
msg: EventMsg::AgentReasoning { text },
|
||||
};
|
||||
sess.tx_event.send(event).await.ok();
|
||||
}
|
||||
}
|
||||
ResponseItem::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
|
||||
@@ -33,6 +33,10 @@ pub enum ResponseItem {
|
||||
role: String,
|
||||
content: Vec<ContentItem>,
|
||||
},
|
||||
Reasoning {
|
||||
id: String,
|
||||
summary: Vec<ReasoningItemReasoningSummary>,
|
||||
},
|
||||
FunctionCall {
|
||||
name: String,
|
||||
// The Responses API returns the function call arguments as a *string* that contains
|
||||
@@ -67,6 +71,12 @@ impl From<ResponseInputItem> for ResponseItem {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ReasoningItemReasoningSummary {
|
||||
SummaryText { text: String },
|
||||
}
|
||||
|
||||
impl From<Vec<InputItem>> for ResponseInputItem {
|
||||
fn from(items: Vec<InputItem>) -> Self {
|
||||
Self::Message {
|
||||
|
||||
@@ -317,6 +317,11 @@ pub enum EventMsg {
|
||||
message: String,
|
||||
},
|
||||
|
||||
/// Reasoning event from agent.
|
||||
AgentReasoning {
|
||||
text: String,
|
||||
},
|
||||
|
||||
/// Ack the client's configure message.
|
||||
SessionConfigured {
|
||||
/// Tell the client what model is being queried.
|
||||
|
||||
@@ -114,7 +114,7 @@ impl RolloutRecorder {
|
||||
ResponseItem::Message { .. }
|
||||
| ResponseItem::FunctionCall { .. }
|
||||
| ResponseItem::FunctionCallOutput { .. } => {}
|
||||
ResponseItem::Other => {
|
||||
ResponseItem::Reasoning { .. } | ResponseItem::Other => {
|
||||
// These should never be serialized.
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -225,6 +225,10 @@ impl ChatWidget<'_> {
|
||||
self.conversation_history.add_agent_message(message);
|
||||
self.request_redraw()?;
|
||||
}
|
||||
EventMsg::AgentReasoning { text } => {
|
||||
self.conversation_history.add_agent_reasoning(text);
|
||||
self.request_redraw()?;
|
||||
}
|
||||
EventMsg::TaskStarted => {
|
||||
self.bottom_pane.set_task_running(true)?;
|
||||
self.request_redraw()?;
|
||||
|
||||
@@ -174,6 +174,10 @@ impl ConversationHistoryWidget {
|
||||
self.add_to_history(HistoryCell::new_agent_message(message));
|
||||
}
|
||||
|
||||
pub fn add_agent_reasoning(&mut self, text: String) {
|
||||
self.add_to_history(HistoryCell::new_agent_reasoning(text));
|
||||
}
|
||||
|
||||
pub fn add_background_event(&mut self, message: String) {
|
||||
self.add_to_history(HistoryCell::new_background_event(message));
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ pub(crate) enum HistoryCell {
|
||||
/// Message from the agent.
|
||||
AgentMessage { lines: Vec<Line<'static>> },
|
||||
|
||||
/// Reasoning event from the agent.
|
||||
AgentReasoning { lines: Vec<Line<'static>> },
|
||||
|
||||
/// An exec tool call that has not finished yet.
|
||||
ActiveExecCommand {
|
||||
call_id: String,
|
||||
@@ -134,6 +137,15 @@ impl HistoryCell {
|
||||
HistoryCell::AgentMessage { lines }
|
||||
}
|
||||
|
||||
pub(crate) fn new_agent_reasoning(text: String) -> Self {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
lines.push(Line::from("codex reasoning".magenta().italic()));
|
||||
append_markdown(&text, &mut lines);
|
||||
lines.push(Line::from(""));
|
||||
|
||||
HistoryCell::AgentReasoning { lines }
|
||||
}
|
||||
|
||||
pub(crate) fn new_active_exec_command(call_id: String, command: Vec<String>) -> Self {
|
||||
let command_escaped = escape_command(&command);
|
||||
let start = Instant::now();
|
||||
@@ -363,6 +375,7 @@ impl HistoryCell {
|
||||
HistoryCell::WelcomeMessage { lines, .. }
|
||||
| HistoryCell::UserPrompt { lines, .. }
|
||||
| HistoryCell::AgentMessage { lines, .. }
|
||||
| HistoryCell::AgentReasoning { lines, .. }
|
||||
| HistoryCell::BackgroundEvent { lines, .. }
|
||||
| HistoryCell::ErrorEvent { lines, .. }
|
||||
| HistoryCell::SessionInfo { lines, .. }
|
||||
|
||||
Reference in New Issue
Block a user