1use ferrin_spec::JsonObject;
8use ferrin_spec::JsonValue;
9use ferrin_tool::Schema;
10use ferrin_tool::Tool;
11use serde::Serialize;
12use serde_json::json;
13
14use crate::prepare_tools::ids;
15
16fn args<T: Serialize>(value: &T) -> JsonObject {
17 match serde_json::to_value(value) {
18 Ok(JsonValue::Object(mut object)) => {
19 object.retain(|_, value| !value.is_null());
20 object
21 }
22 _ => JsonObject::new(),
23 }
24}
25
26fn executed(id: &str, args: JsonObject) -> Tool {
27 Tool::provider_executed(id, args)
28 .input_schema(Schema::any())
29 .build()
30}
31
32#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
34#[serde(rename_all = "camelCase")]
35pub struct SearchTypes {
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub web_search: Option<JsonObject>,
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub image_search: Option<JsonObject>,
42}
43
44#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
46#[serde(rename_all = "camelCase")]
47pub struct TimeRangeFilter {
48 pub start_time: String,
50 pub end_time: String,
52}
53
54#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
56#[serde(rename_all = "camelCase")]
57pub struct GoogleSearchArgs {
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub search_types: Option<SearchTypes>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub time_range_filter: Option<TimeRangeFilter>,
64}
65
66#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
68#[serde(rename_all = "camelCase")]
69pub struct FileSearchArgs {
70 pub file_search_store_names: Vec<String>,
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub top_k: Option<u32>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub metadata_filter: Option<String>,
78}
79
80#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
82#[serde(rename_all = "camelCase")]
83pub struct VertexRagStoreArgs {
84 pub rag_corpus: String,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub top_k: Option<u32>,
89}
90
91#[derive(Debug, Clone, Copy, Default)]
93pub struct GoogleTools;
94
95impl GoogleTools {
96 #[must_use]
98 pub fn new() -> Self {
99 Self
100 }
101
102 #[must_use]
104 pub fn google_search(&self, config: GoogleSearchArgs) -> Tool {
105 executed(ids::GOOGLE_SEARCH, args(&config))
106 }
107
108 #[must_use]
110 pub fn enterprise_web_search(&self) -> Tool {
111 executed(ids::ENTERPRISE_WEB_SEARCH, JsonObject::new())
112 }
113
114 #[must_use]
116 pub fn url_context(&self) -> Tool {
117 executed(ids::URL_CONTEXT, JsonObject::new())
118 }
119
120 #[must_use]
122 pub fn code_execution(&self) -> Tool {
123 Tool::provider_executed(ids::CODE_EXECUTION, JsonObject::new())
124 .input_schema(Schema::from_json_schema(json!({
125 "type": "object",
126 "properties": {
127 "language": {"type": "string", "description": "The programming language of the code."},
128 "code": {"type": "string", "description": "The code to be executed."}
129 },
130 "required": ["language", "code"]
131 })))
132 .output_schema(Schema::from_json_schema(json!({
133 "type": "object",
134 "properties": {
135 "outcome": {"type": "string", "description": "The outcome of the execution (e.g., \"OUTCOME_OK\")."},
136 "output": {"type": "string", "description": "The output from the code execution."}
137 },
138 "required": ["outcome", "output"]
139 })))
140 .build()
141 }
142
143 #[must_use]
145 pub fn file_search(&self, config: FileSearchArgs) -> Tool {
146 executed(ids::FILE_SEARCH, args(&config))
147 }
148
149 #[must_use]
151 pub fn vertex_rag_store(&self, config: VertexRagStoreArgs) -> Tool {
152 executed(ids::VERTEX_RAG_STORE, args(&config))
153 }
154
155 #[must_use]
157 pub fn google_maps(&self) -> Tool {
158 executed(ids::GOOGLE_MAPS, JsonObject::new())
159 }
160}