tool calling boxes
This commit is contained in:
@@ -31,6 +31,7 @@ const TERMINAL_DARK_AMBER: Color = Color::Rgb(204, 119, 34); // Dark amber for P
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TuiMessage {
|
pub enum TuiMessage {
|
||||||
AgentOutput(String),
|
AgentOutput(String),
|
||||||
|
ToolOutput { name: String, content: String },
|
||||||
SystemStatus(String),
|
SystemStatus(String),
|
||||||
ContextUpdate {
|
ContextUpdate {
|
||||||
used: u32,
|
used: u32,
|
||||||
@@ -91,6 +92,64 @@ impl TerminalState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format tool call output with a box
|
||||||
|
fn format_tool_output(&mut self, tool_name: &str, content: &str) {
|
||||||
|
// Calculate box width (use a reasonable width, accounting for terminal size)
|
||||||
|
let box_width = 80;
|
||||||
|
let border_char = "─";
|
||||||
|
let corner_tl = "┌";
|
||||||
|
let corner_tr = "┐";
|
||||||
|
let corner_bl = "└";
|
||||||
|
let corner_br = "┘";
|
||||||
|
let vertical = "│";
|
||||||
|
|
||||||
|
// Add top border
|
||||||
|
self.output_history.push(format!(
|
||||||
|
"{}{}{}",
|
||||||
|
corner_tl,
|
||||||
|
border_char.repeat(box_width - 2),
|
||||||
|
corner_tr
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add header with tool name (will be styled with green background in draw)
|
||||||
|
let header_text = format!(" {} ", tool_name.to_uppercase());
|
||||||
|
let padding = box_width - 2 - header_text.len();
|
||||||
|
self.output_history.push(format!(
|
||||||
|
"{}[TOOL_HEADER]{}{}{}",
|
||||||
|
vertical,
|
||||||
|
header_text,
|
||||||
|
" ".repeat(padding),
|
||||||
|
vertical
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add separator between header and content
|
||||||
|
self.output_history.push(format!(
|
||||||
|
"{}{}{}",
|
||||||
|
"├",
|
||||||
|
border_char.repeat(box_width - 2),
|
||||||
|
"┤"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add content lines
|
||||||
|
for line in content.lines() {
|
||||||
|
// Wrap long lines if needed
|
||||||
|
let max_content_width = box_width - 4; // Account for borders and padding
|
||||||
|
if line.len() <= max_content_width {
|
||||||
|
self.output_history.push(format!("{} {:<width$} {}", vertical, line, vertical, width = max_content_width));
|
||||||
|
} else {
|
||||||
|
// Simple word wrapping for long lines
|
||||||
|
for chunk in line.chars().collect::<Vec<_>>().chunks(max_content_width) {
|
||||||
|
let chunk_str: String = chunk.iter().collect();
|
||||||
|
self.output_history.push(format!("{} {:<width$} {}", vertical, chunk_str, vertical, width = max_content_width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add bottom border
|
||||||
|
self.output_history.push(format!("{}{}{}", corner_bl, border_char.repeat(box_width - 2), corner_br));
|
||||||
|
self.output_history.push(String::new()); // Empty line after box
|
||||||
|
}
|
||||||
|
|
||||||
/// Add text to output history
|
/// Add text to output history
|
||||||
fn add_output(&mut self, text: &str) {
|
fn add_output(&mut self, text: &str) {
|
||||||
// Split text by newlines and add each line
|
// Split text by newlines and add each line
|
||||||
@@ -142,6 +201,9 @@ impl RetroTui {
|
|||||||
TuiMessage::AgentOutput(text) => {
|
TuiMessage::AgentOutput(text) => {
|
||||||
state.add_output(&text);
|
state.add_output(&text);
|
||||||
}
|
}
|
||||||
|
TuiMessage::ToolOutput { name, content } => {
|
||||||
|
state.format_tool_output(&name, &content);
|
||||||
|
}
|
||||||
TuiMessage::SystemStatus(status) => {
|
TuiMessage::SystemStatus(status) => {
|
||||||
state.status_line = status;
|
state.status_line = status;
|
||||||
}
|
}
|
||||||
@@ -293,6 +355,24 @@ impl RetroTui {
|
|||||||
.skip(scroll)
|
.skip(scroll)
|
||||||
.take(visible_height)
|
.take(visible_height)
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
|
// Check if this is a tool header line
|
||||||
|
if line.contains("[TOOL_HEADER]") {
|
||||||
|
// Extract the actual header text
|
||||||
|
let cleaned = line.replace("[TOOL_HEADER]", "");
|
||||||
|
// Style with green background and black text
|
||||||
|
return Line::from(Span::styled(
|
||||||
|
format!(" {}", cleaned),
|
||||||
|
Style::default()
|
||||||
|
.bg(TERMINAL_GREEN)
|
||||||
|
.fg(Color::Black)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a box border line
|
||||||
|
if line.starts_with("┌") || line.starts_with("└") || line.starts_with("│") || line.starts_with("├") {
|
||||||
|
return Line::from(Span::styled(format!(" {}", line), Style::default().fg(TERMINAL_DIM_GREEN)));
|
||||||
|
}
|
||||||
// Apply different colors based on content
|
// Apply different colors based on content
|
||||||
let style = if line.starts_with("ERROR:") {
|
let style = if line.starts_with("ERROR:") {
|
||||||
Style::default()
|
Style::default()
|
||||||
@@ -445,6 +525,11 @@ impl RetroTui {
|
|||||||
let _ = self.tx.send(TuiMessage::AgentOutput(text.to_string()));
|
let _ = self.tx.send(TuiMessage::AgentOutput(text.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send tool output to the terminal
|
||||||
|
pub fn tool_output(&self, name: &str, content: &str) {
|
||||||
|
let _ = self.tx.send(TuiMessage::ToolOutput { name: name.to_string(), content: content.to_string() });
|
||||||
|
}
|
||||||
|
|
||||||
/// Update system status
|
/// Update system status
|
||||||
pub fn status(&self, status: &str) {
|
pub fn status(&self, status: &str) {
|
||||||
let _ = self.tx.send(TuiMessage::SystemStatus(status.to_string()));
|
let _ = self.tx.send(TuiMessage::SystemStatus(status.to_string()));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::retro_tui::RetroTui;
|
use crate::retro_tui::RetroTui;
|
||||||
use g3_core::ui_writer::UiWriter;
|
use g3_core::ui_writer::UiWriter;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
/// Console implementation of UiWriter that prints to stdout
|
/// Console implementation of UiWriter that prints to stdout
|
||||||
pub struct ConsoleUiWriter;
|
pub struct ConsoleUiWriter;
|
||||||
@@ -84,11 +85,13 @@ impl UiWriter for ConsoleUiWriter {
|
|||||||
/// RetroTui implementation of UiWriter that sends output to the TUI
|
/// RetroTui implementation of UiWriter that sends output to the TUI
|
||||||
pub struct RetroTuiWriter {
|
pub struct RetroTuiWriter {
|
||||||
tui: RetroTui,
|
tui: RetroTui,
|
||||||
|
current_tool_name: Mutex<Option<String>>,
|
||||||
|
current_tool_output: Mutex<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetroTuiWriter {
|
impl RetroTuiWriter {
|
||||||
pub fn new(tui: RetroTui) -> Self {
|
pub fn new(tui: RetroTui) -> Self {
|
||||||
Self { tui }
|
Self { tui, current_tool_name: Mutex::new(None), current_tool_output: Mutex::new(Vec::new()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,32 +124,45 @@ impl UiWriter for RetroTuiWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_header(&self, tool_name: &str) {
|
fn print_tool_header(&self, tool_name: &str) {
|
||||||
self.tui.output(&format!("┌─ {}", tool_name));
|
// Start collecting tool output
|
||||||
|
*self.current_tool_name.lock().unwrap() = Some(tool_name.to_string());
|
||||||
|
self.current_tool_output.lock().unwrap().clear();
|
||||||
|
self.current_tool_output.lock().unwrap().push(format!("Tool: {}", tool_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_arg(&self, key: &str, value: &str) {
|
fn print_tool_arg(&self, key: &str, value: &str) {
|
||||||
self.tui.output(&format!("│ {}: {}", key, value));
|
self.current_tool_output.lock().unwrap().push(format!("{}: {}", key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_output_header(&self) {
|
fn print_tool_output_header(&self) {
|
||||||
self.tui.output("├─ output:");
|
self.current_tool_output.lock().unwrap().push(String::new());
|
||||||
|
self.current_tool_output.lock().unwrap().push("Output:".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_output_line(&self, line: &str) {
|
fn print_tool_output_line(&self, line: &str) {
|
||||||
self.tui.output(&format!("│ {}", line));
|
self.current_tool_output.lock().unwrap().push(line.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_output_summary(&self, hidden_count: usize) {
|
fn print_tool_output_summary(&self, hidden_count: usize) {
|
||||||
self.tui.output(&format!(
|
self.current_tool_output.lock().unwrap().push(format!(
|
||||||
"│ ... ({} more line{} hidden)",
|
"... ({} more line{} hidden)",
|
||||||
hidden_count,
|
hidden_count,
|
||||||
if hidden_count == 1 { "" } else { "s" }
|
if hidden_count == 1 { "" } else { "s" }
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_timing(&self, duration_str: &str) {
|
fn print_tool_timing(&self, duration_str: &str) {
|
||||||
self.tui.output(&format!("└─ ⚡️ {}", duration_str));
|
self.current_tool_output.lock().unwrap().push(format!("⚡️ {}", duration_str));
|
||||||
self.tui.output("");
|
|
||||||
|
// Now send the complete tool output as a box
|
||||||
|
if let Some(tool_name) = self.current_tool_name.lock().unwrap().as_ref() {
|
||||||
|
let content = self.current_tool_output.lock().unwrap().join("\n");
|
||||||
|
self.tui.tool_output(tool_name, &content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the buffers
|
||||||
|
*self.current_tool_name.lock().unwrap() = None;
|
||||||
|
self.current_tool_output.lock().unwrap().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_agent_prompt(&self) {
|
fn print_agent_prompt(&self) {
|
||||||
|
|||||||
Reference in New Issue
Block a user