everruns_core/
tool_execution.rs1use crate::error::Result;
4use crate::tool_context::ToolContext;
5use crate::tool_types::{ToolCall, ToolDefinition, ToolResult};
6use crate::typed_id::SessionId;
7use async_trait::async_trait;
8use std::collections::HashMap;
9
10fn build_tool_map(tool_defs: &[ToolDefinition]) -> HashMap<&str, &ToolDefinition> {
11 tool_defs.iter().map(|def| (def.name(), def)).collect()
12}
13
14#[async_trait]
21pub trait ToolExecutor: Send + Sync {
22 async fn execute(&self, tool_call: &ToolCall, tool_def: &ToolDefinition) -> Result<ToolResult>;
27
28 async fn execute_with_context(
33 &self,
34 tool_call: &ToolCall,
35 tool_def: &ToolDefinition,
36 _context: &ToolContext,
37 ) -> Result<ToolResult> {
38 self.execute(tool_call, tool_def).await
40 }
41
42 async fn execute_batch(
44 &self,
45 tool_calls: &[ToolCall],
46 tool_defs: &[ToolDefinition],
47 ) -> Result<Vec<ToolResult>> {
48 let mut results = Vec::with_capacity(tool_calls.len());
49
50 let tool_map = build_tool_map(tool_defs);
51
52 for tool_call in tool_calls {
53 let tool_def = tool_map.get(tool_call.name.as_str()).ok_or_else(|| {
54 crate::error::AgentLoopError::tool(format!(
55 "Tool definition not found: {}",
56 tool_call.name
57 ))
58 })?;
59
60 results.push(self.execute(tool_call, tool_def).await?);
61 }
62
63 Ok(results)
64 }
65
66 async fn execute_parallel(
68 &self,
69 tool_calls: &[ToolCall],
70 tool_defs: &[ToolDefinition],
71 ) -> Result<Vec<ToolResult>>
72 where
73 Self: Sized,
74 {
75 use futures::future::join_all;
76
77 let tool_map = build_tool_map(tool_defs);
78
79 let futures: Vec<_> = tool_calls
80 .iter()
81 .map(|tool_call| async {
82 let tool_def = tool_map.get(tool_call.name.as_str()).ok_or_else(|| {
83 crate::error::AgentLoopError::tool(format!(
84 "Tool definition not found: {}",
85 tool_call.name
86 ))
87 })?;
88 self.execute(tool_call, tool_def).await
89 })
90 .collect();
91
92 let results = join_all(futures).await;
93 results.into_iter().collect()
94 }
95}
96
97#[async_trait]
101impl ToolExecutor for std::sync::Arc<dyn ToolExecutor> {
102 async fn execute(&self, tool_call: &ToolCall, tool_def: &ToolDefinition) -> Result<ToolResult> {
103 (**self).execute(tool_call, tool_def).await
104 }
105
106 async fn execute_with_context(
107 &self,
108 tool_call: &ToolCall,
109 tool_def: &ToolDefinition,
110 context: &ToolContext,
111 ) -> Result<ToolResult> {
112 (**self)
113 .execute_with_context(tool_call, tool_def, context)
114 .await
115 }
116
117 async fn execute_batch(
118 &self,
119 tool_calls: &[ToolCall],
120 tool_defs: &[ToolDefinition],
121 ) -> Result<Vec<ToolResult>> {
122 (**self).execute_batch(tool_calls, tool_defs).await
123 }
124}
125
126#[async_trait]
132pub trait BudgetChecker: Send + Sync {
133 async fn check_budgets(&self, session_id: &str) -> Result<crate::budget::BudgetToolResponse>;
135}
136
137#[async_trait]
146pub trait PaymentAuthority: Send + Sync {
147 async fn execute_machine_payment(
148 &self,
149 session_id: SessionId,
150 request: crate::payment::MachinePaymentRequest,
151 ) -> Result<crate::payment::MachinePaymentResponse>;
152}
153
154#[async_trait]
161pub trait OutboundToolRateLimiter: Send + Sync {
162 async fn check_org(&self, org_id: &crate::typed_id::OrgId) -> bool;
164}