Files
zesdex/crates/zesdex-backend/src/app/subagent/spawn.rs
T

104 lines
3.3 KiB
Rust
Raw Normal View History

//! `AgentDefinition` -- declarative specification for instantiating a
//! subagent from workflow scripts or programmatic calls.
//!
//! Also provides a shared [`spawn_subagent_with_drain`] helper that
//! eliminates the channel-creation + drain-thread boilerplate duplicated
//! across `auto/mod.rs`, `review/mod.rs`, and `workflow/engine/mod.rs`.
use super::event::SubagentEvent;
use serde::{Deserialize, Serialize};
/// Declarative specification for instantiating a subagent: name, role,
/// optional system prompt, allowed tools, step budget, and temperature.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentDefinition {
pub name: String,
pub role: String,
pub system_prompt: Option<String>,
pub allowed_tools: Option<Vec<String>>,
pub max_steps: Option<usize>,
pub temperature: Option<f32>,
}
impl AgentDefinition {
/// Create an agent definition with the required name and role; all
/// optional fields start as `None`.
pub fn new(name: String, role: String) -> Self {
AgentDefinition {
name,
role,
system_prompt: None,
allowed_tools: None,
max_steps: None,
temperature: None,
}
}
/// Builder method: set the system prompt for this agent.
pub fn with_system_prompt(mut self, prompt: String) -> Self {
self.system_prompt = Some(prompt);
self
}
/// Builder method: set the allowed tool list for this agent.
pub fn with_allowed_tools(mut self, tools: Vec<String>) -> Self {
self.allowed_tools = Some(tools);
self
}
/// Builder method: set the maximum step count for this agent.
pub fn with_max_steps(mut self, steps: usize) -> Self {
self.max_steps = Some(steps);
self
}
}
/// Shared subagent spawning utility: creates an mpsc channel and spawns a
/// drain thread that forwards every [`SubagentEvent`] to `on_event`.
///
/// Returns the sender half (for passing to [`run_subagent`](super::engine::run_subagent))
/// and the drain thread's join handle so the caller can keep it alive for
/// the duration of the subagent run.
///
/// # Example
///
/// ```ignore
/// let (tx, _drain) = spawn_subagent_with_drain(|event| {
/// match &event {
/// SubagentEvent::ToolCall { tool, .. } => tracing::debug!("tool: {tool}"),
/// SubagentEvent::Completed => tracing::debug!("done"),
/// _ => {}
/// }
/// });
/// let verdict = run_subagent(&ctx, &tx)?;
/// ```
///
/// # Duplication eliminated
///
/// Previously every subagent caller inlined the same 5-line pattern:
///
/// ```ignore
/// let (tx, mut rx) = tokio::sync::mpsc::channel(32);
/// let _drain = std::thread::spawn(move || {
/// while let Some(event) = rx.blocking_recv() { ... }
/// });
/// ```
///
/// Callers that need a larger buffer (e.g. workflow engine uses 64) should
/// create the channel manually instead of using this helper.
pub fn spawn_subagent_with_drain<F>(
on_event: F,
) -> (tokio::sync::mpsc::Sender<SubagentEvent>, std::thread::JoinHandle<()>)
where
F: Fn(SubagentEvent) + Send + 'static,
{
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
let drain = std::thread::spawn(move || {
while let Some(event) = rx.blocking_recv() {
on_event(event);
}
});
(tx, drain)
}