Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/services/variable_truncator.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ def _truncate_array(self, value: list[object], target_size: int) -> _PartResult[
used_size += 1 # Account for comma

if used_size > target_size:
truncated = True
break

remaining_budget = target_size - used_size
Expand All @@ -304,7 +305,7 @@ def _truncate_array(self, value: list[object], target_size: int) -> _PartResult[
raise UnknownTypeError(f"got unknown type {type(item)} in array truncation")
truncated_value.append(part_result.value)
used_size += part_result.value_size
truncated = part_result.truncated
truncated = truncated or part_result.truncated
return _PartResult(truncated_value, used_size, truncated)

@classmethod
Expand Down
28 changes: 28 additions & 0 deletions api/tests/unit_tests/services/test_variable_truncator.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,34 @@ def test_array_size_budget_truncation(self, small_truncator: VariableTruncator):
assert isinstance(item, str)
assert VariableTruncator.calculate_json_size(result.value) <= 50

def test_array_budget_break_sets_truncated_flag(self):
"""Budget break drops trailing elements, so it must report truncated=True.

Regression for a defect where ``_truncate_array`` ``break``ed on the size
budget without flagging truncation, unlike the length-based path.
"""
truncator = VariableTruncator(array_element_limit=20, max_size_bytes=1000)
# Each int fits individually, but the running total exceeds target_size=12,
# so the loop breaks at the 5th element and discards it.
result = truncator._truncate_array([10, 20, 30, 40, 50], 12)
assert result.value == [10, 20, 30, 40] # trailing 50 was dropped
assert result.truncated is True

def test_array_truncated_flag_not_reset_by_later_untruncated_element(self):
"""A later element that fits must not reset the flag set by an earlier one.

Regression for a defect where the per-element ``truncated`` flag was
overwritten instead of accumulated.
"""
truncator = VariableTruncator(array_element_limit=20, max_size_bytes=1000, string_length_limit=10)
# Element 0 (long string) gets truncated; element 1 (short string) fits and
# must not flip the flag back to False. Both elements share one type, as
# real array variables are homogeneously typed.
result = truncator._truncate_array(["x" * 60, "a"], 60)
assert result.value[0] == "xxxxxxxxxx..." # long string was truncated
assert result.value[1] == "a" # short string kept intact
assert result.truncated is True

def test_array_with_nested_objects(self, small_truncator):
"""Test array truncation with nested objects."""
nested_array = [
Expand Down