use ferrin_spec::JsonObject;
use ferrin_spec::JsonValue;
use ferrin_tool::Schema;
use ferrin_tool::Tool;
use serde::Serialize;
use serde_json::json;
use crate::prepare_tools::ids;
fn args<T: Serialize>(value: &T) -> JsonObject {
match serde_json::to_value(value) {
Ok(JsonValue::Object(mut object)) => {
object.retain(|_, value| !value.is_null());
object
}
_ => JsonObject::new(),
}
}
fn executed(id: &str, args: JsonObject) -> Tool {
let schema = Schema::from_provider_json_schema(json!({"type":"object","properties":{}}));
Tool::provider_executed(id, args)
.input_schema(schema.clone())
.output_schema(schema)
.build()
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchTypes {
#[serde(skip_serializing_if = "Option::is_none")]
pub web_search: Option<JsonObject>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_search: Option<JsonObject>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TimeRangeFilter {
pub start_time: String,
pub end_time: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GoogleSearchArgs {
#[serde(skip_serializing_if = "Option::is_none")]
pub search_types: Option<SearchTypes>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_range_filter: Option<TimeRangeFilter>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileSearchArgs {
pub file_search_store_names: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata_filter: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VertexRagStoreArgs {
pub rag_corpus: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct GoogleTools;
impl GoogleTools {
#[must_use]
pub fn new() -> Self {
Self
}
#[must_use]
pub fn google_search(&self, config: GoogleSearchArgs) -> Tool {
executed(ids::GOOGLE_SEARCH, args(&config))
}
#[must_use]
pub fn enterprise_web_search(&self) -> Tool {
executed(ids::ENTERPRISE_WEB_SEARCH, JsonObject::new())
}
#[must_use]
pub fn url_context(&self) -> Tool {
executed(ids::URL_CONTEXT, JsonObject::new())
}
#[must_use]
pub fn code_execution(&self) -> Tool {
Tool::provider_executed(ids::CODE_EXECUTION, JsonObject::new())
.input_schema(Schema::from_provider_json_schema(json!({
"type": "object",
"properties": {
"language": {"type": "string", "description": "The programming language of the code."},
"code": {"type": "string", "description": "The code to be executed."}
},
"required": ["language", "code"]
})))
.output_schema(Schema::from_provider_json_schema(json!({
"type": "object",
"properties": {
"outcome": {"type": "string", "description": "The outcome of the execution (e.g., \"OUTCOME_OK\")."},
"output": {"type": "string", "description": "The output from the code execution."}
},
"required": ["outcome", "output"]
})))
.build()
}
#[must_use]
pub fn file_search(&self, config: FileSearchArgs) -> Tool {
executed(ids::FILE_SEARCH, args(&config))
}
#[must_use]
pub fn vertex_rag_store(&self, config: VertexRagStoreArgs) -> Tool {
executed(ids::VERTEX_RAG_STORE, args(&config))
}
#[must_use]
pub fn google_maps(&self) -> Tool {
executed(ids::GOOGLE_MAPS, JsonObject::new())
}
}