Remove unnecessary fields from the tool schemas (#29381)
Max Brunsfeld
created 8 months ago
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
Change summary
crates/agent/src/thread.rs | 35 +++++++++------------
crates/assistant_tool/src/tool_schema.rs | 10 +++--
crates/assistant_tools/src/assistant_tools.rs | 29 ++++++++++++++++
3 files changed, 49 insertions(+), 25 deletions(-)
Detailed changes
@@ -931,26 +931,21 @@ impl Thread {
let mut request = self.to_completion_request(cx);
if model.supports_tools() {
- request.tools = {
- let mut tools = Vec::new();
- tools.extend(
- self.tools()
- .read(cx)
- .enabled_tools(cx)
- .into_iter()
- .filter_map(|tool| {
- // Skip tools that cannot be supported
- let input_schema = tool.input_schema(model.tool_input_format()).ok()?;
- Some(LanguageModelRequestTool {
- name: tool.name(),
- description: tool.description(),
- input_schema,
- })
- }),
- );
-
- tools
- };
+ request.tools = self
+ .tools()
+ .read(cx)
+ .enabled_tools(cx)
+ .into_iter()
+ .filter_map(|tool| {
+ // Skip tools that cannot be supported
+ let input_schema = tool.input_schema(model.tool_input_format()).ok()?;
+ Some(LanguageModelRequestTool {
+ name: tool.name(),
+ description: tool.description(),
+ input_schema,
+ })
+ })
+ .collect();
}
self.stream_completion(request, model, window, cx);
@@ -10,6 +10,11 @@ pub fn adapt_schema_to_format(
json: &mut Value,
format: LanguageModelToolSchemaFormat,
) -> Result<()> {
+ if let Value::Object(obj) = json {
+ obj.remove("$schema");
+ obj.remove("title");
+ }
+
match format {
LanguageModelToolSchemaFormat::JsonSchema => Ok(()),
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"];
- for key in KEYS_TO_REMOVE {
- obj.remove(key);
- }
+ obj.remove("format");
if let Some(default) = obj.get("default") {
let is_null = default.is_null();
@@ -110,11 +110,38 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
#[cfg(test)]
mod tests {
+ use super::*;
use client::Client;
use clock::FakeSystemClock;
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]
fn test_builtin_tool_schema_compatibility(cx: &mut App) {