Skip to content

fix(ci): ignore-list recursive form-field flatteners in recursive_detector - #38149

Merged
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_fix_recursive_detector_form_flatteners
Aug 24, 2026
Merged

fix(ci): ignore-list recursive form-field flatteners in recursive_detector#38149
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_fix_recursive_detector_form_flatteners

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • code-quality check is red on every PR
  • Its recursive_detector step fails on litellm_internal_staging itself

How it solves it:

  • Ignore-list the two new recursive form-flatteners
  • Both are bounded structural recursion, matching existing entries

User Flow

Before: any contributor opening a PR sees the code-quality check go red through no fault of their own, because the base branch itself fails the detector step

  1. They push a branch and open a PR at https://github.com/BerriAI/litellm/pulls against litellm_internal_staging
  2. GitHub Actions runs the code-quality workflow and they watch the checks on their PR page
  3. The recursive_detector step exits 1 with Unignored recursive functions found in ./litellm/litellm_core_utils/llm_request_utils.py: ['_flatten_form_field', '_flatten_form_data_field'], so the code-quality check shows a red X
  4. That red check appears on every open PR regardless of what it changed, since the failure lives on the base branch

After: the same contributor opens a PR and the code-quality check passes, reflecting only their own change

  1. They push a branch and open a PR at https://github.com/BerriAI/litellm/pulls against litellm_internal_staging
  2. GitHub Actions runs the code-quality workflow and they watch the checks on their PR page
  3. The recursive_detector step exits 0 (the two flatteners are now recognized as bounded structural recursion), so the code-quality check shows a green check
  4. Their PR's checks reflect only their own change

Relevant issues

Linear ticket

Resolves LIT-6066

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

The recursive_detector gate is itself the regression test here: it fails whenever a recursive function in litellm/ is not ignore-listed, and it now passes with these two entries added, so the same breakage cannot recur silently.

Screenshots / Proof of Fix

This is a CI/lint-config fix with no runtime behavior change, so the live-proxy curl QA rule does not apply. The proof is the recursive_detector gate itself: it exits 1 before (naming the two functions) and exits 0 after. CI runs it as uv run --no-sync python ./tests/code_coverage_tests/recursive_detector.py; the script only imports ast and os, so python3 reproduces CI exactly.

Before (47c988e)

  1. python3 ./tests/code_coverage_tests/recursive_detector.py ; echo EXIT=$?
  2. Observed:
UNIGNORED RECURSIVE FUNCTIONS:  {'./litellm/litellm_core_utils/llm_request_utils.py': ['_flatten_form_field', '_flatten_form_data_field']}
...
Exception: 🚨 Unignored recursive functions found include ./litellm/litellm_core_utils/llm_request_utils.py: ['_flatten_form_field', '_flatten_form_data_field']. ...
EXIT=1

After (5d34b12)

  1. python3 ./tests/code_coverage_tests/recursive_detector.py ; echo EXIT=$?
  2. Observed:
UNIGNORED RECURSIVE FUNCTIONS:  {}
...
EXIT=0

The two functions now appear in the IGNORED RECURSIVE FUNCTIONS list instead.

Type

🚄 Infrastructure

Caveats (if any)

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

  • 5d34b12 passes /live-pr-risk


Note

Low Risk
CI allowlist-only change with no runtime or security behavior; recursion bounds are documented as structural on parsed JSON.

Overview
Fixes a base-branch failure in the code-quality workflow’s recursive_detector step, which was flagging _flatten_form_field and _flatten_form_data_field in llm_request_utils.py as unlisted recursive functions and failing every PR.

Adds both names to IGNORE_FUNCTIONS in recursive_detector.py with rationale that they recurse only over the nesting depth of an already-parsed request body (finite JSON, no cycles)—the same pattern as other bounded structural walkers on the list. No application logic changes; only the AST-based gate passes again.

Reviewed by Cursor Bugbot for commit 5d34b12. Bugbot is set up for automated code reviews on this repo. Configure here.

…ector

The recursive_detector code-quality gate fails on litellm_internal_staging
because _flatten_form_field and _flatten_form_data_field in
llm_request_utils.py are recursive but absent from IGNORE_FUNCTIONS. Both are
bounded structural recursion over an already-parsed JSON-shaped request body
(a finite tree, no cycles possible), matching the existing ignored walkers, so
add them to the ignore list with a justification comment.
@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores the recursive-detector CI gate by exempting two finite structural form-field flatteners.

  • Adds _flatten_form_field and _flatten_form_data_field to the recursion detector’s ignore list.
  • Documents the finite, acyclic request-tree rationale for both exemptions.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
tests/code_coverage_tests/recursive_detector.py Adds the two form-field flatteners to the recursive-function allowlist; no eligible blocking follow-up issue remains.

Reviews (2): Last reviewed commit: "fix(ci): ignore-list recursive form-fiel..." | Re-trigger Greptile

Comment thread tests/code_coverage_tests/recursive_detector.py
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5d34b12. Configure here.

@mateo-berri
mateo-berri enabled auto-merge August 24, 2026 21:46

@tin-berri tin-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving — this is the red I was chasing across #38144, #38114 and #38136, so landing it first turns three PRs green.

One note on the justification, since the ignore list is load-bearing. Every neighbouring entry cites an explicit bound the function enforces itself ("max depth set (MAX_STRUCTURED_CONTENT_SCAN_DEPTH)", "fails closed by raising at the cap"). These two enforce nothing — they recurse until the input stops nesting. What actually makes that safe isn't "no cycles possible"; a self-referential dict handed to flatten_form_field_values would still spin. It's that the only thing reaching these is an already-parsed request body, and json.loads caps nesting itself, so a body deep enough to matter fails to parse before it ever gets here.

Worth saying that in the comment rather than "no cycles possible" — it names the actual invariant, and it tells the next person what would break it (feeding these a hand-built Python mapping instead of a parsed body).

@mateo-berri
mateo-berri merged commit ccb03f5 into litellm_internal_staging Aug 24, 2026
79 of 80 checks passed
@mateo-berri
mateo-berri deleted the litellm_fix_recursive_detector_form_flatteners branch August 24, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants