1use adk_core::{Result, Tool, ToolContext};
2use async_trait::async_trait;
3use serde_json::{Map, Value, json};
4use std::sync::Arc;
5
6#[derive(Debug, Clone, Default)]
8pub struct OpenAIApproximateLocation {
9 city: Option<String>,
10 country: Option<String>,
11 region: Option<String>,
12 timezone: Option<String>,
13}
14
15impl OpenAIApproximateLocation {
16 pub fn new() -> Self {
17 Self::default()
18 }
19
20 pub fn with_city(mut self, city: impl Into<String>) -> Self {
21 self.city = Some(city.into());
22 self
23 }
24
25 pub fn with_country(mut self, country: impl Into<String>) -> Self {
26 self.country = Some(country.into());
27 self
28 }
29
30 pub fn with_region(mut self, region: impl Into<String>) -> Self {
31 self.region = Some(region.into());
32 self
33 }
34
35 pub fn with_timezone(mut self, timezone: impl Into<String>) -> Self {
36 self.timezone = Some(timezone.into());
37 self
38 }
39
40 fn to_json(&self) -> Value {
41 json!({
42 "type": "approximate",
43 "city": self.city,
44 "country": self.country,
45 "region": self.region,
46 "timezone": self.timezone,
47 })
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
53pub enum OpenAIWebSearchVariant {
54 #[default]
55 Stable20250826,
56 Preview20250311,
57}
58
59impl OpenAIWebSearchVariant {
60 fn as_wire(self) -> &'static str {
61 match self {
62 Self::Stable20250826 => "web_search_2025_08_26",
63 Self::Preview20250311 => "web_search_preview_2025_03_11",
64 }
65 }
66}
67
68#[derive(Debug, Clone, Default)]
70pub struct OpenAIWebSearchTool {
71 variant: OpenAIWebSearchVariant,
72 allowed_domains: Option<Vec<String>>,
73 user_location: Option<OpenAIApproximateLocation>,
74 search_context_size: Option<String>,
75}
76
77impl OpenAIWebSearchTool {
78 pub fn new() -> Self {
79 Self::default()
80 }
81
82 pub fn preview(mut self) -> Self {
83 self.variant = OpenAIWebSearchVariant::Preview20250311;
84 self
85 }
86
87 pub fn with_allowed_domains(
88 mut self,
89 domains: impl IntoIterator<Item = impl Into<String>>,
90 ) -> Self {
91 self.allowed_domains = Some(domains.into_iter().map(Into::into).collect());
92 self
93 }
94
95 pub fn with_user_location(mut self, user_location: OpenAIApproximateLocation) -> Self {
96 self.user_location = Some(user_location);
97 self
98 }
99
100 pub fn with_search_context_size(mut self, size: impl Into<String>) -> Self {
101 self.search_context_size = Some(size.into());
102 self
103 }
104
105 fn tool_json(&self) -> Value {
106 json!({
107 "type": self.variant.as_wire(),
108 "filters": self.allowed_domains.as_ref().map(|domains| json!({ "allowed_domains": domains })),
109 "user_location": self.user_location.as_ref().map(OpenAIApproximateLocation::to_json),
110 "search_context_size": self.search_context_size,
111 })
112 }
113}
114
115#[async_trait]
116impl Tool for OpenAIWebSearchTool {
117 fn name(&self) -> &str {
118 "openai_web_search"
119 }
120
121 fn description(&self) -> &str {
122 "Uses OpenAI hosted web search to retrieve current web information."
123 }
124
125 fn is_builtin(&self) -> bool {
126 true
127 }
128
129 fn declaration(&self) -> Value {
130 json!({
131 "name": self.name(),
132 "description": self.description(),
133 "x-adk-openai-tool": self.tool_json(),
134 })
135 }
136
137 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
138 Err(adk_core::AdkError::tool("OpenAI web search is handled by the Responses API"))
139 }
140}
141
142#[derive(Debug, Clone)]
144pub struct OpenAIFileSearchTool {
145 vector_store_ids: Vec<String>,
146 max_num_results: Option<u32>,
147 filters: Option<Value>,
148 ranking_options: Option<Value>,
149}
150
151impl OpenAIFileSearchTool {
152 pub fn new(vector_store_ids: impl IntoIterator<Item = impl Into<String>>) -> Self {
153 Self {
154 vector_store_ids: vector_store_ids.into_iter().map(Into::into).collect(),
155 max_num_results: None,
156 filters: None,
157 ranking_options: None,
158 }
159 }
160
161 pub fn with_max_num_results(mut self, max_num_results: u32) -> Self {
162 self.max_num_results = Some(max_num_results);
163 self
164 }
165
166 pub fn with_filters(mut self, filters: Value) -> Self {
167 self.filters = Some(filters);
168 self
169 }
170
171 pub fn with_ranking_options(mut self, ranking_options: Value) -> Self {
172 self.ranking_options = Some(ranking_options);
173 self
174 }
175
176 fn tool_json(&self) -> Value {
177 json!({
178 "type": "file_search",
179 "vector_store_ids": self.vector_store_ids,
180 "max_num_results": self.max_num_results,
181 "filters": self.filters,
182 "ranking_options": self.ranking_options,
183 })
184 }
185}
186
187#[async_trait]
188impl Tool for OpenAIFileSearchTool {
189 fn name(&self) -> &str {
190 "openai_file_search"
191 }
192
193 fn description(&self) -> &str {
194 "Uses OpenAI hosted file search against one or more vector stores."
195 }
196
197 fn is_builtin(&self) -> bool {
198 true
199 }
200
201 fn declaration(&self) -> Value {
202 json!({
203 "name": self.name(),
204 "description": self.description(),
205 "x-adk-openai-tool": self.tool_json(),
206 })
207 }
208
209 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
210 Err(adk_core::AdkError::tool("OpenAI file search is handled by the Responses API"))
211 }
212}
213
214#[derive(Debug, Clone, Default)]
216pub struct OpenAICodeInterpreterTool {
217 file_ids: Vec<String>,
218 memory_limit: Option<u64>,
219 container_id: Option<String>,
220}
221
222impl OpenAICodeInterpreterTool {
223 pub fn new() -> Self {
224 Self::default()
225 }
226
227 pub fn with_file_ids(mut self, file_ids: impl IntoIterator<Item = impl Into<String>>) -> Self {
228 self.file_ids = file_ids.into_iter().map(Into::into).collect();
229 self
230 }
231
232 pub fn with_memory_limit(mut self, memory_limit: u64) -> Self {
233 self.memory_limit = Some(memory_limit);
234 self
235 }
236
237 pub fn with_container_id(mut self, container_id: impl Into<String>) -> Self {
238 self.container_id = Some(container_id.into());
239 self
240 }
241
242 fn tool_json(&self) -> Value {
243 let container = if let Some(container_id) = &self.container_id {
244 Value::String(container_id.clone())
245 } else {
246 json!({
247 "type": "auto",
248 "file_ids": (!self.file_ids.is_empty()).then_some(self.file_ids.clone()),
249 "memory_limit": self.memory_limit,
250 })
251 };
252
253 json!({
254 "type": "code_interpreter",
255 "container": container,
256 })
257 }
258}
259
260#[async_trait]
261impl Tool for OpenAICodeInterpreterTool {
262 fn name(&self) -> &str {
263 "openai_code_interpreter"
264 }
265
266 fn description(&self) -> &str {
267 "Uses OpenAI hosted code interpreter to execute Python and return outputs."
268 }
269
270 fn is_builtin(&self) -> bool {
271 true
272 }
273
274 fn declaration(&self) -> Value {
275 json!({
276 "name": self.name(),
277 "description": self.description(),
278 "x-adk-openai-tool": self.tool_json(),
279 })
280 }
281
282 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
283 Err(adk_core::AdkError::tool("OpenAI code interpreter is handled by the Responses API"))
284 }
285}
286
287#[derive(Debug, Clone, Default)]
289pub struct OpenAIImageGenerationTool {
290 options: Map<String, Value>,
291}
292
293impl OpenAIImageGenerationTool {
294 pub fn new() -> Self {
295 Self::default()
296 }
297
298 pub fn with_option(mut self, key: impl Into<String>, value: Value) -> Self {
299 self.options.insert(key.into(), value);
300 self
301 }
302
303 fn tool_json(&self) -> Value {
304 let mut tool = self.options.clone();
305 tool.insert("type".to_string(), Value::String("image_generation".to_string()));
306 Value::Object(tool)
307 }
308}
309
310#[async_trait]
311impl Tool for OpenAIImageGenerationTool {
312 fn name(&self) -> &str {
313 "openai_image_generation"
314 }
315
316 fn description(&self) -> &str {
317 "Uses OpenAI hosted image generation."
318 }
319
320 fn is_builtin(&self) -> bool {
321 true
322 }
323
324 fn declaration(&self) -> Value {
325 json!({
326 "name": self.name(),
327 "description": self.description(),
328 "x-adk-openai-tool": self.tool_json(),
329 })
330 }
331
332 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
333 Err(adk_core::AdkError::tool("OpenAI image generation is handled by the Responses API"))
334 }
335}
336
337#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339pub enum OpenAIComputerEnvironment {
340 Browser,
341 Mac,
342 Windows,
343 Linux,
344 Ubuntu,
345}
346
347impl OpenAIComputerEnvironment {
348 fn as_wire(self) -> &'static str {
349 match self {
350 Self::Browser => "browser",
351 Self::Mac => "mac",
352 Self::Windows => "windows",
353 Self::Linux => "linux",
354 Self::Ubuntu => "ubuntu",
355 }
356 }
357}
358
359#[derive(Debug, Clone)]
361pub struct OpenAIComputerUseTool {
362 environment: OpenAIComputerEnvironment,
363 display_width: u32,
364 display_height: u32,
365}
366
367impl OpenAIComputerUseTool {
368 pub fn new(
369 environment: OpenAIComputerEnvironment,
370 display_width: u32,
371 display_height: u32,
372 ) -> Self {
373 Self { environment, display_width, display_height }
374 }
375
376 fn tool_json(&self) -> Value {
377 json!({
378 "type": "computer_use_preview",
379 "environment": self.environment.as_wire(),
380 "display_width": self.display_width,
381 "display_height": self.display_height,
382 })
383 }
384}
385
386#[async_trait]
387impl Tool for OpenAIComputerUseTool {
388 fn name(&self) -> &str {
389 "openai_computer_use"
390 }
391
392 fn description(&self) -> &str {
393 "Enables OpenAI computer use tool calls."
394 }
395
396 fn is_builtin(&self) -> bool {
397 true
398 }
399
400 fn declaration(&self) -> Value {
401 json!({
402 "name": self.name(),
403 "description": self.description(),
404 "x-adk-openai-tool": self.tool_json(),
405 })
406 }
407
408 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
409 Err(adk_core::AdkError::tool("OpenAI computer use requires client-side action handling"))
410 }
411}
412
413#[derive(Debug, Clone)]
415pub struct OpenAIMcpTool {
416 server_label: String,
417 definition: Map<String, Value>,
418}
419
420impl OpenAIMcpTool {
421 pub fn new_with_url(server_label: impl Into<String>, server_url: impl Into<String>) -> Self {
422 let server_label = server_label.into();
423 let mut definition = Map::new();
424 definition.insert("type".to_string(), Value::String("mcp".to_string()));
425 definition.insert("server_label".to_string(), Value::String(server_label.clone()));
426 definition.insert("server_url".to_string(), Value::String(server_url.into()));
427 Self { server_label, definition }
428 }
429
430 pub fn new_with_connector(
431 server_label: impl Into<String>,
432 connector_id: impl Into<String>,
433 ) -> Self {
434 let server_label = server_label.into();
435 let mut definition = Map::new();
436 definition.insert("type".to_string(), Value::String("mcp".to_string()));
437 definition.insert("server_label".to_string(), Value::String(server_label.clone()));
438 definition.insert("connector_id".to_string(), Value::String(connector_id.into()));
439 Self { server_label, definition }
440 }
441
442 pub fn with_allowed_tools(mut self, allowed_tools: Value) -> Self {
443 self.definition.insert("allowed_tools".to_string(), allowed_tools);
444 self
445 }
446
447 pub fn with_authorization(mut self, authorization: impl Into<String>) -> Self {
448 self.definition.insert("authorization".to_string(), Value::String(authorization.into()));
449 self
450 }
451
452 pub fn with_headers(mut self, headers: Map<String, Value>) -> Self {
453 self.definition.insert("headers".to_string(), Value::Object(headers));
454 self
455 }
456
457 pub fn with_require_approval(mut self, require_approval: Value) -> Self {
458 self.definition.insert("require_approval".to_string(), require_approval);
459 self
460 }
461
462 fn tool_json(&self) -> Value {
463 Value::Object(self.definition.clone())
464 }
465}
466
467#[async_trait]
468impl Tool for OpenAIMcpTool {
469 fn name(&self) -> &str {
470 &self.server_label
471 }
472
473 fn description(&self) -> &str {
474 "Grants the model access to a remote MCP server."
475 }
476
477 fn is_builtin(&self) -> bool {
478 true
479 }
480
481 fn declaration(&self) -> Value {
482 json!({
483 "name": self.name(),
484 "description": self.description(),
485 "x-adk-openai-tool": self.tool_json(),
486 })
487 }
488
489 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
490 Err(adk_core::AdkError::tool("OpenAI MCP tool calls are handled by the Responses API"))
491 }
492}
493
494#[derive(Debug, Clone, Default)]
496pub struct OpenAILocalShellTool;
497
498impl OpenAILocalShellTool {
499 pub fn new() -> Self {
500 Self
501 }
502}
503
504#[async_trait]
505impl Tool for OpenAILocalShellTool {
506 fn name(&self) -> &str {
507 "openai_local_shell"
508 }
509
510 fn description(&self) -> &str {
511 "Allows OpenAI to execute commands in the local shell tool protocol."
512 }
513
514 fn is_builtin(&self) -> bool {
515 true
516 }
517
518 fn declaration(&self) -> Value {
519 json!({
520 "name": self.name(),
521 "description": self.description(),
522 "x-adk-openai-tool": {
523 "type": "local_shell"
524 },
525 })
526 }
527
528 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
529 Err(adk_core::AdkError::tool(
530 "OpenAI local shell outputs must be handled through the Responses API item protocol",
531 ))
532 }
533}
534
535#[derive(Debug, Clone, Default)]
537pub struct OpenAIShellTool {
538 environment: Option<Value>,
539}
540
541impl OpenAIShellTool {
542 pub fn new() -> Self {
543 Self::default()
544 }
545
546 pub fn with_environment(mut self, environment: Value) -> Self {
547 self.environment = Some(environment);
548 self
549 }
550
551 fn tool_json(&self) -> Value {
552 json!({
553 "type": "shell",
554 "environment": self.environment,
555 })
556 }
557}
558
559#[async_trait]
560impl Tool for OpenAIShellTool {
561 fn name(&self) -> &str {
562 "openai_shell"
563 }
564
565 fn description(&self) -> &str {
566 "Allows OpenAI to execute commands in the managed shell tool protocol."
567 }
568
569 fn is_builtin(&self) -> bool {
570 true
571 }
572
573 fn declaration(&self) -> Value {
574 json!({
575 "name": self.name(),
576 "description": self.description(),
577 "x-adk-openai-tool": self.tool_json(),
578 })
579 }
580
581 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
582 Err(adk_core::AdkError::tool(
583 "OpenAI shell outputs must be handled through the Responses API item protocol",
584 ))
585 }
586}
587
588#[derive(Debug, Clone, Default)]
590pub struct OpenAIApplyPatchTool;
591
592impl OpenAIApplyPatchTool {
593 pub fn new() -> Self {
594 Self
595 }
596}
597
598#[async_trait]
599impl Tool for OpenAIApplyPatchTool {
600 fn name(&self) -> &str {
601 "openai_apply_patch"
602 }
603
604 fn description(&self) -> &str {
605 "Allows OpenAI to propose file patches through the native apply_patch tool."
606 }
607
608 fn is_builtin(&self) -> bool {
609 true
610 }
611
612 fn declaration(&self) -> Value {
613 json!({
614 "name": self.name(),
615 "description": self.description(),
616 "x-adk-openai-tool": {
617 "type": "apply_patch"
618 },
619 })
620 }
621
622 async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
623 Err(adk_core::AdkError::tool(
624 "OpenAI apply_patch outputs must be handled through the Responses API item protocol",
625 ))
626 }
627}