ActiveTools wrapper: replace SharedActiveTools Arc<Mutex<Vec>>
New ActiveTools struct with proper methods: push, remove, abort_all, take_finished, take_foreground, iter, len. Lives directly on AgentState, no separate Arc<Mutex> needed. TUI reads active tools through agent.state.try_lock(). Turn loop uses helpers instead of manual index iteration. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
9c9618d034
commit
31a41fa042
5 changed files with 23 additions and 26 deletions
|
|
@ -21,7 +21,6 @@ mod vision;
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
fn default_timeout() -> u64 { 120 }
|
fn default_timeout() -> u64 { 120 }
|
||||||
|
|
@ -108,6 +107,12 @@ impl ActiveTools {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn abort_all(&mut self) {
|
||||||
|
for entry in self.0.drain(..) {
|
||||||
|
entry.handle.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize { self.0.len() }
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,8 +264,6 @@ impl Mind {
|
||||||
config: SessionConfig,
|
config: SessionConfig,
|
||||||
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
|
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let shared_active_tools = crate::agent::tools::shared_active_tools();
|
|
||||||
|
|
||||||
let client = ApiClient::new(&config.api_base, &config.api_key, &config.model);
|
let client = ApiClient::new(&config.api_base, &config.api_key, &config.model);
|
||||||
let conversation_log = log::ConversationLog::new(
|
let conversation_log = log::ConversationLog::new(
|
||||||
config.session_dir.join("conversation.jsonl"),
|
config.session_dir.join("conversation.jsonl"),
|
||||||
|
|
@ -278,7 +276,7 @@ impl Mind {
|
||||||
config.app.clone(),
|
config.app.clone(),
|
||||||
config.prompt_file.clone(),
|
config.prompt_file.clone(),
|
||||||
conversation_log,
|
conversation_log,
|
||||||
shared_active_tools,
|
crate::agent::tools::ActiveTools::new(),
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns)));
|
let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns)));
|
||||||
|
|
@ -352,10 +350,7 @@ impl Mind {
|
||||||
}
|
}
|
||||||
MindCommand::Interrupt => {
|
MindCommand::Interrupt => {
|
||||||
self.shared.lock().unwrap().interrupt();
|
self.shared.lock().unwrap().interrupt();
|
||||||
let active_tools = self.agent.state.lock().await.active_tools.clone();
|
self.agent.state.lock().await.active_tools.abort_all();
|
||||||
let mut tools = active_tools.lock().unwrap();
|
|
||||||
for entry in tools.drain(..) { entry.handle.abort(); }
|
|
||||||
drop(tools);
|
|
||||||
if let Some(h) = self.shared.lock().unwrap().turn_handle.take() { h.abort(); }
|
if let Some(h) = self.shared.lock().unwrap().turn_handle.take() { h.abort(); }
|
||||||
self.shared.lock().unwrap().turn_active = false;
|
self.shared.lock().unwrap().turn_active = false;
|
||||||
let _ = self.turn_watch.send(false);
|
let _ = self.turn_watch.send(false);
|
||||||
|
|
|
||||||
|
|
@ -585,8 +585,9 @@ impl InteractScreen {
|
||||||
/// Draw the main (F1) screen — four-pane layout with status bar.
|
/// Draw the main (F1) screen — four-pane layout with status bar.
|
||||||
fn draw_main(&mut self, frame: &mut Frame, size: Rect, app: &App) {
|
fn draw_main(&mut self, frame: &mut Frame, size: Rect, app: &App) {
|
||||||
// Main layout: content area + active tools overlay + status bar
|
// Main layout: content area + active tools overlay + status bar
|
||||||
let active_tools = app.active_tools.lock().unwrap();
|
let st_guard = app.agent.state.try_lock().ok();
|
||||||
let tool_lines = active_tools.len() as u16;
|
let tool_lines = st_guard.as_ref()
|
||||||
|
.map(|st| st.active_tools.len() as u16).unwrap_or(0);
|
||||||
let main_chunks = Layout::default()
|
let main_chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
|
|
@ -675,10 +676,10 @@ impl InteractScreen {
|
||||||
frame.render_widget(gutter, input_chunks[0]);
|
frame.render_widget(gutter, input_chunks[0]);
|
||||||
frame.render_widget(&self.textarea, input_chunks[1]);
|
frame.render_widget(&self.textarea, input_chunks[1]);
|
||||||
|
|
||||||
// Draw active tools overlay
|
if let Some(ref st) = st_guard {
|
||||||
if !active_tools.is_empty() {
|
if !st.active_tools.is_empty() {
|
||||||
let tool_style = Style::default().fg(Color::Yellow).add_modifier(Modifier::DIM);
|
let tool_style = Style::default().fg(Color::Yellow).add_modifier(Modifier::DIM);
|
||||||
let tool_text: Vec<Line> = active_tools.iter().map(|t| {
|
let tool_text: Vec<Line> = st.active_tools.iter().map(|t| {
|
||||||
let elapsed = t.started.elapsed().as_secs();
|
let elapsed = t.started.elapsed().as_secs();
|
||||||
let line = if t.detail.is_empty() {
|
let line = if t.detail.is_empty() {
|
||||||
format!(" [{}] ({}s)", t.name, elapsed)
|
format!(" [{}] ({}s)", t.name, elapsed)
|
||||||
|
|
@ -689,7 +690,7 @@ impl InteractScreen {
|
||||||
}).collect();
|
}).collect();
|
||||||
let tool_para = Paragraph::new(tool_text);
|
let tool_para = Paragraph::new(tool_text);
|
||||||
frame.render_widget(tool_para, tools_overlay_area);
|
frame.render_widget(tool_para, tools_overlay_area);
|
||||||
}
|
}}
|
||||||
|
|
||||||
// Draw status bar with live activity indicator
|
// Draw status bar with live activity indicator
|
||||||
let timer = if !app.activity.is_empty() {
|
let timer = if !app.activity.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,9 @@ impl ScreenView for ConsciousScreen {
|
||||||
)));
|
)));
|
||||||
lines.push(Line::raw(format!(" Reasoning: {}", app.reasoning_effort)));
|
lines.push(Line::raw(format!(" Reasoning: {}", app.reasoning_effort)));
|
||||||
lines.push(Line::raw(format!(" Running processes: {}", app.running_processes)));
|
lines.push(Line::raw(format!(" Running processes: {}", app.running_processes)));
|
||||||
lines.push(Line::raw(format!(" Active tools: {}", app.active_tools.lock().unwrap().len())));
|
let tool_count = app.agent.state.try_lock()
|
||||||
|
.map(|st| st.active_tools.len()).unwrap_or(0);
|
||||||
|
lines.push(Line::raw(format!(" Active tools: {}", tool_count)));
|
||||||
|
|
||||||
let block = pane_block("context")
|
let block = pane_block("context")
|
||||||
.title_top(Line::from(screen_legend()).left_aligned())
|
.title_top(Line::from(screen_legend()).left_aligned())
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ impl App {
|
||||||
temperature: 0.6,
|
temperature: 0.6,
|
||||||
top_p: 0.95,
|
top_p: 0.95,
|
||||||
top_k: 20,
|
top_k: 20,
|
||||||
agent: mind.agent.clone(),
|
agent,
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
context_info: None,
|
context_info: None,
|
||||||
agent_state: Vec::new(),
|
agent_state: Vec::new(),
|
||||||
|
|
@ -181,8 +181,6 @@ async fn start(cli: crate::user::CliArgs) -> Result<()> {
|
||||||
|
|
||||||
let mind = crate::mind::Mind::new(config, turn_tx).await;
|
let mind = crate::mind::Mind::new(config, turn_tx).await;
|
||||||
|
|
||||||
let shared_active_tools = mind.agent.state.lock().await.active_tools.clone();
|
|
||||||
|
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
tokio_scoped::scope(|s| {
|
tokio_scoped::scope(|s| {
|
||||||
// Mind event loop — init + run
|
// Mind event loop — init + run
|
||||||
|
|
@ -194,7 +192,7 @@ async fn start(cli: crate::user::CliArgs) -> Result<()> {
|
||||||
// UI event loop
|
// UI event loop
|
||||||
s.spawn(async {
|
s.spawn(async {
|
||||||
result = run(
|
result = run(
|
||||||
tui::App::new(String::new(), shared_active_tools),
|
tui::App::new(String::new(), mind.agent.clone()),
|
||||||
&mind, mind_tx,
|
&mind, mind_tx,
|
||||||
).await;
|
).await;
|
||||||
});
|
});
|
||||||
|
|
@ -222,15 +220,11 @@ fn hotkey_cycle_reasoning(mind: &crate::mind::Mind) {
|
||||||
|
|
||||||
async fn hotkey_kill_processes(mind: &crate::mind::Mind) {
|
async fn hotkey_kill_processes(mind: &crate::mind::Mind) {
|
||||||
let mut st = mind.agent.state.lock().await;
|
let mut st = mind.agent.state.lock().await;
|
||||||
let active_tools = st.active_tools.clone();
|
if st.active_tools.is_empty() {
|
||||||
let mut tools = active_tools.lock().unwrap();
|
|
||||||
if tools.is_empty() {
|
|
||||||
st.notify("no running tools");
|
st.notify("no running tools");
|
||||||
} else {
|
} else {
|
||||||
let count = tools.len();
|
let count = st.active_tools.len();
|
||||||
for entry in tools.drain(..) {
|
st.active_tools.abort_all();
|
||||||
entry.handle.abort();
|
|
||||||
}
|
|
||||||
st.notify(format!("killed {} tools", count));
|
st.notify(format!("killed {} tools", count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue