diff --git a/src/agents/strict_schema.py b/src/agents/strict_schema.py index 3f37660a0..22366e629 100644 --- a/src/agents/strict_schema.py +++ b/src/agents/strict_schema.py @@ -51,6 +51,8 @@ def _ensure_strict_json_schema( typ = json_schema.get("type") if typ == "object" and "additionalProperties" not in json_schema: json_schema["additionalProperties"] = False + elif typ == "object" and json_schema.get("additionalProperties") is None: + json_schema["additionalProperties"] = False elif ( typ == "object" and "additionalProperties" in json_schema @@ -107,6 +109,16 @@ def _ensure_strict_json_schema( if json_schema.get("default", NOT_GIVEN) is None: json_schema.pop("default") + # Remove all null values to comply with JSON Schema Draft 2020-12 + # This prevents LLM providers from rejecting schemas with null values + keys_to_remove = [] + for key, value in json_schema.items(): + if value is None: + keys_to_remove.append(key) + + for key in keys_to_remove: + json_schema.pop(key) + # we can't use `$ref`s if there are also other properties defined, e.g. # `{"$ref": "...", "description": "my description"}` # diff --git a/tests/test_strict_schema.py b/tests/test_strict_schema.py index c35e9adf7..4d63a823c 100644 --- a/tests/test_strict_schema.py +++ b/tests/test_strict_schema.py @@ -124,3 +124,52 @@ def test_invalid_ref_format(): schema = {"type": "object", "properties": {"a": {"$ref": "invalid", "description": "desc"}}} with pytest.raises(ValueError): ensure_strict_json_schema(schema) + + +def test_null_values_removal(): + """ + Test that null values are removed from JSON schemas to comply with JSON Schema Draft 2020-12. + """ + schema_with_nulls = { + "type": "object", + "properties": { + "param": { + "type": "boolean", + "default": True, + "enum": None, + "minimum": None, + "maximum": None, + "items": None, + "properties": None, + } + }, + } + + result = ensure_strict_json_schema(schema_with_nulls) + + # The result should not contain any null values + param_schema = result["properties"]["param"] + assert param_schema["type"] == "boolean" + assert param_schema["default"] is True + assert "enum" not in param_schema + assert "minimum" not in param_schema + assert "maximum" not in param_schema + assert "items" not in param_schema + assert "properties" not in param_schema + + +def test_additional_properties_none_handling(): + """ + Test that additionalProperties: None is properly converted to False to maintain strictness. + """ + schema_with_none_additional_properties = { + "type": "object", + "properties": {"param": {"type": "string"}}, + "additionalProperties": None, + } + + result = ensure_strict_json_schema(schema_with_none_additional_properties) + + # additionalProperties should be False, not removed + assert result["additionalProperties"] is False + assert "additionalProperties" in result