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