diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index f99f0102d83..a8d2fb9ad72 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -72,34 +72,42 @@ def remove_custom_field_from_tools(request_body: dict) -> None: def normalize_json_schema_custom_types_to_object(schema: dict) -> None: """ - In-place: replace JSON Schema ``type: \"custom\"`` with ``\"object\"`` recursively. + In-place: replace JSON Schema ``type: \"custom\"`` with ``\"object`` (iterative walk). Anthropic / Claude Code use ``custom`` for tool schemas; Bedrock Invoke and Bedrock Converse only accept standard JSON Schema type strings. - """ - def _fix_schema(node: Any) -> None: + Uses an explicit stack (not recursion) to satisfy recursive-function guards in CI. + """ + stack: List[Any] = [schema] + seen: set[int] = set() + while stack: + node = stack.pop() if not isinstance(node, dict): - return + continue + node_id = id(node) + if node_id in seen: + continue + seen.add(node_id) if node.get("type") == "custom": node["type"] = "object" items = node.get("items") if isinstance(items, dict): - _fix_schema(items) + stack.append(items) addl = node.get("additionalProperties") if isinstance(addl, dict): - _fix_schema(addl) + stack.append(addl) props = node.get("properties") if isinstance(props, dict): for sub in props.values(): - _fix_schema(sub) + if isinstance(sub, dict): + stack.append(sub) for combiner in ("allOf", "anyOf", "oneOf"): arr = node.get(combiner) if isinstance(arr, list): for sub in arr: - _fix_schema(sub) - - _fix_schema(schema) + if isinstance(sub, dict): + stack.append(sub) def normalize_tool_input_schema_types_for_bedrock_invoke(request_body: dict) -> None: