j_agent/tools/
load_tool.rs1use crate::tools::{PlanDecision, Tool, ToolResult, schema_to_tool_params};
2use schemars::JsonSchema;
3use serde::Deserialize;
4use serde_json::Value;
5use std::borrow::Cow;
6use std::sync::{Arc, Mutex, atomic::AtomicBool};
7
8#[derive(Deserialize, JsonSchema)]
10#[allow(dead_code)]
11struct LoadToolParams {
12 name: String,
14}
15
16#[derive(Debug)]
22pub struct LoadTool {
23 deferred_tools: Arc<Mutex<Vec<String>>>,
25 session_loaded_deferred: Arc<Mutex<Vec<String>>>,
27}
28
29impl LoadTool {
30 pub const NAME: &'static str = "LoadTool";
31
32 pub const STATIC_DESCRIPTION: &'static str = "Load a deferred tool so it becomes available in subsequent turns. \
40 Use this when you need a tool that is not currently available in your tool list. \
41 The tool name must match exactly. After loading, the tool will be available in the next turn.";
42
43 pub fn new(
44 deferred_tools: Arc<Mutex<Vec<String>>>,
45 session_loaded_deferred: Arc<Mutex<Vec<String>>>,
46 ) -> Self {
47 Self {
48 deferred_tools,
49 session_loaded_deferred,
50 }
51 }
52}
53
54impl Tool for LoadTool {
55 fn name(&self) -> &str {
56 Self::NAME
57 }
58
59 fn description(&self) -> Cow<'_, str> {
60 Cow::Borrowed(Self::STATIC_DESCRIPTION)
61 }
62
63 fn parameters_schema(&self) -> Value {
64 schema_to_tool_params::<LoadToolParams>()
65 }
66
67 fn execute(&self, arguments: &str, _cancelled: &Arc<AtomicBool>) -> ToolResult {
68 let params: LoadToolParams = match serde_json::from_str(arguments) {
70 Ok(p) => p,
71 Err(e) => {
72 return ToolResult {
73 output: format!("Failed to parse arguments: {e}"),
74 is_error: true,
75 images: vec![],
76 plan_decision: PlanDecision::None,
77 };
78 }
79 };
80
81 let tool_name = params.name.trim().to_string();
82 if tool_name.is_empty() {
83 return ToolResult {
84 output: "Tool name cannot be empty.".to_string(),
85 is_error: true,
86 images: vec![],
87 plan_decision: PlanDecision::None,
88 };
89 }
90
91 let mut deferred = match self.deferred_tools.lock() {
95 Ok(guard) => guard,
96 Err(e) => {
97 e.into_inner()
99 }
100 };
101 let idx = deferred.iter().position(|n| n == &tool_name);
102 match idx {
103 Some(i) => {
104 deferred.remove(i);
105 if let Ok(mut loaded) = self.session_loaded_deferred.lock()
107 && !loaded.iter().any(|n| n == &tool_name)
108 {
109 loaded.push(tool_name.clone());
110 }
111 ToolResult {
112 output: format!(
113 "Tool '{}' has been loaded successfully. It will be available in the next turn.",
114 tool_name
115 ),
116 is_error: false,
117 images: vec![],
118 plan_decision: PlanDecision::None,
119 }
120 }
121 None => {
122 ToolResult {
124 output: format!(
125 "Tool '{}' is not in the deferred list. It may already be loaded or does not exist.",
126 tool_name
127 ),
128 is_error: true,
129 images: vec![],
130 plan_decision: PlanDecision::None,
131 }
132 }
133 }
134 }
135
136 fn requires_confirmation(&self) -> bool {
137 false
138 }
139}