Sebelumnya loop utama mengeksekusi semua tool call satu-per-satu (sequential for loop). Seperti Claude Code, tool read-only yang independen dalam satu pesan assistant (read/grep/glob/semantic_search dsb.) kini dijalankan konkuren dengan bounded parallelism (max 8), mengurangi latensi per turn secara signifikan untuk beban coding. - feat(registry): tool_is_parallel_safe() — whitelist tool read-only yang aman dijalankan paralel; tool mutating/shell tetap sequential - feat(executor): is_parallel_safe() delegasi ke registry; ToolExecutor trait Default=false (konservatif) - fix(application): execute_tool_calls_in_parallel() — join_all + semaphore bounded 8, hasil dikumpulkan dalam URUTAN panggilan asli (kontrak OpenAI/Anthropic tool-result ordering) - loop utama: batch paralel hanya jika SEMUA tool parallel-safe; jika ada satu tool mutating, jatuh balik ke jalur sequential aman - test: +2 registry test, +2 application test (konkurensi & urutan, fallback batch mutating)
52 lines
1.4 KiB
Rust
52 lines
1.4 KiB
Rust
use crate::tools::{all_tools, Tool, ToolCtx};
|
|
use anyhow::Result;
|
|
use std::future::Future;
|
|
use zesdex_application::agent::ToolExecutor;
|
|
|
|
pub struct InfrastructureToolExecutor {
|
|
ctx: ToolCtx,
|
|
tools: Vec<Box<dyn Tool>>,
|
|
}
|
|
|
|
impl InfrastructureToolExecutor {
|
|
pub fn new(ctx: ToolCtx) -> Self {
|
|
Self {
|
|
ctx,
|
|
tools: all_tools(),
|
|
}
|
|
}
|
|
|
|
/// Whether a tool is read-only and safe to execute concurrently with
|
|
/// other parallel-safe tools. Delegates to the registry so the main
|
|
/// turn loop and subagent engine share one source of truth.
|
|
pub fn is_parallel_safe(tool_name: &str) -> bool {
|
|
crate::tools::tool_is_parallel_safe(tool_name)
|
|
}
|
|
}
|
|
|
|
impl ToolExecutor for InfrastructureToolExecutor {
|
|
fn execute(
|
|
&self,
|
|
tool_name: &str,
|
|
args: &serde_json::Value,
|
|
) -> impl Future<Output = Result<String>> + Send {
|
|
// Find the tool by name
|
|
let tool_opt = self.tools.iter().find(|t| t.name() == tool_name);
|
|
let ctx = self.ctx.clone();
|
|
let args = args.clone();
|
|
|
|
async move {
|
|
match tool_opt {
|
|
Some(tool) => tokio::task::block_in_place(move || tool.run(&ctx, &args)),
|
|
None => {
|
|
anyhow::bail!("Unknown tool: {}", tool_name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_parallel_safe(&self, tool_name: &str) -> bool {
|
|
Self::is_parallel_safe(tool_name)
|
|
}
|
|
}
|