Converted all external tool calling to async
This commit is contained in:
@@ -10,12 +10,12 @@ use super::types::{agent_info_from_entry, composite_key};
|
||||
impl AgentPool {
|
||||
/// Return the names of configured agents for `stage` that are not currently
|
||||
/// running or pending.
|
||||
pub fn available_agents_for_stage(
|
||||
pub async fn available_agents_for_stage(
|
||||
&self,
|
||||
config: &ProjectConfig,
|
||||
stage: &PipelineStage,
|
||||
) -> Result<Vec<String>, String> {
|
||||
let agents = self.agents.lock().map_err(|e| e.to_string())?;
|
||||
let agents = self.agents.lock().await;
|
||||
Ok(config
|
||||
.agent
|
||||
.iter()
|
||||
@@ -44,8 +44,8 @@ impl AgentPool {
|
||||
}
|
||||
|
||||
/// List all agents with their status.
|
||||
pub fn list_agents(&self) -> Result<Vec<AgentInfo>, String> {
|
||||
let agents = self.agents.lock().map_err(|e| e.to_string())?;
|
||||
pub async fn list_agents(&self) -> Result<Vec<AgentInfo>, String> {
|
||||
let agents = self.agents.lock().await;
|
||||
Ok(agents
|
||||
.iter()
|
||||
.map(|(key, agent)| {
|
||||
@@ -59,14 +59,35 @@ impl AgentPool {
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Best-effort agent list for sync callers (chat commands, rendering).
|
||||
///
|
||||
/// Uses `try_lock()` so it never blocks the thread. Returns an empty list
|
||||
/// if the lock is currently held — callers that refresh periodically (htop,
|
||||
/// pipeline board) tolerate this gracefully.
|
||||
pub fn list_agents_nonblocking(&self) -> Vec<AgentInfo> {
|
||||
let Ok(agents) = self.agents.try_lock() else {
|
||||
return Vec::new();
|
||||
};
|
||||
agents
|
||||
.iter()
|
||||
.map(|(key, agent)| {
|
||||
let story_id = key
|
||||
.rsplit_once(':')
|
||||
.map(|(sid, _)| sid.to_string())
|
||||
.unwrap_or_else(|| key.clone());
|
||||
agent_info_from_entry(&story_id, agent)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Subscribe to events for a story agent.
|
||||
pub fn subscribe(
|
||||
pub async fn subscribe(
|
||||
&self,
|
||||
story_id: &str,
|
||||
agent_name: &str,
|
||||
) -> Result<broadcast::Receiver<AgentEvent>, String> {
|
||||
let key = composite_key(story_id, agent_name);
|
||||
let agents = self.agents.lock().map_err(|e| e.to_string())?;
|
||||
let agents = self.agents.lock().await;
|
||||
let agent = agents
|
||||
.get(&key)
|
||||
.ok_or_else(|| format!("No agent '{agent_name}' for story '{story_id}'"))?;
|
||||
@@ -74,13 +95,13 @@ impl AgentPool {
|
||||
}
|
||||
|
||||
/// Drain accumulated events for polling. Returns all events since the last drain.
|
||||
pub fn drain_events(
|
||||
pub async fn drain_events(
|
||||
&self,
|
||||
story_id: &str,
|
||||
agent_name: &str,
|
||||
) -> Result<Vec<AgentEvent>, String> {
|
||||
let key = composite_key(story_id, agent_name);
|
||||
let agents = self.agents.lock().map_err(|e| e.to_string())?;
|
||||
let agents = self.agents.lock().await;
|
||||
let agent = agents
|
||||
.get(&key)
|
||||
.ok_or_else(|| format!("No agent '{agent_name}' for story '{story_id}'"))?;
|
||||
@@ -91,9 +112,13 @@ impl AgentPool {
|
||||
/// Get the log session ID and project root for an agent, if available.
|
||||
///
|
||||
/// Used by MCP tools to find the persistent log file for a completed agent.
|
||||
pub fn get_log_info(&self, story_id: &str, agent_name: &str) -> Option<(String, PathBuf)> {
|
||||
pub async fn get_log_info(
|
||||
&self,
|
||||
story_id: &str,
|
||||
agent_name: &str,
|
||||
) -> Option<(String, PathBuf)> {
|
||||
let key = composite_key(story_id, agent_name);
|
||||
let agents = self.agents.lock().ok()?;
|
||||
let agents = self.agents.lock().await;
|
||||
let agent = agents.get(&key)?;
|
||||
let session_id = agent.log_session_id.clone()?;
|
||||
let project_root = agent.project_root.clone()?;
|
||||
@@ -111,8 +136,8 @@ mod tests {
|
||||
ProjectConfig::parse(toml_str).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn available_agents_for_stage_returns_idle_agents() {
|
||||
#[tokio::test]
|
||||
async fn available_agents_for_stage_returns_idle_agents() {
|
||||
let config = make_config(
|
||||
r#"
|
||||
[[agent]]
|
||||
@@ -133,17 +158,19 @@ stage = "qa"
|
||||
|
||||
let available = pool
|
||||
.available_agents_for_stage(&config, &PipelineStage::Coder)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(available, vec!["coder-2"]);
|
||||
|
||||
let available_qa = pool
|
||||
.available_agents_for_stage(&config, &PipelineStage::Qa)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(available_qa, vec!["qa"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn available_agents_for_stage_returns_empty_when_all_busy() {
|
||||
#[tokio::test]
|
||||
async fn available_agents_for_stage_returns_empty_when_all_busy() {
|
||||
let config = make_config(
|
||||
r#"
|
||||
[[agent]]
|
||||
@@ -156,12 +183,13 @@ stage = "coder"
|
||||
|
||||
let available = pool
|
||||
.available_agents_for_stage(&config, &PipelineStage::Coder)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(available.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn available_agents_for_stage_ignores_completed_agents() {
|
||||
#[tokio::test]
|
||||
async fn available_agents_for_stage_ignores_completed_agents() {
|
||||
let config = make_config(
|
||||
r#"
|
||||
[[agent]]
|
||||
@@ -174,6 +202,7 @@ stage = "coder"
|
||||
|
||||
let available = pool
|
||||
.available_agents_for_stage(&config, &PipelineStage::Coder)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(available, vec!["coder-1"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user