Skip to content

fix(python): enforce size_limit_bytes in multipart ingest path#2935

Draft
KiewanVillatel wants to merge 1 commit into
mainfrom
kiewan/lsdk-207-multipart-ingest-path-has-no-batch-size-enforcement-allowing
Draft

fix(python): enforce size_limit_bytes in multipart ingest path#2935
KiewanVillatel wants to merge 1 commit into
mainfrom
kiewan/lsdk-207-multipart-ingest-path-has-no-batch-size-enforcement-allowing

Conversation

@KiewanVillatel
Copy link
Copy Markdown
Contributor

Summary

  • _multipart_ingest_ops now splits ops into multiple requests when the accumulated payload would exceed size_limit_bytes, mirroring the batch-splitting logic that already existed in _batch_ingest_run_ops
  • Adds _multipart_part_size helper in _multipart.py to estimate the byte size of a MultipartPartsAndContext (uses len(data) for bytes parts, os.fstat for file attachments)
  • Size limit respects the same hierarchy: _max_batch_size_bytes > server batch_ingest_config.size_limit_bytes > _SIZE_LIMIT_BYTES (20 MB default)
  • A single op larger than the limit is still sent (the check requires batch_size > 0 before splitting, preserving current behavior for oversized individual ops)

Linear

https://linear.app/langchain/issue/LSDK-207/multipart-ingest-path-has-no-batch-size-enforcement-allowing

Test Plan

  • test_multipart_ingest_ops_splits_by_size — calls _multipart_ingest_ops with 3 × 500-byte ops against a 600-byte limit; asserts 3 separate _send_multipart_req calls
  • test_multipart_ingest_ops_no_split_when_within_limit — 2 × 100-byte ops against 10 KB limit; asserts 1 _send_multipart_req call
  • test_batch_ingest_run_splits_large_batches[True-*] — updated expected request count to use the same formula as the False (non-multipart) parameterized variant, since both paths now enforce the same limit
  • Full unit test suite: 1208 passed, 10 pre-existing failures unrelated to this change (sandbox + test_utils env-var tests)

Release Note

Fixed an issue where the multipart ingest path could send arbitrarily large requests when many runs with large payloads were batched together, causing OOM crashes in high-throughput environments.

Mirrors the batch-splitting logic from _batch_ingest_run_ops into
_multipart_ingest_ops so that accumulated multipart payloads are split
into multiple requests when they exceed size_limit_bytes. Previously
all ops drained in a single cycle were joined into one request
regardless of size, causing OOM crashes when large tool outputs were
bundled together (e.g. 194 MB POST → repeated retries at 3 GB RSS).

Adds _multipart_part_size helper to estimate the byte size of a
MultipartPartsAndContext, and updates test_batch_ingest_run_splits_large_batches
to reflect that the multipart path now splits at the same threshold.

Fixes LSDK-207.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@KiewanVillatel KiewanVillatel marked this pull request as draft May 26, 2026 18:35
finally:
_close_files(list(opened_files_dict.values()))
except ls_utils.LangSmithNotFoundError:
# Fallback to batch ingest if multipart endpoint returns 404
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.

Do we need that?

for _, (_, data, _, _) in part.parts:
if isinstance(data, bytes):
total += len(data)
elif isinstance(data, BufferedReader):
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.

Is it checking for attachments? I am not sure we should as attachements have a higher size limit.
The multipart endpoint shouldn't error on a small JSON payload with large attachments.

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