Repository navigation
Conversation
…utputs readLines appended every decoded chunk to one string buffer and re-scanned/re-sliced the whole buffer on each read, so a long line with no newlines (base64 images, wide DataFrames, large JSON) cost O(n^2) and blocked the event loop (~10s for a 40MB line). Decode and scan only the new chunk, accumulate line fragments in an array, and join once per emitted line. Streaming TextDecoder usage, trailing-partial-line flush at done, CR preservation, and reader.releaseLock() semantics are unchanged. Fixes e2b-dev#1990 Signed-off-by: Junyi Yao <junyi.yao.real@outlook.com>
|
We require contributors to sign our Contributor License Agreement, and we don't have @Yao-Junyi on file. You can sign our CLA at https://e2b.dev/docs/cla . Once you've signed, post a comment here that says '@cla-bot check' |
🦋 Changeset detectedLatest commit: 61cff83 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
TASTE.md review: complies, 0 violations. readLines is internal (not re-exported from index.ts) so no public surface changes; checked T-29 (streaming UTF-8 decode with replacement, flushed at stream end: kept, covered by tests) and T-1/T-2 (parity: Python uses httpx iter_lines/aiter_lines, which doesn't have this bug).
There was a problem hiding this comment.
🔍 Devin Review: 1 flag
Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
Fixes #1990.
What changed
readLines()inpackages/code-interpreter-js/src/utils.tsappended everydecoded chunk to a single string buffer and re-scanned the whole buffer with
buffer.indexOf('\n')(plusbuffer.slice()copies) on every read. For along line with no newlines — a base64-encoded image, a wide DataFrame, large
JSON — that is O(n²) in line length and blocks the Node event loop.
The fix decodes and scans only the newly arrived chunk, accumulates line
fragments in an array, and joins once per emitted line. Streaming
TextDecoderusage (multi-byte characters split across chunks still decodecorrectly), the trailing partial line yielded at stream end,
\rpreservation(lines are split on
\nonly), andreader.releaseLock()infinallyareall unchanged.
Per the repo's "equivalent changes in JS and Python" rule I checked the
Python side: both the sync and async code-interpreter stream lines through
httpx's
iter_lines()/aiter_lines(), whoseLineDecoderalreadyaccumulates fragments in a list and joins once per emitted line — no
equivalent bug there, so no Python change is needed.
Reproducer
Save as
repro-1990.mjsand run withnode repro-1990.mjs(Node 22+, nodependencies). It feeds a single-line stream with no newlines through
readLines()in 64 KiB chunks —readLinesBeforeis the implementation onmain before this PR,
readLinesAfteris this PR's version:Output (verbatim, measured on this machine):
4× the input → ~19× the time before the fix (quadratic), ~2.6× after
(linear); the 40MB line goes from ~9s of blocked event loop to ~0.1s.
Tests
packages/code-interpreter-js/tests/utils.test.ts:a 1MB single line assembled from 1 KiB chunks yields one intact line;
\rkept when splitting on\n; newline straddling a chunk boundary;empty stream; leading empty chunks.
pnpm vitest run tests/utils.test.ts→ 15/15 pass. (The package's othertest files are live-API integration tests requiring
E2B_API_KEYand failidentically without the key on unmodified main.)
pnpm run format,pnpm run lint(0 errors; 1 pre-existing warning inuntouched
src/messaging.ts),pnpm run typecheckall pass.Changeset
Patch changeset for
@e2b/code-interpreterincluded.