agent2: Fix tool schemas for Gemini (#36507)

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
This commit is contained in:
Bennet Bo Fenner 2025-08-19 20:06:09 +02:00 committed by GitHub
parent 1af47a563f
commit 6b6eb11643
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 4 deletions

View file

@ -2,6 +2,7 @@ mod agent;
mod native_agent_server;
mod templates;
mod thread;
mod tool_schema;
mod tools;
#[cfg(test)]

View file

@ -1732,8 +1732,8 @@ where
fn initial_title(&self, input: Result<Self::Input, serde_json::Value>) -> SharedString;
/// Returns the JSON schema that describes the tool's input.
fn input_schema(&self) -> Schema {
schemars::schema_for!(Self::Input)
fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Schema {
crate::tool_schema::root_schema_for::<Self::Input>(format)
}
/// Some tools rely on a provider for the underlying billing or other reasons.
@ -1819,7 +1819,7 @@ where
}
fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> {
let mut json = serde_json::to_value(self.0.input_schema())?;
let mut json = serde_json::to_value(self.0.input_schema(format))?;
adapt_schema_to_format(&mut json, format)?;
Ok(json)
}

View file

@ -0,0 +1,43 @@
use language_model::LanguageModelToolSchemaFormat;
use schemars::{
JsonSchema, Schema,
generate::SchemaSettings,
transform::{Transform, transform_subschemas},
};
pub(crate) fn root_schema_for<T: JsonSchema>(format: LanguageModelToolSchemaFormat) -> Schema {
let mut generator = match format {
LanguageModelToolSchemaFormat::JsonSchema => SchemaSettings::draft07().into_generator(),
LanguageModelToolSchemaFormat::JsonSchemaSubset => SchemaSettings::openapi3()
.with(|settings| {
settings.meta_schema = None;
settings.inline_subschemas = true;
})
.with_transform(ToJsonSchemaSubsetTransform)
.into_generator(),
};
generator.root_schema_for::<T>()
}
#[derive(Debug, Clone)]
struct ToJsonSchemaSubsetTransform;
impl Transform for ToJsonSchemaSubsetTransform {
fn transform(&mut self, schema: &mut Schema) {
// Ensure that the type field is not an array, this happens when we use
// Option<T>, the type will be [T, "null"].
if let Some(type_field) = schema.get_mut("type")
&& let Some(types) = type_field.as_array()
&& let Some(first_type) = types.first()
{
*type_field = first_type.clone();
}
// oneOf is not supported, use anyOf instead
if let Some(one_of) = schema.remove("oneOf") {
schema.insert("anyOf".to_string(), one_of);
}
transform_subschemas(self, schema);
}
}

View file

@ -266,7 +266,7 @@ pub struct CitationMetadata {
pub struct PromptFeedback {
#[serde(skip_serializing_if = "Option::is_none")]
pub block_reason: Option<String>,
pub safety_ratings: Vec<SafetyRating>,
pub safety_ratings: Option<Vec<SafetyRating>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub block_reason_message: Option<String>,
}