Compare commits

...

2 Commits

Author SHA1 Message Date
Celia Chen
1cbedbb11d Merge branch 'main' into dev/cc/new-schema 2026-05-08 19:18:58 -07:00
celia-oai
e260c91a1d changes 2026-05-08 17:31:06 -07:00
2 changed files with 66 additions and 4 deletions

View File

@@ -72,6 +72,13 @@ impl JsonSchema {
}
}
pub fn any(description: Option<String>) -> Self {
Self {
description,
..Default::default()
}
}
pub fn boolean(description: Option<String>) -> Self {
Self::typed(JsonSchemaPrimitiveType::Boolean, description)
}
@@ -162,6 +169,7 @@ pub fn parse_tool_input_schema(input_schema: &JsonValue) -> Result<JsonSchema, s
/// Sanitize a JSON Schema (as serde_json::Value) so it can fit our limited
/// schema representation. This function:
/// - Ensures every typed schema object has a `"type"` when required.
/// - Preserves empty schema objects as unconstrained schemas.
/// - Preserves explicit `anyOf`.
/// - Collapses `const` into single-value `enum`.
/// - Fills required child fields for object/array schema types, including
@@ -178,6 +186,10 @@ fn sanitize_json_schema(value: &mut JsonValue) {
}
}
JsonValue::Object(map) => {
if map.is_empty() {
return;
}
if let Some(properties) = map.get_mut("properties")
&& let Some(properties_map) = properties.as_object_mut()
{

View File

@@ -250,16 +250,66 @@ fn parse_tool_input_schema_infers_string_from_enum_const_and_format_keywords() {
}
#[test]
fn parse_tool_input_schema_defaults_empty_schema_to_string() {
fn parse_tool_input_schema_preserves_empty_schema_as_any() {
// Example schema shape:
// {}
//
// Expected normalization behavior:
// - With no structural hints at all, the normalizer falls back to a
// permissive string schema.
// - Empty schema objects are unconstrained schemas in JSON Schema, so
// preserve them as unconstrained instead of narrowing to a primitive.
let schema = parse_tool_input_schema(&serde_json::json!({})).expect("parse schema");
assert_eq!(schema, JsonSchema::string(/*description*/ None));
assert_eq!(schema, JsonSchema::any(/*description*/ None));
}
#[test]
fn parse_tool_input_schema_preserves_nested_empty_property_schema_as_any() {
// Example schema shape:
// {
// "type": "object",
// "properties": {
// "body": {
// "type": "object",
// "properties": {
// "parent": {}
// }
// }
// }
// }
//
// Expected normalization behavior:
// - Nested `{}` property schemas remain unconstrained so object values are
// not misrepresented to the model as strings.
let schema = parse_tool_input_schema(&serde_json::json!({
"type": "object",
"properties": {
"body": {
"type": "object",
"properties": {
"parent": {}
}
}
}
}))
.expect("parse schema");
assert_eq!(
schema,
JsonSchema::object(
BTreeMap::from([(
"body".to_string(),
JsonSchema::object(
BTreeMap::from([
("parent".to_string(), JsonSchema::any(/*description*/ None),)
]),
/*required*/ None,
/*additional_properties*/ None,
),
)]),
/*required*/ None,
/*additional_properties*/ None
)
);
}
#[test]