litellm/tests/code_coverage_tests/recursive_detector.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

132 lines
5.6 KiB
Python
Raw Normal View History

import ast
import os
IGNORE_FUNCTIONS = [
"_format_type",
"_remove_additional_properties",
"_remove_strict_from_schema",
"filter_schema_fields",
"text_completion",
"_check_for_os_environ_vars",
"clean_message",
"unpack_defs",
UI - Azure Content Guardrails (#12341) * build(model_prices_and_context_window.json): remove 'supports_tool_choice' for specific mistral models Closes https://github.com/BerriAI/litellm/issues/11750 * feat: initial commit adding cleaner ui for azure text moderation guardrails * feat(guardrail_endpoints.py): add discoverable guardrail configs and improve converting base model to dict with types * fix(guardrail_provider_fields.tsx): render from api endpoint correctly * fix(guardrail_provider_fields.tsx): cleanup * refactor(guardrail_endpoints.py): refactor to handle dictionaries with literal - allows multiselect * feat(ui/): render dictionary with known keys correctly * feat(ui/): render optional params on separate page * style(ui/): style improvements to rendering optional params on the UI * feat(azure/prompt_shield.py): add azure prompt shield back on UI * fix(add_guardrail_form.tsx): fix form to handle updated api * fix(guardrail_optional_params.tsx): ensure values are nested correctly for writing to api * fix: fix linting error * feat(text_moderation.py): handle str to int conversion * fix(guardrail_info.tsx): only render pii settings if guardrail is presidio * fix(guardrail_info.tsx): add guardrail specific fields to update settings allows updating guardrail fields (e.g. severity threshold) post-create * fix(guardrail_endpoints.py): set guardrail_id in guardrail object ensures duplicate objects not created on guardrail update * fix(guardrail_endpoints.py): allow provider specific fields to be updated on patch update * refactor(guardrail_endpoints.py): remove duplicate info endpoint * fix(guardrail_endpoints.py): mask sensitive keys on returning via guardrail `/info` Prevent leaking keys * fix(guardrail_optional_params.tsx): return numerical input when numerical component used fixes issue where output was a str * fix(guardrail_optional_params.tsx): render dict keys correctly * fix(text_moderation.py): fix severity by category check * fix(proxy/utils.py): check if guardrail should run for post call streaming hook Prevents invalid guardrails from running if not requested * test: fix import * fix: fix linting error * test: update test * fix: fix tests * fix: fix code qa errors * fix(guardrail_endpoints.py): set max depth for function * test: update recursive_detector.py * test: update list * build: merge main * fix: fix ruff check errors
2025-07-06 01:19:29 +08:00
"convert_anyof_null_to_nullable", # has a set max depth
"add_object_type",
"strip_field",
"_transform_prompt",
"mask_dict",
"_serialize", # we now set a max depth for this
UI - Azure Content Guardrails (#12341) * build(model_prices_and_context_window.json): remove 'supports_tool_choice' for specific mistral models Closes https://github.com/BerriAI/litellm/issues/11750 * feat: initial commit adding cleaner ui for azure text moderation guardrails * feat(guardrail_endpoints.py): add discoverable guardrail configs and improve converting base model to dict with types * fix(guardrail_provider_fields.tsx): render from api endpoint correctly * fix(guardrail_provider_fields.tsx): cleanup * refactor(guardrail_endpoints.py): refactor to handle dictionaries with literal - allows multiselect * feat(ui/): render dictionary with known keys correctly * feat(ui/): render optional params on separate page * style(ui/): style improvements to rendering optional params on the UI * feat(azure/prompt_shield.py): add azure prompt shield back on UI * fix(add_guardrail_form.tsx): fix form to handle updated api * fix(guardrail_optional_params.tsx): ensure values are nested correctly for writing to api * fix: fix linting error * feat(text_moderation.py): handle str to int conversion * fix(guardrail_info.tsx): only render pii settings if guardrail is presidio * fix(guardrail_info.tsx): add guardrail specific fields to update settings allows updating guardrail fields (e.g. severity threshold) post-create * fix(guardrail_endpoints.py): set guardrail_id in guardrail object ensures duplicate objects not created on guardrail update * fix(guardrail_endpoints.py): allow provider specific fields to be updated on patch update * refactor(guardrail_endpoints.py): remove duplicate info endpoint * fix(guardrail_endpoints.py): mask sensitive keys on returning via guardrail `/info` Prevent leaking keys * fix(guardrail_optional_params.tsx): return numerical input when numerical component used fixes issue where output was a str * fix(guardrail_optional_params.tsx): render dict keys correctly * fix(text_moderation.py): fix severity by category check * fix(proxy/utils.py): check if guardrail should run for post call streaming hook Prevents invalid guardrails from running if not requested * test: fix import * fix: fix linting error * test: update test * fix: fix tests * fix: fix code qa errors * fix(guardrail_endpoints.py): set max depth for function * test: update recursive_detector.py * test: update list * build: merge main * fix: fix ruff check errors
2025-07-06 01:19:29 +08:00
"_sanitize_request_body_for_spend_logs_payload", # testing added for circular reference
"_sanitize_value", # testing added for circular reference
"set_schema_property_ordering", # testing added for infinite recursion
"process_items", # testing added for infinite recursion + max depth set.
"_can_object_call_model", # max depth set.
"encode_unserializable_types", # max depth set.
"filter_value_from_dict", # max depth set.
"normalize_json_schema_types", # max depth set.
"_extract_fields_recursive", # max depth set.
"_remove_json_schema_refs", # max depth set.,
"_convert_schema_types", # max depth set.,
"_fix_enum_empty_strings", # max depth set.,
2025-09-09 08:33:36 +08:00
"get_access_token", # max depth set.,
2025-10-29 08:38:16 +08:00
"_redact_base64", # max depth set.
"_contains_vision_content", # max depth set.
"_read_all_bytes", # max depth set.
"_fix_enum_types", # max depth set.
"_collect_argument_paths", # max depth set.
"_split_text", # max depth set.
2026-01-11 05:20:43 +08:00
"_mask_sequence", # max depth set.
"_delete_nested_value_custom", # max depth set (bounded by number of path segments).
[Fix] CI/CD - Fix Bedrock tool calling test failures with non-serializable objects and internal parameters (#17930) * fix(bedrock): filter non-serializable objects from request params - Enhanced filter_exceptions_from_params() to filter callable objects (functions) and Logging objects - Applied filtering in Bedrock's _prepare_request_params() before deepcopy - Applied filtering to additional_request_params before JSON serialization - Prevents TypeError during deepcopy (APIConnectionError objects) and JSON serialization (functions, Logging objects) - Fixes test_bedrock_tool_calling test failures Root cause: MCP-related functions (handle_chat_completion_with_mcp, completion_callable) and litellm_logging_obj were incorrectly added to optional_params via add_provider_specific_params_to_optional_params(), which then ended up in additional_request_params. These objects should be in litellm_params, not optional_params. * fix(bedrock): filter internal MCP parameters from API requests Filter out LiteLLM internal/MCP-related parameters (skip_mcp_handler, mcp_handler_context, _skip_mcp_handler) from additional_request_params before sending to Bedrock API to prevent 'extraneous key' errors. - Added filter_internal_params() helper function in core_helpers.py - Applied filtering in Bedrock's _prepare_request_params() method - Fixes test_bedrock_completion.py::test_bedrock_tool_calling * fix: mypy type error * fix: add filter_exceptions_from_params to recursive function ignore list - Add filter_exceptions_from_params to IGNORE_FUNCTIONS in recursive_detector.py - Function is safe: has max_depth parameter (default 20) to prevent infinite recursion
2025-12-14 04:38:07 +08:00
"filter_exceptions_from_params", # max depth set (default 20) to prevent infinite recursion.
"__getattr__", # lazy loading pattern in litellm/__init__.py with proper caching to prevent infinite recursion.
"_validate_inheritance_chain", # max depth set (default 100) to prevent infinite recursion in policy inheritance validation.
"_basic_json_schema_validate", # max depth set.
"extract_text_from_a2a_message", # max depth set (default 10) to prevent infinite recursion in A2A message parsing.
"_convert_to_json_serializable_dict", # max depth set (default 20) and circular reference protection to prevent infinite recursion.
2026-02-15 04:59:15 +08:00
"dict", # max depth set. _LiteLLMParamsDictView.dict() calls builtin dict(), not itself.
"_read_image_bytes", # max depth set.
]
class RecursiveFunctionFinder(ast.NodeVisitor):
def __init__(self):
self.recursive_functions = []
self.ignored_recursive_functions = []
def visit_FunctionDef(self, node):
# Check if the function calls itself
if any(self._is_recursive_call(node, call) for call in ast.walk(node)):
if node.name in IGNORE_FUNCTIONS:
self.ignored_recursive_functions.append(node.name)
else:
self.recursive_functions.append(node.name)
self.generic_visit(node)
def _is_recursive_call(self, func_node, call_node):
# Check if the call node is a function call
if not isinstance(call_node, ast.Call):
return False
# Case 1: Direct function call (e.g., my_func())
if isinstance(call_node.func, ast.Name) and call_node.func.id == func_node.name:
return True
# Case 2: Method call with self (e.g., self.my_func())
if isinstance(call_node.func, ast.Attribute) and isinstance(
call_node.func.value, ast.Name
):
return (
call_node.func.value.id == "self"
and call_node.func.attr == func_node.name
)
return False
def find_recursive_functions_in_file(file_path):
with open(file_path, "r") as file:
tree = ast.parse(file.read(), filename=file_path)
finder = RecursiveFunctionFinder()
finder.visit(tree)
return finder.recursive_functions, finder.ignored_recursive_functions
def find_recursive_functions_in_directory(directory):
recursive_functions = {}
ignored_recursive_functions = {}
for root, _, files in os.walk(directory):
for file in files:
print("file: ", file)
if file.endswith(".py"):
file_path = os.path.join(root, file)
functions, ignored = find_recursive_functions_in_file(file_path)
if functions:
recursive_functions[file_path] = functions
if ignored:
ignored_recursive_functions[file_path] = ignored
return recursive_functions, ignored_recursive_functions
UI - Azure Content Guardrails (#12341) * build(model_prices_and_context_window.json): remove 'supports_tool_choice' for specific mistral models Closes https://github.com/BerriAI/litellm/issues/11750 * feat: initial commit adding cleaner ui for azure text moderation guardrails * feat(guardrail_endpoints.py): add discoverable guardrail configs and improve converting base model to dict with types * fix(guardrail_provider_fields.tsx): render from api endpoint correctly * fix(guardrail_provider_fields.tsx): cleanup * refactor(guardrail_endpoints.py): refactor to handle dictionaries with literal - allows multiselect * feat(ui/): render dictionary with known keys correctly * feat(ui/): render optional params on separate page * style(ui/): style improvements to rendering optional params on the UI * feat(azure/prompt_shield.py): add azure prompt shield back on UI * fix(add_guardrail_form.tsx): fix form to handle updated api * fix(guardrail_optional_params.tsx): ensure values are nested correctly for writing to api * fix: fix linting error * feat(text_moderation.py): handle str to int conversion * fix(guardrail_info.tsx): only render pii settings if guardrail is presidio * fix(guardrail_info.tsx): add guardrail specific fields to update settings allows updating guardrail fields (e.g. severity threshold) post-create * fix(guardrail_endpoints.py): set guardrail_id in guardrail object ensures duplicate objects not created on guardrail update * fix(guardrail_endpoints.py): allow provider specific fields to be updated on patch update * refactor(guardrail_endpoints.py): remove duplicate info endpoint * fix(guardrail_endpoints.py): mask sensitive keys on returning via guardrail `/info` Prevent leaking keys * fix(guardrail_optional_params.tsx): return numerical input when numerical component used fixes issue where output was a str * fix(guardrail_optional_params.tsx): render dict keys correctly * fix(text_moderation.py): fix severity by category check * fix(proxy/utils.py): check if guardrail should run for post call streaming hook Prevents invalid guardrails from running if not requested * test: fix import * fix: fix linting error * test: update test * fix: fix tests * fix: fix code qa errors * fix(guardrail_endpoints.py): set max depth for function * test: update recursive_detector.py * test: update list * build: merge main * fix: fix ruff check errors
2025-07-06 01:19:29 +08:00
if __name__ == "__main__":
# Example usage
# raise exception if any recursive functions are found, except for the ignored ones
# this is used in the CI/CD pipeline to prevent recursive functions from being merged
directory_path = "./litellm"
recursive_functions, ignored_recursive_functions = (
find_recursive_functions_in_directory(directory_path)
)
print("UNIGNORED RECURSIVE FUNCTIONS: ", recursive_functions)
print("IGNORED RECURSIVE FUNCTIONS: ", ignored_recursive_functions)
if len(recursive_functions) > 0:
# raise exception if any recursive functions are found
for file, functions in recursive_functions.items():
print(
UI - Azure Content Guardrails (#12341) * build(model_prices_and_context_window.json): remove 'supports_tool_choice' for specific mistral models Closes https://github.com/BerriAI/litellm/issues/11750 * feat: initial commit adding cleaner ui for azure text moderation guardrails * feat(guardrail_endpoints.py): add discoverable guardrail configs and improve converting base model to dict with types * fix(guardrail_provider_fields.tsx): render from api endpoint correctly * fix(guardrail_provider_fields.tsx): cleanup * refactor(guardrail_endpoints.py): refactor to handle dictionaries with literal - allows multiselect * feat(ui/): render dictionary with known keys correctly * feat(ui/): render optional params on separate page * style(ui/): style improvements to rendering optional params on the UI * feat(azure/prompt_shield.py): add azure prompt shield back on UI * fix(add_guardrail_form.tsx): fix form to handle updated api * fix(guardrail_optional_params.tsx): ensure values are nested correctly for writing to api * fix: fix linting error * feat(text_moderation.py): handle str to int conversion * fix(guardrail_info.tsx): only render pii settings if guardrail is presidio * fix(guardrail_info.tsx): add guardrail specific fields to update settings allows updating guardrail fields (e.g. severity threshold) post-create * fix(guardrail_endpoints.py): set guardrail_id in guardrail object ensures duplicate objects not created on guardrail update * fix(guardrail_endpoints.py): allow provider specific fields to be updated on patch update * refactor(guardrail_endpoints.py): remove duplicate info endpoint * fix(guardrail_endpoints.py): mask sensitive keys on returning via guardrail `/info` Prevent leaking keys * fix(guardrail_optional_params.tsx): return numerical input when numerical component used fixes issue where output was a str * fix(guardrail_optional_params.tsx): render dict keys correctly * fix(text_moderation.py): fix severity by category check * fix(proxy/utils.py): check if guardrail should run for post call streaming hook Prevents invalid guardrails from running if not requested * test: fix import * fix: fix linting error * test: update test * fix: fix tests * fix: fix code qa errors * fix(guardrail_endpoints.py): set max depth for function * test: update recursive_detector.py * test: update list * build: merge main * fix: fix ruff check errors
2025-07-06 01:19:29 +08:00
f"🚨 Unignored recursive functions found in {file}: {functions}. THIS IS REALLY BAD, it has caused CPU Usage spikes in the past. Only keep this if it's ABSOLUTELY necessary."
)
file, functions = list(recursive_functions.items())[0]
raise Exception(
UI - Azure Content Guardrails (#12341) * build(model_prices_and_context_window.json): remove 'supports_tool_choice' for specific mistral models Closes https://github.com/BerriAI/litellm/issues/11750 * feat: initial commit adding cleaner ui for azure text moderation guardrails * feat(guardrail_endpoints.py): add discoverable guardrail configs and improve converting base model to dict with types * fix(guardrail_provider_fields.tsx): render from api endpoint correctly * fix(guardrail_provider_fields.tsx): cleanup * refactor(guardrail_endpoints.py): refactor to handle dictionaries with literal - allows multiselect * feat(ui/): render dictionary with known keys correctly * feat(ui/): render optional params on separate page * style(ui/): style improvements to rendering optional params on the UI * feat(azure/prompt_shield.py): add azure prompt shield back on UI * fix(add_guardrail_form.tsx): fix form to handle updated api * fix(guardrail_optional_params.tsx): ensure values are nested correctly for writing to api * fix: fix linting error * feat(text_moderation.py): handle str to int conversion * fix(guardrail_info.tsx): only render pii settings if guardrail is presidio * fix(guardrail_info.tsx): add guardrail specific fields to update settings allows updating guardrail fields (e.g. severity threshold) post-create * fix(guardrail_endpoints.py): set guardrail_id in guardrail object ensures duplicate objects not created on guardrail update * fix(guardrail_endpoints.py): allow provider specific fields to be updated on patch update * refactor(guardrail_endpoints.py): remove duplicate info endpoint * fix(guardrail_endpoints.py): mask sensitive keys on returning via guardrail `/info` Prevent leaking keys * fix(guardrail_optional_params.tsx): return numerical input when numerical component used fixes issue where output was a str * fix(guardrail_optional_params.tsx): render dict keys correctly * fix(text_moderation.py): fix severity by category check * fix(proxy/utils.py): check if guardrail should run for post call streaming hook Prevents invalid guardrails from running if not requested * test: fix import * fix: fix linting error * test: update test * fix: fix tests * fix: fix code qa errors * fix(guardrail_endpoints.py): set max depth for function * test: update recursive_detector.py * test: update list * build: merge main * fix: fix ruff check errors
2025-07-06 01:19:29 +08:00
f"🚨 Unignored recursive functions found include {file}: {functions}. THIS IS REALLY BAD, it has caused CPU Usage spikes in the past. Only keep this if it's ABSOLUTELY necessary."
)