Pipelines
Composition Patterns
Compose reusable workflows from small pipeline stages.
Once the basic flow works, split reusable stages into small pipeline operations and compose them with .use(...).
Reuse a Pipeline Operation
const normalizeTicket = new PipelineBuilder<string>()
.step((ticket) => ticket.trim())
.step((ticket) => ticket.replace(/\s+/g, " "))
.build();
const supportPipeline = new PipelineBuilder<string>()
.use(normalizeTicket)
.prompt(supportAgent)
.build();Use .use(...) when a whole stage is useful in more than one workflow.
Normalize, Prompt, Extract
const triagePipeline = new PipelineBuilder<string>()
.use(normalizeTicket)
.step((ticket) => `Summarize and classify:\n\n${ticket}`)
.prompt(triageAgent)
.extract(triageExtractor)
.build();This is the default shape for many production workflows: deterministic preprocessing, model reasoning, then schema validation.
Branch, Then Merge
const pipeline = new PipelineBuilder<string>()
.use(normalizeTicket)
.parallel({
reply: new PipelineBuilder<string>().prompt(replyAgent).build(),
triage: new PipelineBuilder<string>().prompt(triageAgent).extract(triageExtractor).build(),
})
.step(({ reply, triage }) => ({
reply,
priority: triage.priority,
}))
.build();Use this when the branches do not depend on each other but the final result should combine them.
Keep the Shape Visible
Prefer a pipeline that reads like the workflow:
const pipeline = new PipelineBuilder<string>()
.use(loadTicket)
.use(addRetrievalContext)
.prompt(agent)
.extract(outputExtractor)
.use(saveResult)
.build();If a step becomes hard to name, split it into smaller steps.
