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