Stream lookahead: concurrent remote/API step execution for video workflows (10-12x FPS, outputs identical to sequential)#2623
Conversation
When WORKFLOWS_STEP_EXECUTION_MODE=remote and the new WORKFLOWS_REMOTE_EXECUTION_PIPELINE_DEPTH is above 1, the object detection block keeps up to depth remote model requests in flight while downstream steps (ByteTrack, visualization) run per frame, in strict frame order, through the stream-pipeline flush path. Disabled by default; activation additionally requires a workflow shape where every other step is downstream of the single pipelined model step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves the in-flight request bookkeeping (worker pool lifecycle, pending FIFO, per-image future chaining, flush-one-per-call) into a shared RemoteStreamPipeline so other remote-capable model blocks can adopt stream pipelining with a thin protocol shim. Also restricts activation to workflow shapes where every step is downstream of the single pipelined step, falling back to sequential execution otherwise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
👋 Thanks for the pull request! Here is how automated Claude review works here, so you spend credits (and reviewer time) wisely. 🚦 This PR is marked Ready for review, so automated Claude review will run — and every pass spends real credits. Warning 💸 The Claude reviewer bills in credits, not vibesAutomated review spins up a real agent that reads real code and spends real credits on every pass. It is glad to help — but it is not a rubber duck, a linter you poke in a loop, or a substitute for reading the contributing guide. Treat it like an expensive senior reviewer whose time you booked, and show up prepared. Draft when unsure, Ready when you mean it:
However you get there, arrive prepared:
Reviews are not free. A draft costs nothing to review; a Ready PR is a promise that it is worth reviewing.
|
Covers out-of-order remote completion at the RemoteStreamPipeline level (FIFO flush regardless of completion order, per-image future identity) and a real-ExecutionEngine end-to-end run of model -> ByteTrack with reversed response delays, asserting frame-ordered emissions, per-frame payload identity, and tracker-id parity with a sequential reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SegmentAnything3BlockV3 adopts the shared RemoteStreamPipeline (gated off when SAM3_EXEC_MODE routes around run_remotely; class_mapping now applies inside the remote task so it composes with deferred futures). The workflow-shape guard now admits multiple independent pipelined steps — none consuming another's output, every remaining step downstream of their union — and the flush-emitting runner drains one pending request from every pipelined step per emitted frame. The benchmark gained sam3 and two-models workflow shapes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Blocks now declare statefulness via a manifest classmethod (is_stateful_for_video_processing, conservative default: stateful) — the first-class version of the stateful/stateless block metadata discussed for the EE. The runner partitions the compiled DAG once into a frontier (steps with all-stateless ancestors; pipelined model steps are launch-only sinks) and a remainder. Each frame's deferred pass executes the frontier and keeps the live ExecutionDataManager buffered; ordered emission resumes it, running only the remainder, with in-flight request futures resolved by the existing input-assembly machinery. This replaces the flush-per-frame emission path and the block-side pending-request FIFO entirely (RemoteStreamPipeline shrinks to a worker pool + future chaining; the EE loses the deferred-downstream hook and gains run/resume lookahead entry points sharing one execution loop). New capabilities: stateless preprocessing upstream of pipelined models (e.g. static crop) and stateless side branches now execute ahead of stream order instead of forcing sequential fallback. Note: unit tests for the previous flush-based emission are migrated in the follow-up commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Correctness: drain buffered frames best-effort when the inference thread errors and isolate per-frame failures during the lookahead drain (a failing remote request no longer drops completed frames); chained prediction futures surface payload-selection errors instead of hanging until the resolution timeout; SAM3 no longer mutates its class_names argument inside the worker task; trace and heatmap visualizations declare themselves stateful (they accumulate cross-frame state) with regression tests. Scope guards: lookahead declines multi-source pipelines (frame batches above one image would run synchronously while still buffering latency) and workflows mixing lookahead with other stream-pipelined steps. Conventions: the dispatch gate reads the same env.py constants the blocks use; both remote blocks share one partial-based run_remotely shape; step_error_handler annotations match the two-argument call contract; engine preamble and usage-id derivation deduplicated; runner close/depth helpers shared; stream-lookahead terminology cross-referenced between the executor and block-side modules; unit suite pruned to behavior-per-test (106 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The engine now owns all pipelining mechanics. A block opts in with one method — is_async_stream_step() — certifying its run() is a re-entrant, long-latency call; the scheduler assembles inputs on the stream thread, executes the whole run() on a lookahead worker pool, and registers per-output future placeholders built from the block's declared outputs. Ordered emission resumes each frame's execution state as before. Adopted across 59 blocks: every Roboflow model family and version (detection, instance/semantic segmentation, keypoints, classification), the SAM2/SAM3 family, and all external-API blocks (Claude, OpenAI, Gemini, Gemma, Llama-Vision, Qwen, OpenRouter variants, Moondream, SmolVLM, OCR services) — API blocks pipeline in any execution mode, which the dispatch gate now permits by keying on the renamed WORKFLOWS_STREAM_LOOKAHEAD_DEPTH alone (old name honored as fallback). Excluded with reasons: wildcard-output and deprecated blocks, stateful video trackers, sinks (side effects), scalar blocks pending batch support, and openai_compatible pending a client-cache fix. Dynamic Custom Python manifests carry no statefulness declaration and are conservatively treated as stateful. The block-side RemoteStreamPipeline machinery is deleted; object detection v3 and SAM3 v3 revert to their plain synchronous forms plus the declaration. Unit coverage rewritten around the engine surface (13 focused tests; block test files restored to main's versions). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The overhead benchmark now sets WORKFLOWS_STREAM_LOOKAHEAD_DEPTH itself and patches the wrap-time gate constant, so it runs out of the box. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A local .env with LOG_LEVEL=DEBUG renders more log lines per frame on the sequential path than the lookahead path, fabricating ~1 ms/frame of apparent lookahead advantage. Measured at WARNING, the two-pass scheduler costs ~0.1 ms/frame in isolation and nets slightly faster than the sequential pass by overlapping the frame's JPEG encode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xt, usage timing, daemon workers - flush() drains all buffered frames best-effort, then raises StreamLookaheadDrainError carrying the drained results; the pipeline dispatches those and lets the failure surface through the normal inference error path (a failed tail frame can no longer disappear as a normal empty drain) - offloaded async steps rebind execution_id, remote-timing and debug collectors, and OTel context on the lookahead worker (mirroring safe_execute_step) and wrap failures in StepExecutionError - workflow usage is recorded on the resume pass instead of the deferred pass, so execution_duration covers the actual step execution - lookahead pool threads are pre-spawned as daemon threads so a request hung past close() cannot block interpreter exit; close() also cancels queued work Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Response to external adversarial review (Codex 5.5, extra-high effort)We ran an independent agent review against this branch with instructions to prove errors with repros, not just flag them. It confirmed the offload/ordering/output contracts hold (61 declared blocks audited clean, focused suites green, no serialization bug), and proved four real defects. All four are fixed in d2f4a36: 1. Critical — final buffered frame errors were swallowed on drain. 2. Async steps lost execution/debug/trace context. The offloaded 3. Usage duration was recorded before async work finished. 4. Hung requests could block interpreter exit after close(). Reviewer's cleared attacks, for the record: declared-output contract audit across all 61 async-declared blocks (0 problems), the three focused pytest suites (95 passed), and a Written by Claude Fable 5 · 🤖 Generated with Claude Code |
Under stream lookahead, scalar list parameters are the same object across every frame's run(); the in-place append(None) raced across concurrent frames and left the shared list as [None, None, ...], duplicating prompts and masks. Mirrors the fresh-copy fix already applied to SAM3 v3. Audit of all 61 async-declared blocks found no other in-place mutation of a scalar parameter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Response to second adversarial review (Opus 4.8 Ultra)A second independent agent review ran against the PR tip F2 (novel, MEDIUM) — SAM3 v1/v2 + seg_preview v1 mutated a shared cross-frame F1 (contextvar loss), F3 (usage duration), F4 (non-daemon workers), F5 (drain swallow) — independently corroborate the first review; all four were already fixed in Cleared attacks worth keeping on record: declared-output contracts across all 61 blocks (0 mismatches), On the suggested depth>1-vs-depth-1 parity matrix over all 60 adopted blocks: agreed that's the single highest-value guard for future adoptions, noted in Follow-ups. It needs either live models or a per-family mock harness, so we're keeping it out of this exploration PR; the shared-scalar-mutation class specifically is now covered by the AST audit + the copy-semantics regression tests. Process note: this review ran concurrently in the same checkout as the fix session and hard-reset the branch twice while diagnosing tree contamination; all work was recovered from the reflog and the branch history is clean ( Written by Claude Fable 5 · 🤖 Generated with Claude Code |
…he model step The daemon-prespawn alone did not deliver its documented guarantee: concurrent.futures joins every registered pool worker at interpreter shutdown regardless of the daemon flag, so a request hung past close() still blocked process exit. close() now also detaches the workers from that shutdown hook; daemon flag + detach together let the interpreter exit immediately (verified: 30s hung task, process exits right away). Also: async batch-contract violations (run() returning fewer elements than input indices) now raise at the model step with counts and a BlockTraceback, instead of surfacing as an index error blamed on the consuming step at resume; docstrings corrected (usage duration is steady-state approximate; stale module reference removed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Response to third adversarial review (Cursor / Fable, extra-high effort)Third independent agent review. Verdict quoted: "I would merge this behind its default-off flag. The correctness core — frontier partitioning, per-output future chaining, ordered resume on a per-frame EDM — survived every attack I could mount." Its five findings, and what we did ( 1 (HIGH, proven) — the daemon-prespawn lifecycle fix was ineffective by construction. Correct, and worth being blunt about: our previous fix shipped a documented guarantee that was false. 2 (MEDIUM, measured) — steady-state 3 (MEDIUM, traced) — the 60 s future-resolution deadline is a stream-killing mode sequential doesn't have. Acknowledged and documented in the failure-semantics section: 4 (LOW, proven) — batch-contract violations surfaced late, blamed on the consumer, without a traceback. Fixed: the offload wrapper now validates the returned element count against the input indices and raises at the model step with exact counts; async 5 (INFO) — stale artifacts. Fixed: the frontier docstring no longer references the deleted Cleared attacks now on record (this review's coverage was the deepest yet): 7 previously-unexercised block families through real lookahead streams with reversed completion order and both serialization modes (16/16 exact parity — including semantic-seg RLE masks and a VLM), output-contract AST audit (0 mismatches across 61 files), OpenAI client-cache + SAM3 re-entrancy hammer (8×100, zero contamination), bounded pool-in-pool growth, frontier shapes e2e (consensus correctly excluded as async-fed; Benchmarks re-validated on the review-hardened head (laptop): tracking 3.33 → 33.3 (d8) → 39.0 FPS (d16), SAM3 1.61 → 15.1 (d8), 0 parity mismatches; scheduler-overhead net −0.15 ms/frame. The fixes are perf-neutral. Written by Claude Fable 5 · 🤖 Generated with Claude Code |
…ills - bump EXECUTION_ENGINE_V1_VERSION to 1.13.0 (new capability, minor) with the changelog entry and both mirrored version-assertion tests - weakref.finalize GC fallback reaps the lookahead pool if the runner is dropped without close(); close() and the finalizer share one shutdown helper (shutdown + detach from the interpreter-shutdown join) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🤖 Claude review started at commit New commits are not auto-reviewed. Add the |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0cf9194. Configure here.
|
@balthazur — thanks for the unusually thorough writeup and the prior adversarial-review trail. I traced the lookahead scheduler, the frontier/async partition, the executor lifecycle, future chaining/resolution, and the block declarations. I did not find a high-confidence correctness blocker in the code itself: the feature is default-off, the EE version companions are all present (1.13.0 + changelog + both version-assertion tests), output futures resolve through the shared timeout-bounded helpers, the pool lifecycle is handled, and the blanket stateless-visualization default is safe (trace/heatmap correctly re-declare stateful; the
IMPORTANT — needs an owner decision, not just code
Questions (please answer; they may hide a defect)
Re-review is not automatic: once you've answered and/or pushed changes, add the Commands that informed this review: Reviewed at HEAD: 0cf9194 |
… var Cursor Bugbot (PR #2623): with WORKFLOWS_STREAM_LOOKAHEAD_DEPTH>1 set but the workflow not qualifying for lookahead (no async steps, multi-source, stateful-fed model — wrap falls back to a plain WorkflowRunner), the pipeline still capped predictions_queue_size to 4 and routed through the buffered dispatch path even though nothing was buffered, dropping throughput with no pipelining benefit. Gate buffered dispatch (queue cap, drain/close, late future resolution) on whether the handler actually buffers — it exposes flush(), same signal _drain_inference_handler/_close_inference_handler already duck-type — instead of on the env var. Plain runners keep the normal path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addressing the automated-review follow-ups: - Registry-wide consistency guard: any block keeping per-video HTTP state (STATEFUL_VIDEO_HTTP_SOFT_RESTRICTION) must never be declared stateless for video processing, else the lookahead frontier would run it ahead of stream order and corrupt its state. 26 of 211 loaded blocks are subjects; this is the declaration-level net for the class of mis-adoption the class_names bug showed. Plus a guard that every manifest returns a boolean statefulness. - Document that _shutdown_lookahead_executor relies on CPython internals (validated 3.9-3.12, degrades safely), pointing at the guarding test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the careful trace — and for separating "code defect" from "owner decision," which is exactly the right cut here. Status on all four, plus the Cursor Bugbot finding: Code-addressable items — fixed and pushed:
Owner decisions — I've documented, not resolved (agree these aren't mine to close):
Net: the two remaining items are genuinely the owners' to decide and the hold is correct. I'll add the Written by Claude Fable 5 · 🤖 Generated with Claude Code |
|
🤖 Claude review started at commit New commits are not auto-reviewed. Add the |
|
@balthazur — thanks for the three pushed fixes; I re-reviewed the delta (
Delta verified clean (no new findings):
IMPORTANT — still open, need an owner decision (not a code change):
Call to action: these two items are genuinely the owners' to decide. Until an owner confirms each, the PR stays on hold and may be kept out of a release. Re-review is not automatic: once the questions are answered and/or you push changes, add the Commands that informed this review: Reviewed at HEAD: 6f79581 |

Stream lookahead: concurrent remote/API step execution for video workflows, with strict frame ordering for stateful steps
Note
Started as an exploration POC for the video-streaming workflows effort (follow-up to the remote-execution benchmarking discussed in #p-video-streaming-workflows and to #2538); now hardened through three adversarial reviews and the repo's
.claude/skillsreview guidelines, and open for review. The feature is opt-in and default-off; the default path is behavior-identical tomain. Profiling-skill trace per the perf-PR requirement is being produced and will be shared alongside.Video workflows in remote execution mode — the mode the app's video previews will run on, and the substrate for any video processing service that doesn't own a GPU — today process one frame per round trip: the whole pipeline waits ~200-600 ms of network for every frame, capping throughput at 2-4 FPS regardless of how fast serverless actually is. This PR teaches the Execution Engine to keep N frames' remote model requests (and external-API calls like Gemini/Claude/OCR) in flight concurrently while trackers, counters, and sinks still see frames strictly in stream order — 10-12x FPS (3.5 → 44 on a laptop, 5 → 60 colocated), with outputs bit-identical to sequential execution, behind a default-off env var.
The mechanism is a two-pass scheduler over the existing compiled DAG (blocks declare two facts, the engine does the rest — details below); it composes with, and does not touch, the local RF-DETR stream pipeline, and it is the "parallel frame processing in the context of full workflow execution" direction the core team has already declared, built on the per-block statefulness metadata Pawel proposed.
What this does
With the new
WORKFLOWS_STREAM_LOOKAHEAD_DEPTH=N(default1= off; old nameWORKFLOWS_REMOTE_EXECUTION_PIPELINE_DEPTHhonored as fallback), the stream scheduler keeps up to N frames' long-latency step executions in flight concurrently — remote model requests in remote execution mode, and external-API calls (Gemini, Claude, GPT, OCR services) in ANY execution mode — while stateful steps (trackers, counters) run strictly in frame order on each frame's own inputs. Tracking output is bit-identical to sequential execution (tracker-ID parity verified in every benchmark).Architecture: the engine owns all mechanics; blocks declare two facts
WorkflowBlockManifest.is_stateful_for_video_processing()(default: stateful, conservative) — whether the block must observe frames in stream order. One override on the visualization base covers the stateless visualizers; trace and heatmap explicitly remain stateful. Dynamic Custom Python manifests carry no declaration and are always treated as stateful.WorkflowBlock.is_async_stream_step()(absent/false by default) — the block certifies itsrun()is a long-latency, re-entrant call the scheduler may execute ahead of stream order. That is the entire per-block surface: 1-3 lines.The engine does everything else, generically: it partitions the compiled DAG once into a frontier (steps whose ancestors are all stateless; async steps are launch-only sinks) and a remainder; per frame, a deferred pass runs the frontier — assembling each async step's inputs on the stream thread, executing its whole
run()on a lookahead worker pool, and registering per-output future placeholders built from the block's owndescribe_outputs()— and buffers the frame's liveExecutionDataManager. At ordered emission, a resume pass runs the remainder on that same EDM; the pre-existing future resolution at input assembly performs the ordered wait. Guards (all falling back to sequential with a warning): async step outside the frontier (stateful-fed, chained-async), multi-source pipelines, mixing with the local RF-DETR pipeline (untouched), missing execution graph, scalar/wildcard-output/dimension-offset steps.Adoption: 59 blocks — every Roboflow model family and version (object detection v1-v3, instance segmentation v1-v4, keypoints v1-v3, both classification families v1-v3, semantic segmentation, SAM2, SAM3 family) and all external-API blocks (Claude v1-3, OpenAI v2-4, Gemini v1-3, Gemma, Llama-Vision, Qwen family, OpenRouter variants, Moondream2, SmolVLM, OCR/EasyOCR/GLM-OCR, seg-preview, LMM-classifier, visual-search). Excluded with reasons: wildcard-output blocks (openai v1, lmm), deprecated blocks (cogvlm, gaze), stateful video trackers (sam2/sam3 video), all sinks (side effects), scalar blocks pending batch support (clip, perception-encoder, google-vision-ocr, stability-ai), and openai_compatible (mutates a client cache in the request path — needs a fix first).
Benchmarks (all numbers from this branch's final architecture)
vehicles-2.mp4, 300 measured frames (SAM3: 100), rfdetr-nano onserverless.roboflow.com, laptop ~100 ms RTT. Reproduce:development/benchmark_scripts/benchmark_remote_stream_pipeline.py --workflow tracking|sam3|two-models|preprocessed-tracking.us-central1 worker (n2-standard-2, colocated with serverless, ~12 ms RTT), tracking workflow, 300 frames, this exact architecture:
Parity 0/300 mismatched frames on the worker too. Laptop (~100 ms RTT) and colocated worker converge on similar pipelined throughput — pipelining makes throughput nearly RTT-independent (the ceiling becomes serverless capacity, not distance).
mainsequential 2.9 FPS; stateless-parallel c16 hack (broken tracking semantics) 70.4 FPS laptop / 38.7 worker.depth ~ target_fps x per-request latency; sensible default 8-16. First emission is delayed by buffer fill (~depth/fps) — relevant for live streams, irrelevant for files. Heavier hosted models (SAM3) saturate at lower depth.Scheduler overhead (
benchmark_remote_stream_pipeline_overhead.py, mocked instant HTTP, production log level): the two-pass machinery costs ~+0.1 ms/frame in isolation (zero-encode control, size-independent); in practice the lookahead pass measures 0.1-0.2 ms/frame FASTER than the sequential pass because the frame's GIL-releasing JPEG encode moves off the scheduling thread (the effect scales with encode cost: ~0.7 ms/frame on high-entropy 640x360 content). Compilation is untouched (compiler diff vs main is empty). An earlier ~1 ms/frame figure was a DEBUG-logging artifact (dev .env; asymmetric log-line counts) and is retracted. Client re-instantiation per request: 0.005 ms (the historical cost was TLS sessions, solved by #2538, which the long-lived pool threads compound).Multi-pipeline: two concurrent pipelines in one process — both pipelined, byte-identical tracker IDs across pipelines (no shared state).
Context: why this is not the depth-2 null result
The team's
RFDETR_PIPELINE_DEPTH=2experiment concluded frame pipelining "didn't give any speedup once the futures were materialized" (Damian; nsys showed ~13kcudasynccalls vs 406 — every materialization forced a same-frame CUDA stream sync). That regime was local-GPU compute-bound: ~15% hideable time. This PR targets the opposite regime: remote workflow execution spends ~85-92% of every frame waiting on the network (~30 ms server compute inside 180-420 ms frame time, measured). Materializing our future is aFuture.result()on an HTTP response that arriveddepth / fpsseconds earlier — a no-op read, not a GPU sync. Same technique, opposite physics: ~1.0x there, 8-12x here.Failure & operational semantics
A failed request surfaces at its frame's emission (SDK retries apply first). On end-of-stream drain, remaining buffered frames are emitted best-effort and any emission failure is re-raised afterwards (
StreamLookaheadDrainError, carrying the frames that did drain) so a failed tail frame surfaces through the normal inference error path instead of disappearing as an empty drain. Offloadedrun()calls execute with the stream thread's execution/debug/OTel context rebound and failures wrapped inStepExecutionErrorwith aBlockTraceback, matching the sync path; batch-contract violations (wrong output count) fail at the model step with counts, not as an index error on the consumer. Hung-request lifecycle: pool threads are daemon andclose()detaches them fromconcurrent.futures' interpreter-shutdown join (which ignores the daemon flag) — verified a 30 s hung request no longer blocks process exit. Future resolution at resume has aWORKFLOWS_ASYNC_FUTURE_RESULT_TIMEOUT(default 60 s) deadline that sequential mode doesn't have: a degraded backend that stalls a single request past it ends the stream rather than just slowing it — raise the env var for flaky links (see Follow-ups for SDK-side request timeouts, which sequential lacks too).Usage accounting caveat (flagging explicitly for the usage-tracking owners): usage is recorded exactly once per frame with api key/workflow id/fps intact, but
execution_durationin steady state reflects the resume-pass compute, not the remote inference wall time — that wait is hidden behind newer frames' work, which is the point of the lookahead. Any consumer of per-frame duration (duration minimums, billing analytics) sees smaller values for lookahead streams than sequential ones doing identical work.Memory: depth x (frame + frontier outputs) buffered (~100 MB at depth 16/1080p). Known cosmetics: profiler counts two run-brackets per frame; watchdog started/ready pairing skews under buffering (shared with RF-DETR). Nothing outside the stream runner offloads — one-shot HTTP
run()calls never enter the lookahead path even with the env var set (verified:run_workflownever passes async selectors).Versioning & repo review-guidelines compliance
Audited against the repo's own
.claude/skills/review suite (review-workflows-execution-engine,review-topic-concurrency-and-resource-safety,review-topic-workflow-state-management,review-topic-backward-compat-and-versioning,review-topic-test-hygiene,review-workflows-blocks,review-topic-local-vs-remote-execution,review-topic-external-contract-and-silent-fallback):EXECUTION_ENGINE_V1_VERSIONbumped1.12.0 -> 1.13.0(new capability, minor), changelog entry added todocs/workflows/execution_engine_changelog.md, and both mirrored version-assertion tests updated (test_workflow_endpoints.py,hosted_platform_tests/test_workflows.py). Defaultrun(...)behavior is unchanged with the env var unset.inference/core/version.pybump deferred to release per the skill's carve-out.executor/utils.pyhelpers withWORKFLOWS_ASYNC_FUTURE_RESULT_TIMEOUT; the lookahead pool is injected (never created insiderun()), bounded, has an explicitclose()AND aweakref.finalizeGC fallback (the Add weakref finalizers for RF-DETR stream pipeline executors #2491 pattern), andmax_concurrent_stepsis honored.type/kind/I-O changes, so no vN+1 siblings needed); output contracts verified across all 61 declared blocks; no new kinds/loader wiring.get_restrictions(); default is conservative-stateful; Custom Python dynamic blocks are treated as stateful.Tests
25 focused test functions / 51 parametrized cases owned by this PR (plus main's pre-existing suites restored and passing): async-step eligibility matrix, frontier shapes (incl. the Custom-Python-manifest case), generic offload with per-output futures, run/resume round trip (no step executed twice), wrap-selection matrix (incl. RF-DETR regression + all fallbacks), runner ordering/drain/error-isolation, gating, statefulness metadata, block declarations — and the adversarial reversed-delay real-
ExecutionEnginetest asserting frame order, per-frame payload identity, and tracker parity against a sequential reference. Review-hardening additions: drain-error propagation at runner + pipeline level, worker-side context rebinding, usage-decorator placement, daemon pool threads,class_namescopy semantics for SAM3 v1/v2/seg_preview, buffered-dispatch gating keyed on the actual handler (not the depth env var), and a registry-wide adoption guard (no per-video-stateful block may be declared stateless-for-lookahead).Follow-ups
stateless: truedeclaration for Custom Python blocks (aligned with the sandbox/WS execution work) — until then they are stateful by construction.class_namesclass of mis-adoption; 26/211 loaded blocks are subjects). A third review also ran 7 previously-unexercised families (keypoints, both classifications, instance-seg v1/v2/v4, semantic-seg, OpenAI v3) through real lookahead streams with reversed completion order and both serialization modes: 16/16 exact parity.session.post/requests.postcarry notimeout=): a dead TCP peer hangs a request forever in BOTH sequential and lookahead modes today; lookahead's exit-safety now contains the blast radius, but per-request socket timeouts are the real fix and belong in inference_sdk.execution_durationsemantics (see the accounting caveat above) with the usage-tracking owners before GA.How this PR got here (3 iterations)
flush_stream_pipelinepath. Worked (13.6x on the worker), but each block needed ~40 lines of shim and the block owned scheduling.ExecutionDataManager; resume pass runs the remainder at ordered emission). Blocks still carried a request-queueing shim.run()and builds output futures fromdescribe_outputs(). The shared shim was deleted; per-block surface shrank to the two declarations; 59 blocks adopted. Each iteration was benchmarked against serverless with tracker-parity checks, and the design went through several adversarial review passes, including three independent external agent reviews: four lifecycle/observability findings (tail-frame drain error swallowing, async-step context loss, usage-duration timing, non-daemon pool threads) fixed ind2f4a36b4; one shared-scalar mutation finding (SAM3 v1/v2 + seg_previewclass_names) fixed in7b4801a9e; and the third review proving the daemon-only lifecycle fix insufficient (concurrent.futures joins pool workers at shutdown regardless of daemonness) plus a late-blamed batch-contract failure — both fixed in1e86fa6b6. Details and each review's cleared-attack record (output contracts across all 61 declared blocks, 7-family lookahead parity incl. serialization, re-entrancy hammer, thread-leak cycles, frontier guards) are in the review-response comments below.Written by Claude Fable 5. Generated with Claude Code