Skip to content

Add incremental files.writeStream / files.readStream#19

Open
matthoare117-wandb wants to merge 9 commits into
mainfrom
hoare-streaming
Open

Add incremental files.writeStream / files.readStream#19
matthoare117-wandb wants to merge 9 commits into
mainfrom
hoare-streaming

Conversation

@matthoare117-wandb

@matthoare117-wandb matthoare117-wandb commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #12

Buffered files.read / files.write (and the #9 StreamExec auto-fallback) still materialize the full payload in the client. This adds Python-shaped incremental APIs so large or already-chunked data can move without holding the whole file in memory.

Summary

  • Add files.writeStream / files.readStream (Option A): write from Uint8Array | Iterable | AsyncIterable, read as AsyncIterable<Uint8Array>
  • Public commands.start({ binaryOutput, streamStdoutOnly }) and process.stdoutBinary so large reads can drain without accumulating wait().stdoutBytes
  • Typed CWSandboxStreamBackpressureError for STREAM_BACKPRESSURE (not remasked as CWSandboxFileError)
  • README documents buffered vs Add automatic large-file read/write fallback via exec-stream #9 fallback vs incremental streaming (partial write / drain promptly)

Usage

// Incremental write — bare buffer is sliced at 64 KiB, or pass chunk iterables
await sandbox.files.writeStream("/tmp/big.bin", new Uint8Array(1024));

await sandbox.files.writeStream("/tmp/chunks.bin", [
  new Uint8Array([1, 2]),
  new Uint8Array([3, 4]),
]);

// Incremental read — drain promptly into a fast local sink
let total = 0;
for await (const chunk of sandbox.files.readStream("/tmp/big.bin")) {
  total += chunk.byteLength;
}

Binary command stdout (used under the hood by readStream with streamStdoutOnly):

const process = await sandbox.commands.start(["cat", "/tmp/blob.bin"], {
  binaryOutput: true,
  streamStdoutOnly: true,
});

for await (const chunk of process.stdoutBinary) {
  // fast local sink only — avoid slow work in the loop
  void chunk;
}

await process.wait();

Backpressure:

import { CWSandboxStreamBackpressureError } from "@coreweave/cwsandbox";

try {
  for await (const chunk of sandbox.files.readStream(path)) {
    /* drain */
  }
} catch (error) {
  if (error instanceof CWSandboxStreamBackpressureError) {
    // consumer fell behind; retry with a tighter drain, not the same slow loop
  }
}

Behavior notes

  • Mid-failure / abort on writeStream may leave a partial remote file (Python direct-write style)
  • readStream: pre-stat + post-drain truncation check (CWSANDBOX_FILE_TRUNCATED)
  • Bad iterable chunks → CWSandboxValidationError
  • No hard client size ceiling on streaming APIs (escape hatch past the 256 MiB buffered cap)

Test plan

  • pnpm check (unit + types + readme examples)
  • Unit: chunking, abort, truncation, validation, backpressure not remasked, stdoutBinary / streamStdoutOnly
  • Smoke: writeStream / readStream multi-chunk round-trip (passed in full pnpm smoke)
  • Full pnpm smoke green — one unrelated flake seen: TTY shell empty output (stdin before ready; separate from this PR)

Out of scope

  • Fetch-like stream-default read + readAll
  • Web ReadableStream / Node Readable input types
  • Atomic temp-and-rename writes
  • Raising the 256 MiB buffered auto-fallback ceiling

@matthoare117-wandb matthoare117-wandb changed the title streaming Add incremental files.writeStream / files.readStream Jul 25, 2026
@matthoare117-wandb
matthoare117-wandb marked this pull request as ready for review July 25, 2026 03:09
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.

Add incremental streaming for sandbox file read/write

2 participants