Identify the framework whose composition syntax overloads the `|` (pipe) operator
LangChain LCEL is the only one that overloads the pipe; LlamaIndex uses method chaining, Vercel AI SDK uses functions, Haystack v2 uses explicit socket wiring.
Imagine four different ways to connect garden hoses. LangChain LCEL is the one with snap-on connectors. You literally click hose A onto hose B with one motion. The `|` symbol is the snap. LlamaIndex is more like turning faucets in sequence. You turn one on, water flows through, then turn the next. Method calls in a chain, no clicking. Vercel AI SDK is just buckets and a person carrying water between them. Explicit function calls, no plumbing metaphor. Haystack v2 makes you label every pipe end and write down 'connect hose A's output to hose B's input' on a clipboard. Explicit wiring. Only one of these uses the snap.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Composition syntax is one of the small details that decides whether a framework feels delightful or clunky. LangChain LCEL's prompt | model | parser pipe is a famous example of getting it right. The syntax matches the data flow, reads top to bottom, and gives the framework a single surface to implement batch, stream, and async over the whole chain.
This dive walks through how the pipe works, why other major frameworks chose different idioms, where LCEL's pipe earns its keep, and where it runs out. Which is exactly when LangGraph or a different framework starts to look attractive.
How LCEL's pipe actually works
The mechanism is Python operator overloading. The Runnable base class implements __or__:
class Runnable:
def __or__(self, other):
return RunnableSequence(first=self, last=other)
Every Runnable in the LangChain ecosystem. Prompts (ChatPromptTemplate), models (ChatOpenAI, ChatAnthropic), parsers (StrOutputParser, JsonOutputParser), retrievers, and arbitrary callables wrapped in RunnableLambda. Inherits this. So prompt | model | parser evaluates as (prompt.__or__(model)).__or__(parser), producing a RunnableSequence with three steps that implements the same surface as the originals.
The uniformity is the load-bearing part. Because every Runnable speaks invoke, batch, stream, astream, ainvoke, abatch, the composed sequence also speaks those methods. Streaming through a chain of prompt → model → parser is .stream(input) on the whole sequence, and the framework handles the per-step streaming semantics. Batching is .batch([inputs]) and the framework batches at the model layer where it makes sense.
The ergonomic payoff: the syntax matches the data flow visually, and the operator's left-associativity matches the read order. Compare with method chaining (prompt.then(model).then(parser)) or explicit composition (RunnableSequence([prompt, model, parser])). Both work, neither reads as cleanly.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
| Framework | Composition idiom | Why this choice |
|---|---|---|
| LangChain LCEL | `a | b | c` pipe over Runnables | Uniform Runnable protocol; batch / stream / async free across the chain |
| LlamaIndex | Method chaining and factories | Data-centric primitives with distinct types per role |
| Vercel AI SDK | Function calls + React hooks | Deliberately thin; mirrors provider SDK shape |
| Haystack v2 | Explicit `pipeline.connect(a.out, b.in)` | Typed sockets; static graph analysis |
Real products, models, and research that use this idea.
- LangChain's own quickstart shows `prompt | model | StrOutputParser()` as the canonical Hello World. Pipe composition is the framework's calling card.
- Haystack v2 production pipelines at deepset use explicit `pipeline.connect` calls so socket types are statically checkable.
What an interviewer would ask next. Try answering before peeking at the approach.
QWhat does `RunnableSequence` look like under the hood after `prompt | model | parser`?
A RunnableSequence with three steps; each step's output feeds the next as input. The class implements the Runnable surface so the sequence itself can be piped further or have .invoke, .batch, .stream called on it.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Assuming any composition framework supports the pipe because LCEL made it famous. Then writing `query_engine | synthesizer` in LlamaIndex and getting a TypeError.
60 second bullets to scan on the way to the call.
LCEL = LangChain Expression Language, owns the pipe
Runnable.or as the mechanism behind the pipe
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.