Suppress duplicate assistant output on stdout in interactive sessions (#13082)
Addresses #12566 Summary - stop printing the final assistant message on stdout when the process is running in a terminal so interactive users only see it once - add a helper that gates the stdout emission and cover it with unit tests
This commit is contained in:
parent
70ed6cbc71
commit
6604608bad
1 changed files with 57 additions and 5 deletions
|
|
@ -41,6 +41,7 @@ use owo_colors::Style;
|
|||
use serde::Deserialize;
|
||||
use shlex::try_join;
|
||||
use std::collections::HashMap;
|
||||
use std::io::IsTerminal;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
|
@ -869,12 +870,17 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
|||
);
|
||||
}
|
||||
|
||||
// If the user has not piped the final message to a file, they will see
|
||||
// it twice: once written to stderr as part of the normal event
|
||||
// processing, and once here on stdout. We print the token summary above
|
||||
// to help break up the output visually in that case.
|
||||
// In interactive terminals we already emitted the final assistant
|
||||
// message on stderr during event processing. Preserve stdout emission
|
||||
// only for non-interactive use so pipes and scripts still receive the
|
||||
// final message.
|
||||
#[allow(clippy::print_stdout)]
|
||||
if let Some(message) = &self.final_message {
|
||||
if should_print_final_message_to_stdout(
|
||||
self.final_message.as_deref(),
|
||||
std::io::stdout().is_terminal(),
|
||||
std::io::stderr().is_terminal(),
|
||||
) && let Some(message) = &self.final_message
|
||||
{
|
||||
if message.ends_with('\n') {
|
||||
print!("{message}");
|
||||
} else {
|
||||
|
|
@ -1025,6 +1031,14 @@ impl EventProcessorWithHumanOutput {
|
|||
}
|
||||
}
|
||||
|
||||
fn should_print_final_message_to_stdout(
|
||||
final_message: Option<&str>,
|
||||
stdout_is_terminal: bool,
|
||||
stderr_is_terminal: bool,
|
||||
) -> bool {
|
||||
final_message.is_some() && !(stdout_is_terminal && stderr_is_terminal)
|
||||
}
|
||||
|
||||
struct AgentJobProgressStats {
|
||||
processed: usize,
|
||||
total: usize,
|
||||
|
|
@ -1192,3 +1206,41 @@ fn format_mcp_invocation(invocation: &McpInvocation) -> String {
|
|||
format!("{fq_tool_name}({args_str})")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::should_print_final_message_to_stdout;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn suppresses_final_stdout_message_when_both_streams_are_terminals() {
|
||||
assert_eq!(
|
||||
should_print_final_message_to_stdout(Some("hello"), true, true),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prints_final_stdout_message_when_stdout_is_not_terminal() {
|
||||
assert_eq!(
|
||||
should_print_final_message_to_stdout(Some("hello"), false, true),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prints_final_stdout_message_when_stderr_is_not_terminal() {
|
||||
assert_eq!(
|
||||
should_print_final_message_to_stdout(Some("hello"), true, false),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_print_when_message_is_missing() {
|
||||
assert_eq!(
|
||||
should_print_final_message_to_stdout(None, false, false),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue