Skip to content

fix(api): honor array-element limit and byte budget for list[File] in VariableTruncator (#39218)#39220

Open
Taranum01 wants to merge 1 commit into
langgenius:mainfrom
Taranum01:fix/39218-file-array-truncation
Open

fix(api): honor array-element limit and byte budget for list[File] in VariableTruncator (#39218)#39220
Taranum01 wants to merge 1 commit into
langgenius:mainfrom
Taranum01:fix/39218-file-array-truncation

Conversation

@Taranum01

@Taranum01 Taranum01 commented Jul 18, 2026

Copy link
Copy Markdown

Summary

VariableTruncator._truncate_array had a "Dirty fix" branch added in
#26133 that
unconditionally appended every File element to truncated_value
before the count cap and the byte-budget check, and before
used_size was ever incremented
. As a result a list[File] was:

  1. never capped at array_element_limit (default 20 items),
  2. never counted against max_size_bytes (default 1000 KB), and
  3. always reported truncated=False.

This directly contradicts the class's own stated guarantee
("ensuring the final size doesn't exceed specified limits",
VariableTruncator docstring) and silently inflated the per-key byte
budget reported back to truncate_variable_mapping, which uses
used_size to compute the remaining budget for sibling keys in the
same inputs/outputs/process_data mapping.

_truncate_json_primitives already has a correct, dedicated File
branch that returns the file untouched but reports its real computed
size (case File(): return _PartResult(val, self.calculate_json_size(val), False)).
The fix removes the early isinstance(item, File): ... continue
bypass and adds File to the tuple of element types handled by the
normal count-and-size accounting loop. File items still aren't
truncated (the dedicated File branch keeps that), but they now
flow through the same count cap and byte budget as every other
element type — which is what #26133 originally needed.

Reproduction (per the issue)

truncator = VariableTruncator(array_element_limit=3, max_size_bytes=1000, string_length_limit=50)

# Baseline: a plain-int array IS correctly capped at 3 items.
int_result = truncator._truncate_array(list(range(10)), target_size=200)
# -> (3, True)

# Bug: 500 File items, with the SAME 3-item limit and 200-byte target.
files = [make_file(f"f{i}") for i in range(500)]
file_result = truncator._truncate_array(files, target_size=200)
# Before fix: (500, False, 2)
# After  fix: (3, True, 713)

Regression tests

Added TestFileArrayTruncationRegression39218 in
tests/unit_tests/services/test_variable_truncator.py with 5 cases:

  • count cap honored (500 files → 3 with cap=3),
  • used_size reflects real serialized size (not 2),
  • byte budget honored (50 files with small budget → 1 file returned),
  • mixed arrays count Files toward the cap,
  • a single File in an array stays intact.

Each new test was verified to fail against the un-fixed source (via
git stash of the implementation file) and pass with the fix. Full
file: 52 passed.

Verification

  • uv run pytest tests/unit_tests/services/test_variable_truncator.py52 passed.
  • ruff check on changed files → clean.
  • ruff format --check on changed files → clean.

Fixes #39218

… VariableTruncator (langgenius#39218)

`VariableTruncator._truncate_array` had a "Dirty fix" branch added in
langgenius#26133 that unconditionally appended every `File` element to
`truncated_value` *before* the count cap and the byte-budget check,
and *before* `used_size` was ever incremented. As a result a
`list[File]` was:

  1. never capped at `array_element_limit` (default 20 items),
  2. never counted against `max_size_bytes` (default 1000 KB), and
  3. always reported `truncated=False`.

This directly contradicts the class's own stated guarantee
("ensuring the final size doesn't exceed specified limits",
`VariableTruncator` docstring) and silently inflated the per-key byte
budget reported back to `truncate_variable_mapping`, which uses
`used_size` to compute the remaining budget for sibling keys in the
same `inputs`/`outputs`/`process_data` mapping.

`_truncate_json_primitives` already has a correct, dedicated `File`
branch that returns the file untouched but reports its real computed
size. The fix removes the early `isinstance(item, File): ... continue`
bypass and adds `File` to the tuple of element types handled by the
normal count-and-size accounting loop. `File` items still aren't
truncated (the dedicated `File` branch keeps that), but they now
flow through the same count cap and byte budget as every other
element type — which is what langgenius#26133 originally needed.

Regression tests added in
`tests/unit_tests/services/test_variable_truncator.py::TestFileArrayTruncationRegression39218`
cover:
- count cap honored (500 files -> 3 with cap=3),
- `used_size` reflects real serialized size (not 2),
- byte budget honored (50 files with small budget -> 1 file returned),
- mixed arrays count Files toward the cap,
- a single File in an array stays intact.

Verified each new test fails against the un-fixed source (via
`git stash` of the implementation file) and passes with the fix.
Full file: 52 passed.

Refs langgenius#39218

Co-authored-by: Cursor <cursoragent@cursor.com>
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jul 18, 2026

@ErenAta16 ErenAta16 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified against current main: _truncate_json_primitives already has a dedicated File branch (case File(): returns the file as-is with its real calculate_json_size), it just never got reached for array elements because the old Dirty fix branch short-circuited before the type dispatch. Routing File through the same isinstance check as the other primitives is the right fix, not a new code path, just removing the thing that was bypassing the existing one.

Test coverage is thorough, count cap, byte budget, real used_size reporting, mixed File/primitive arrays, and the single-file no-truncation case are all exercised separately rather than folded into one big assertion. Confirms the fix without over-fitting to one scenario.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VariableTruncator._truncate_array: File-array items bypass both array_element_limit and the size budget

2 participants