diff --git a/tests/tools/test_schema_sanitizer.py b/tests/tools/test_schema_sanitizer.py index 018f621d97..e6b11bea81 100644 --- a/tests/tools/test_schema_sanitizer.py +++ b/tests/tools/test_schema_sanitizer.py @@ -156,6 +156,20 @@ def test_required_pruned_to_existing_properties(): assert out[0]["function"]["parameters"]["required"] == ["name"] +def test_empty_required_key_survives_sanitization(): + """A ``required`` list that is empty (or emptied by pruning) is kept as ``[]`` — strict + OpenAI-compatible proxies read a missing key as ``null`` and 400 the whole request.""" + declared_empty = _tool("t", {"type": "object", "properties": {}, "required": []}) + all_pruned = _tool("u", { + "type": "object", + "properties": {"name": {"type": "string"}}, + "required": ["missing_field"], + }) + out = sanitize_tool_schemas([declared_empty, all_pruned]) + assert out[0]["function"]["parameters"]["required"] == [] + assert out[1]["function"]["parameters"]["required"] == [] + + def test_well_formed_schema_unchanged(): schema = { "type": "object", diff --git a/tools/schema_sanitizer.py b/tools/schema_sanitizer.py index 644232882e..a8e8d8e627 100644 --- a/tools/schema_sanitizer.py +++ b/tools/schema_sanitizer.py @@ -315,11 +315,9 @@ def _sanitize_node(node: Any, path: str) -> Any: if not isinstance(out.get("properties"), dict): out["properties"] = {} if isinstance(out.get("required"), list): - valid = [r for r in out["required"] if isinstance(r, str) and r in out["properties"]] - if valid: - out["required"] = valid - else: - del out["required"] + # Keep the key even when nothing survives: ``required: []`` is valid everywhere, + # while a missing key reads as ``null`` on strict OpenAI-compatible proxies. + out["required"] = [r for r in out["required"] if isinstance(r, str) and r in out["properties"]] return out