LangChain
Run a LangChain.js runnable as the pipeline's LLM step with LangchainProcessor.
Overview
LangchainProcessor puts a LangChain.js runnable where an LLM service
would sit in the pipeline. It takes the latest user message from the context, streams it through
the runnable and pushes the output as text frames.
LangchainProcessor: a frame processor that wraps anyRunnablefrom@langchain/core: a prompt piped into a chat model, a chain with message history, or a compiled LangGraph graph.
Upstream source
Pipecat's LangchainProcessor. Upstream has no service page for it.
Source
packages/langchain/src/langchain.ts.
LangChain.js docs
Runnables, chat models and LangGraph.
Installation
@langchain/core is a peer dependency (^1.0.0): @pipedog/langchain imports from it but does
not install it, so you pick the version. Add the chat model integration you use as well.
bun add @pipedog/langchain @langchain/core @langchain/openaiimport { LangchainProcessor } from "@pipedog/langchain/langchain";Prerequisites
- Whatever your runnable needs. With
ChatOpenAIthat is an OpenAI API key inOPENAI_API_KEY. - The processor itself needs no key.
LangchainProcessor
Configuration
The constructor is new LangchainProcessor(chain, options). chain is a Runnable from
@langchain/core/runnables. The processor calls only its stream(input, config) method.
Prop
Type
What the runnable receives:
- input:
{ [transcriptKey]: text }, wheretextis the trimmed content of the last context message. With the default key that is{ input: "..." }, so a prompt template should use an{input}variable. - config:
{ configurable: { session_id }, signal }.session_idis the value passed tosetParticipantId()(nulluntil then), which is whatRunnableWithMessageHistoryreads.signalaborts the stream when the pipeline is interrupted or cancelled.
What it must yield: strings or AIMessageChunks. A chunk's string content becomes the frame
text. List content (multimodal) is turned into text with JSON.stringify. Any other value
becomes an empty TextFrame.
Usage
Build the runnable with LangChain:
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a helpful voice assistant. Keep answers short."],
["human", "{input}"],
]);
export const chain = prompt.pipe(new ChatOpenAI({ model: "gpt-4o-mini" }));Then wrap it in the processor, in place of an LLM service:
import { LangchainProcessor } from "@pipedog/langchain/langchain";
// Any @langchain/core Runnable: a prompt piped into a chat model, a compiled LangGraph, ...
declare const chain: ConstructorParameters<typeof LangchainProcessor>[0];
const langchain = new LangchainProcessor(chain, { transcriptKey: "input" });
// Sent to the chain as `config.configurable.session_id`, for message history.
langchain.setParticipantId("participant-123");The block that builds the chain is not type-checked by the docs build, since the docs app does
not install @langchain/core. The processor block is.
Notes
- Consumes
LLMContextFrame. Every other frame passes through. - The last message must be a standard message with string content. A provider-specific message
(
LLMSpecificMessage) or list content is not sent to the chain: the context frame is passed on instead. - For each run it pushes
LLMFullResponseStartFrame, oneTextFrameper streamed chunk (withincludesInterFrameSpacesset) andLLMFullResponseEndFrame. The end frame is pushed even when the chain throws. - A chain error is pushed upstream as an
ErrorFrame(Unknown error occurred: ...). - A compiled LangGraph graph is a
Runnable, so it works here too. Its defaultstream()yields state updates, not text, so wrap it (for example in aRunnableLambdathat returns the final message) or stream withstreamMode: "messages". The package tests show both.
Runtime notes
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/langchain/package.json: ✅ means the compat smoke test passes on that runtime.
- Each step of the stream is a cancellation point, so an interruption stops the chain mid-stream.
The iterator is closed in a
finallyblock. - Upstream logs a warning on Python's
GeneratorExit. That branch has no JS equivalent and is not ported. - The package targets Bun, Node, Deno and Cloudflare Workers, and uses no runtime-specific APIs. Whether a chain runs on a given runtime depends on the LangChain integrations it uses.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).