-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Description
Description
#3457 introduced support for strict tool schemas, but the JsonTransformer will default to tool.strict=False when the user doesn't explicitly set Tool(strict=True)
# in AnthropicJsonSchemaTransformer.walk()
self.is_strict_compatible = self.strict or False # default to False if NoneThe desired behavior should be to default to True if the schema transformation is not lossy.
Here's a code snippet for potential handling:
# in profiles/anthropic.py
@staticmethod
def _has_lossy_changes(before: JsonSchema, after: JsonSchema) -> bool: # noqa: C901
"""Check if transformation dropped validation constraints.
Safe changes that don't count as lossy:
- Adding additionalProperties: false
- Removing title, $schema, or other metadata fields
- Reordering keys
Lossy changes:
- Removing validation constraints (minLength, pattern, minimum, etc.)
- Changing constraint values
- Moving constraints to description field
"""
def normalize(schema: JsonSchema) -> JsonSchema:
"""Remove fields that are safe to add/remove."""
normalized = deepcopy(schema)
# NOTE that this normalization can't be used because it's lossy in itself
# if the tool schema has `additionalProperties`, the current anthropic sdk (0.75) doesn't support it
normalized.pop('additionalProperties', None)
normalized.pop('title', None)
normalized.pop('$schema', None)
return normalized
def has_lossy_object_changes(before_obj: JsonSchema, after_obj: JsonSchema) -> bool:
"""Recursively check for lossy changes in object schemas.
Returns:
True if validation constraints were removed or modified (lossy changes detected).
False if all validation constraints are preserved (no lossy changes).
"""
validation_keys = {
'minLength',
'maxLength',
'pattern',
'format',
'minimum',
'maximum',
'exclusiveMinimum',
'exclusiveMaximum',
'minItems',
'maxItems',
'uniqueItems',
'minProperties',
'maxProperties',
}
for key in validation_keys:
if key in before_obj and key not in after_obj:
return True
# should never happen that an sdk modifies a constraint value
if key in before_obj and key in after_obj and before_obj[key] != after_obj[key]:
return True # pragma: no cover
before_props = before_obj.get('properties', {})
after_props = after_obj.get('properties', {})
for prop_name, before_prop in before_props.items():
if prop_name in after_props: # pragma: no branch
if has_lossy_schema_changes(before_prop, after_props[prop_name]):
return True
if 'items' in before_obj and 'items' in after_obj:
if has_lossy_schema_changes(before_obj['items'], after_obj['items']):
return True
before_defs = before_obj.get('$defs', {})
after_defs = after_obj.get('$defs', {})
for def_name, before_def in before_defs.items():
if def_name in after_defs: # pragma: no branch
if has_lossy_schema_changes(before_def, after_defs[def_name]): # pragma: no branch
return True
return False
def has_lossy_schema_changes(before_schema: JsonSchema, after_schema: JsonSchema) -> bool:
"""Check a single schema object for lossy changes.
Returns:
True if validation constraints were removed or modified (lossy changes detected).
False if all validation constraints are preserved (no lossy changes).
"""
if isinstance(before_schema, dict) and isinstance(after_schema, dict):
return has_lossy_object_changes(before_schema, after_schema)
# schemas should always be dicts
assert_never(False)
return has_lossy_schema_changes(normalize(before), normalize(after))References
No response
Metadata
Metadata
Assignees
Labels
No labels