monoloop_loop/transaction/
host_tools.rs1use super::tool_handler::ToolHandler;
4use monoloop_contracts::{ToolId, ToolName, ToolSpec};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8#[derive(Clone)]
10pub struct RegisteredTool {
11 pub spec: ToolSpec,
13 pub handler: Arc<dyn ToolHandler>,
15}
16
17impl std::fmt::Debug for RegisteredTool {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 f.debug_struct("RegisteredTool")
20 .field("spec", &self.spec)
21 .field("handler", &"<dyn ToolHandler>")
22 .finish()
23 }
24}
25
26impl RegisteredTool {
27 pub fn new(spec: ToolSpec, handler: Arc<dyn ToolHandler>) -> Self {
31 Self::try_new(spec, handler).expect("handler supports declared ToolCancellationPolicy")
32 }
33
34 pub fn try_new(
36 spec: ToolSpec,
37 handler: Arc<dyn ToolHandler>,
38 ) -> Result<Self, super::StartupError> {
39 use monoloop_contracts::ToolCancellationPolicy;
40 match &spec.cancellation {
41 ToolCancellationPolicy::Abortable => {
42 if !handler.supports_abort() {
43 return Err(super::StartupError::ToolRegistry(
44 "Abortable tool requires supports_abort handler",
45 ));
46 }
47 }
48 ToolCancellationPolicy::IsolatedKillable { .. } => {
49 if !handler.supports_isolated_kill() {
50 return Err(super::StartupError::ToolRegistry(
51 "IsolatedKillable tool requires supports_isolated_kill handler",
52 ));
53 }
54 }
55 ToolCancellationPolicy::Cooperative { .. } => {
56 }
59 }
60 Ok(Self { spec, handler })
61 }
62}
63
64#[derive(Clone, Debug, Default)]
66pub struct HostToolRegistry {
67 by_id: HashMap<ToolId, RegisteredTool>,
68 by_name: HashMap<ToolName, ToolId>,
69}
70
71impl HostToolRegistry {
72 pub fn empty() -> Self {
74 Self::default()
75 }
76
77 pub fn build(tools: Vec<RegisteredTool>) -> Result<Self, super::StartupError> {
83 let mut by_id = HashMap::with_capacity(tools.len());
84 let mut by_name = HashMap::with_capacity(tools.len());
85 for tool in tools {
86 let schema_bytes = serde_json::to_vec(tool.spec.input_schema.as_value())
88 .map(|b| b.len())
89 .unwrap_or(0);
90 if schema_bytes > 64 * 1024 {
91 return Err(super::StartupError::ToolRegistry("tool schema too large"));
92 }
93 if by_id.contains_key(&tool.spec.id) {
94 return Err(super::StartupError::ToolRegistry("duplicate ToolId"));
95 }
96 if by_name.contains_key(&tool.spec.name) {
97 return Err(super::StartupError::ToolRegistry("duplicate ToolName"));
98 }
99 by_name.insert(tool.spec.name.clone(), tool.spec.id.clone());
100 by_id.insert(tool.spec.id.clone(), tool);
101 }
102 Ok(Self { by_id, by_name })
103 }
104
105 pub fn len(&self) -> usize {
107 self.by_id.len()
108 }
109
110 pub fn is_empty(&self) -> bool {
112 self.by_id.is_empty()
113 }
114
115 pub fn get(&self, id: &ToolId) -> Option<&RegisteredTool> {
117 self.by_id.get(id)
118 }
119
120 pub fn get_spec(&self, id: &ToolId) -> Option<&ToolSpec> {
122 self.by_id.get(id).map(|t| &t.spec)
123 }
124
125 pub fn id_for_name(&self, name: &ToolName) -> Option<&ToolId> {
127 self.by_name.get(name)
128 }
129
130 pub fn specs_sorted(&self) -> Vec<&ToolSpec> {
132 let mut ids: Vec<_> = self.by_id.keys().collect();
133 ids.sort_by(|a, b| a.as_str().cmp(b.as_str()));
134 ids.into_iter()
135 .filter_map(|id| self.by_id.get(id).map(|t| &t.spec))
136 .collect()
137 }
138}