Remove unnecessary fields from the tool schemas (#29381)

This PR removes two fields from JSON schemas (`$schema` and `title`),
which are not expected by any model provider, but were spuriously
included by our JSON schema library, `schemars`.

These added noise to requests and cost wasted input tokens.

### Old

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "FetchToolInput",
  "type": "object",
  "required": [
    "url"
  ],
  "properties": {
    "url": {
      "description": "The URL to fetch.",
      "type": "string"
    }
  }
}
```

### New:

```json
{
  "properties": {
    "url": {
      "description": "The URL to fetch.",
      "type": "string"
    }
  },
  "required": [
    "url"
  ],
  "type": "object"
}
```

- N/A
This commit is contained in:
Max Brunsfeld 2025-04-24 18:09:25 -07:00 committed by GitHub
parent 17ecf94f6f
commit 57d8397f53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 25 deletions

View file

@ -931,26 +931,21 @@ impl Thread {
let mut request = self.to_completion_request(cx); let mut request = self.to_completion_request(cx);
if model.supports_tools() { if model.supports_tools() {
request.tools = { request.tools = self
let mut tools = Vec::new(); .tools()
tools.extend( .read(cx)
self.tools() .enabled_tools(cx)
.read(cx) .into_iter()
.enabled_tools(cx) .filter_map(|tool| {
.into_iter() // Skip tools that cannot be supported
.filter_map(|tool| { let input_schema = tool.input_schema(model.tool_input_format()).ok()?;
// Skip tools that cannot be supported Some(LanguageModelRequestTool {
let input_schema = tool.input_schema(model.tool_input_format()).ok()?; name: tool.name(),
Some(LanguageModelRequestTool { description: tool.description(),
name: tool.name(), input_schema,
description: tool.description(), })
input_schema, })
}) .collect();
}),
);
tools
};
} }
self.stream_completion(request, model, window, cx); self.stream_completion(request, model, window, cx);

View file

@ -10,6 +10,11 @@ pub fn adapt_schema_to_format(
json: &mut Value, json: &mut Value,
format: LanguageModelToolSchemaFormat, format: LanguageModelToolSchemaFormat,
) -> Result<()> { ) -> Result<()> {
if let Value::Object(obj) = json {
obj.remove("$schema");
obj.remove("title");
}
match format { match format {
LanguageModelToolSchemaFormat::JsonSchema => Ok(()), LanguageModelToolSchemaFormat::JsonSchema => Ok(()),
LanguageModelToolSchemaFormat::JsonSchemaSubset => adapt_to_json_schema_subset(json), LanguageModelToolSchemaFormat::JsonSchemaSubset => adapt_to_json_schema_subset(json),
@ -30,10 +35,7 @@ fn adapt_to_json_schema_subset(json: &mut Value) -> Result<()> {
} }
} }
const KEYS_TO_REMOVE: [&str; 2] = ["format", "$schema"]; obj.remove("format");
for key in KEYS_TO_REMOVE {
obj.remove(key);
}
if let Some(default) = obj.get("default") { if let Some(default) = obj.get("default") {
let is_null = default.is_null(); let is_null = default.is_null();

View file

@ -110,11 +110,38 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use client::Client; use client::Client;
use clock::FakeSystemClock; use clock::FakeSystemClock;
use http_client::FakeHttpClient; use http_client::FakeHttpClient;
use schemars::JsonSchema;
use serde::Serialize;
use super::*; #[test]
fn test_json_schema() {
#[derive(Serialize, JsonSchema)]
struct GetWeatherTool {
location: String,
}
let schema = schema::json_schema_for::<GetWeatherTool>(
language_model::LanguageModelToolSchemaFormat::JsonSchema,
)
.unwrap();
assert_eq!(
schema,
serde_json::json!({
"type": "object",
"properties": {
"location": {
"type": "string"
}
},
"required": ["location"],
})
);
}
#[gpui::test] #[gpui::test]
fn test_builtin_tool_schema_compatibility(cx: &mut App) { fn test_builtin_tool_schema_compatibility(cx: &mut App) {