picoflow.io Docs

Compare

Parallelism and fan-out

Both systems can run work concurrently; their main difference is whether concurrency is explicit helper-driven work or graph-scheduled work.

Concurrency is not a reason by itself to select either framework. The design question is where the ownership of input, state updates, joins, and failure handling belongs.

Diagram comparing PicoFlow's flow-level batch concurrency and nested step fan-out with LangGraph's topology-driven super-steps and dynamic Send fan-out.
PicoFlow makes the parent or step initiate and join concurrent work. LangGraph's scheduler can derive concurrent work from graph topology; concurrent writes to shared state need reducers.

PicoFlow: two scopes

Flow.concurrentSteps() batches independent flow executions through the application’s own endpoint and returns each result to the coordinating flow. It is appropriate when each item needs its own flow request and configuration.

This helper is coordinator concurrency, not an internal graph branch. It slices items by batchSize, calls the app through SELF_URL, and invokes callbacks with each response. Every child is an independent flow request with its own session. Network routing, authentication, timeouts, retry policy, and partial failures therefore matter even when parent and child run in the same deployment.

Step.runSteps() fans out nested child steps within an active flow and aggregates their responses. It is appropriate for bounded child work whose parent owns the result. Nested child steps are intentionally restricted: they must not use tool-driven transitions such as go, stay, or direct, because they execute inside the parent execution frame. A tool in such a child may instead return directResult(json): it stops that child after the tool call and delivers the JSON value as the branch’s output, with no second model call or cursor change.

runSteps() creates a fresh Step instance and private memory/state view per invocation, then publishes validated state at an explicit join. The default retain-successes policy keeps fulfilled state when a sibling fails; atomic makes application-state publication all-or- nothing. Repeated Step classes use stable branch keys and Step-owned reducers. Bounded concurrency and cooperative cancellation are built in, but this remains an in-process join, not a persisted work queue.

LangGraph: scheduler-level branches

LangGraph can schedule multiple nodes in the same super-step through graph topology, and its dynamic fan-out mechanisms can send work to downstream nodes. If branches update the same state key, the graph state needs a reducer that defines the join.

Normal multiple outgoing edges schedule their destinations concurrently in the next super-step. Send supports map/reduce-style dynamic fan-out where each destination receives a different input. Reducers are not optional decoration in that design: they define whether parallel results append, add, merge, replace, or conflict. See the current Graph API guide.

This makes parallelism visible in the graph itself, which is valuable when scheduling and joins are central to the workflow. It also means state-channel and reducer choices are part of normal graph authoring.

The direct hotel graph is sequential. Each agent routes to at most one tool node, and each tool node routes to at most one next agent. Its append reducers have not been exercised under parallel writes. LangGraph’s capacity for parallel super-steps is therefore a framework capability, not a benefit demonstrated by this particular implementation.

Join and failure semantics

Question PicoFlow concurrentSteps() PicoFlow runSteps() LangGraph branch/Send
Unit of work Independent flow HTTP request Child step inside one flow Graph node invocation
State ownership Separate child session Private invocation drafts; canonical session state at join Shared or per-Send graph input
Join owner Parent callbacks/application code Explicit calling Step plus Step-owned reducers Scheduler plus state reducers
Durable progress Whatever each child flow saves Outer turn save or optional root-join checkpoint Checkpoints when configured
Partial failure policy Parent callback/HTTP policy retain-successes (default) or atomic Graph retry/checkpoint/application policy
Dynamic fan-out Input array Explicit step request array Send from routing logic

Backpressure and resource limits

None of the abstractions removes the need for limits. Bound concurrency by provider quotas, database connections, tool capacity, and memory—not only CPU. Define what happens when one item times out, whether successful siblings may commit, and how a caller observes partial progress.

For LLM work, parallelism can reduce wall-clock time while multiplying token spend and rate- limit pressure. A deterministic tool fan-out and a model fan-out deserve different budgets.

A useful rule

Use PicoFlow helpers when a parent stage owns a small, bounded aggregation or coordinates independent flow requests. Use LangGraph fan-out when the graph runtime should own branching, scheduling, and reduction. In both cases, make external effects idempotent: concurrent session writes cannot undo an email, booking, or payment already sent to another system.