From 7c1390c8364126e4e3c1f08643bc9570851d98a2 Mon Sep 17 00:00:00 2001 From: kerry Date: Tue, 6 Oct 2026 00:54:14 +0000 Subject: [PATCH] fix(caching): drop TypeIs guard and register Responses prompt walker with the recursion check Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../prompt_templates/common_utils.py | 8 +------- tests/code_coverage_tests/recursive_detector.py | 14 ++++---------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/common_utils.py b/litellm/litellm_core_utils/prompt_templates/common_utils.py index 59c582e181e..3e5aa4747cf 100644 --- a/litellm/litellm_core_utils/prompt_templates/common_utils.py +++ b/litellm/litellm_core_utils/prompt_templates/common_utils.py @@ -13,8 +13,6 @@ from pathlib import Path from types import MappingProxyType from typing import TYPE_CHECKING, Any, Final, Literal, TypeVar, cast -from typing_extensions import TypeIs # noqa: TID251 # narrows untyped message payloads without a runtime conversion - import litellm from litellm import verbose_logger from litellm.router_utils.batch_utils import InMemoryFile @@ -328,7 +326,7 @@ def _compact_json(value: object) -> str: def _dumped_dicts(values: object) -> tuple[Mapping[str, object], ...]: - return tuple(item for item in map(_dumped, _sequence(values)) if _is_str_mapping(item)) + return tuple(item for item in map(_dumped, _sequence(values)) if isinstance(item, Mapping)) def _sequence(values: object) -> Sequence[object]: @@ -352,10 +350,6 @@ def _field(item: object, key: str) -> object: return getattr(item, key, None) -def _is_str_mapping(value: object) -> TypeIs[Mapping[str, object]]: - return isinstance(value, Mapping) - - def is_non_content_values_set(message: AllMessageValues) -> bool: ignore_keys: Final = ["content", "role", "name"] return any(message.get(key, None) is not None for key in message if key not in ignore_keys) diff --git a/tests/code_coverage_tests/recursive_detector.py b/tests/code_coverage_tests/recursive_detector.py index 863e8befcf9..4a024f6f7cb 100644 --- a/tests/code_coverage_tests/recursive_detector.py +++ b/tests/code_coverage_tests/recursive_detector.py @@ -23,6 +23,7 @@ IGNORE_FUNCTIONS = [ "_can_object_call_model", # max depth set. "encode_unserializable_types", # max depth set. "filter_value_from_dict", # max depth set. + "_responses_text_parts", # walks only the nesting a Responses `input` carries; same walk the RedisSemanticCache classmethod did before it moved here. "normalize_json_schema_types", # max depth set. "_extract_fields_recursive", # max depth set. "_remove_json_schema_refs", # max depth set., @@ -106,13 +107,8 @@ class RecursiveFunctionFinder(ast.NodeVisitor): 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 - ) + 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 @@ -147,9 +143,7 @@ if __name__ == "__main__": # 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) - ) + 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)