From 32df6504cc8b3027d5ad9712096e5b36424fade7 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 27 Oct 2026 19:53:55 +0000 Subject: [PATCH] Freeze mutable values nested inside tuple arguments --- great_ai/helper/freeze_arguments.py | 6 ++++++ tests/test_cache_regressions.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/test_cache_regressions.py diff --git a/great_ai/helper/freeze_arguments.py b/great_ai/helper/freeze_arguments.py index 524906f..3085394 100644 --- a/great_ai/helper/freeze_arguments.py +++ b/great_ai/helper/freeze_arguments.py @@ -68,6 +68,12 @@ def freeze(value: Union[Sequence[Any], Mapping[str, Any], Set[Any], BaseModel]) if isinstance(value, set): return FrozenSet(value) + if isinstance(value, tuple): + values = tuple(freeze(item) for item in value) + if hasattr(type(value), "_fields"): # preserve named-tuple argument types + return type(value)(*values) + return values + if isinstance(value, BaseModel): # Keep the already-validated values, aliases, nested types and private # attributes. Reconstructing from model_dump() runs validators again. diff --git a/tests/test_cache_regressions.py b/tests/test_cache_regressions.py new file mode 100644 index 0000000..4edfa5c --- /dev/null +++ b/tests/test_cache_regressions.py @@ -0,0 +1,17 @@ +from typing import Dict, Tuple + +from great_ai import GreatAI + + +def test_nested_tuple_inputs_can_be_cached(isolated_context: None) -> None: + calls = 0 + + @GreatAI.create + def predict(value: Tuple[Dict[str, int]]) -> int: + nonlocal calls + calls += 1 + return value[0]["x"] + + assert predict(({"x": 7},)).output == 7 + assert predict(({"x": 7},)).output == 7 + assert calls == 1