shared active tools: Agent writes, TUI reads directly
Move active tool tracking from TUI message-passing to shared Arc<RwLock> state. Agent pushes on dispatch, removes on apply_tool_result. TUI reads during render. Background tasks show as active until drained at next turn start. Co-Developed-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
d25033b9f4
commit
474b66c834
6 changed files with 62 additions and 49 deletions
|
|
@ -80,6 +80,8 @@ pub struct Agent {
|
|||
pub memory_scores: Option<crate::agent::training::MemoryScore>,
|
||||
/// Whether a /score task is currently running.
|
||||
pub scoring_in_flight: bool,
|
||||
/// Shared active tools — Agent writes, TUI reads.
|
||||
pub active_tools: crate::user::ui_channel::SharedActiveTools,
|
||||
/// Background tool calls that outlive the current turn.
|
||||
background_tasks: futures::stream::FuturesUnordered<
|
||||
std::pin::Pin<Box<dyn std::future::Future<Output = (ToolCall, tools::ToolOutput)> + Send>>
|
||||
|
|
@ -105,6 +107,7 @@ impl Agent {
|
|||
prompt_file: String,
|
||||
conversation_log: Option<ConversationLog>,
|
||||
shared_context: SharedContextState,
|
||||
active_tools: crate::user::ui_channel::SharedActiveTools,
|
||||
) -> Self {
|
||||
let tool_defs = tools::definitions();
|
||||
let tokenizer = tiktoken_rs::cl100k_base()
|
||||
|
|
@ -135,6 +138,7 @@ impl Agent {
|
|||
agent_cycles,
|
||||
memory_scores: None,
|
||||
scoring_in_flight: false,
|
||||
active_tools,
|
||||
background_tasks: futures::stream::FuturesUnordered::new(),
|
||||
};
|
||||
|
||||
|
|
@ -240,20 +244,15 @@ impl Agent {
|
|||
// Inject completed background task results
|
||||
{
|
||||
use futures::{StreamExt, FutureExt};
|
||||
let mut bg_ds = DispatchState {
|
||||
yield_requested: false, had_tool_calls: false,
|
||||
tool_errors: 0, model_switch: None, dmn_pause: false,
|
||||
};
|
||||
while let Some(Some((call, output))) =
|
||||
std::pin::Pin::new(&mut self.background_tasks).next().now_or_never()
|
||||
{
|
||||
let preview = &output.text[..output.text.len().min(500)];
|
||||
let _ = ui_tx.send(UiMessage::Info(format!(
|
||||
"[background] {} completed: {}",
|
||||
call.function.name,
|
||||
&preview[..preview.len().min(80)],
|
||||
)));
|
||||
let notification = format!(
|
||||
"<task-notification>\nTool: {}\nResult: {}\n</task-notification>",
|
||||
call.function.name, preview,
|
||||
);
|
||||
self.push_message(Message::user(notification));
|
||||
// Show result in TUI and inject into conversation
|
||||
self.apply_tool_result(&call, output, ui_tx, &mut bg_ds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -324,11 +323,14 @@ impl Agent {
|
|||
name: call.function.name.clone(),
|
||||
args_summary: args_summary.clone(),
|
||||
});
|
||||
let _ = ui_tx.send(UiMessage::ToolStarted {
|
||||
id: call.id.clone(),
|
||||
name: call.function.name.clone(),
|
||||
detail: args_summary,
|
||||
});
|
||||
self.active_tools.write().unwrap().push(
|
||||
crate::user::ui_channel::ActiveTool {
|
||||
id: call.id.clone(),
|
||||
name: call.function.name.clone(),
|
||||
detail: args_summary,
|
||||
started: std::time::Instant::now(),
|
||||
}
|
||||
);
|
||||
let tracker = self.process_tracker.clone();
|
||||
let is_background = args.get("run_in_background")
|
||||
.and_then(|v| v.as_bool())
|
||||
|
|
@ -548,11 +550,14 @@ impl Agent {
|
|||
name: call.function.name.clone(),
|
||||
args_summary: args_summary.clone(),
|
||||
});
|
||||
let _ = ui_tx.send(UiMessage::ToolStarted {
|
||||
id: call.id.clone(),
|
||||
name: call.function.name.clone(),
|
||||
detail: args_summary,
|
||||
});
|
||||
self.active_tools.write().unwrap().push(
|
||||
crate::user::ui_channel::ActiveTool {
|
||||
id: call.id.clone(),
|
||||
name: call.function.name.clone(),
|
||||
detail: args_summary,
|
||||
started: std::time::Instant::now(),
|
||||
}
|
||||
);
|
||||
|
||||
// Handle working_stack tool — needs &mut self for context state
|
||||
if call.function.name == "working_stack" {
|
||||
|
|
@ -568,7 +573,7 @@ impl Agent {
|
|||
name: call.function.name.clone(),
|
||||
result: output.text.clone(),
|
||||
});
|
||||
let _ = ui_tx.send(UiMessage::ToolFinished { id: call.id.clone() });
|
||||
self.active_tools.write().unwrap().retain(|t| t.id != call.id);
|
||||
self.push_message(Message::tool_result(&call.id, &output.text));
|
||||
ds.had_tool_calls = true;
|
||||
|
||||
|
|
@ -616,7 +621,7 @@ impl Agent {
|
|||
name: call.function.name.clone(),
|
||||
result: output.text.clone(),
|
||||
});
|
||||
let _ = ui_tx.send(UiMessage::ToolFinished { id: call.id.clone() });
|
||||
self.active_tools.write().unwrap().retain(|t| t.id != call.id);
|
||||
|
||||
// Tag memory_render results for context deduplication
|
||||
if call.function.name == "memory_render" && !output.text.starts_with("Error:") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue