mrkeyoor.com_
Wed 05 Aug 19:58 UTC
npmAI / MLupdated 05 Aug 2026

@langchain/core

@langchain/core is the foundation package of LangChain.js: the base abstractions everything else in that ecosystem implements. It defines chat models, messages, prompt templates, tools, output parsers, retrievers, and the Runnable interface that lets you pipe these pieces together with streaming, batching, and fallbacks built in. You rarely install it as your only dependency; provider packages like @langchain/openai and @langchain/anthropic extend its base classes, and the langchain and langgraph packages build on top of it.

Verdict

If you are in the LangChain ecosystem, core is not optional, and its abstractions are genuinely well designed for provider swapping and streaming. If you are not already committed to that ecosystem, start with a plain SDK or the Vercel AI SDK and adopt LangChain only when you feel the need for its orchestration.

API stability3/5Hit 1.0 in late 2025 after years of fast-moving 0.x releases; the Runnable core is settled now, but the 0.2/0.3 era broke imports repeatedly and much online example code is stale.
Docs3/5docs.langchain.com is broad and the reference is typed, but content spans versions and packages, so finding the current v1 way to do something often means wading past outdated snippets.
Maintenance5/5Pushed the same day this was written with a steady release cadence from the LangChain team; 229 open issues on the monorepo is moderate for its usage.
Ecosystem5/5The entire @langchain/* provider catalog, langgraph, and langsmith all build on these abstractions, and at 5M+ weekly downloads nearly every integration question has prior art.

Use it if

  • You already use langchain, langgraph, or any @langchain/* provider package: core is their shared contract and you will import messages and prompts from it constantly
  • You are writing a custom integration (a chat model wrapper, retriever, or tool) that should plug into the LangChain ecosystem: extending core's base classes is the whole point of the package
  • You want provider-agnostic app code where swapping ChatOpenAI for ChatAnthropic is a one-line change because both satisfy the same BaseChatModel interface
  • You need streaming, batching, retries, and fallbacks as composable wrappers instead of hand-rolled loops around a provider SDK
Skip it if

Setup reality

npm install @langchain/core is easy; keeping it consistent is the real work. Provider packages declare core as a peer dependency, so with multiple @langchain/* packages you can silently end up with two core copies and get errors like a prompt not being accepted as a Runnable. The documented fix is pinning one version via package.json overrides (npm) or resolutions (yarn/pnpm). Node 20+ is required, the package is dual ESM/CJS, and imports come from deep paths like @langchain/core/messages and @langchain/core/prompts, which your editor will not always autocomplete correctly. The v1 release also moved and renamed things, so pre-1.0 tutorials frequently show imports that no longer exist.

Patterns

Build a chat prompt templateprompt-template

import { ChatPromptTemplate } from "@langchain/core/prompts";

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are a strict copy editor."],
  ["human", "Fix this text: {text}"],
]);

const messages = await prompt.invoke({ text: "teh cat sat" });

Braces are template variables; to output a literal { or } in the prompt you must double them as {{ and }}.

Pipe prompt, model, and parser into a chainchain-with-pipe

import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { ChatOpenAI } from "@langchain/openai";

const chain = ChatPromptTemplate.fromTemplate("Summarize: {input}")
  .pipe(new ChatOpenAI({ model: "gpt-4o-mini" }))
  .pipe(new StringOutputParser());

const summary = await chain.invoke({ input: longText });

The model class comes from a provider package, not core. If pipe complains that the prompt is not a Runnable, you almost certainly have two @langchain/core versions installed.

Stream a chain token by tokenstream-response

const stream = await chain.stream({ input: "Why is the sky blue?" });

for await (const chunk of stream) {
  process.stdout.write(chunk);
}

Every Runnable exposes stream(); with a StringOutputParser at the end the chunks are plain strings, otherwise they are AIMessageChunk objects you concat yourself.

Create messages directlyconstruct-messages

import { HumanMessage, SystemMessage } from "@langchain/core/messages";

const res = await model.invoke([
  new SystemMessage("Answer in one sentence."),
  new HumanMessage("What is an LRU cache?"),
]);

console.log(res.content);

res.content can be a string or an array of content blocks depending on the provider and modality; check before assuming string.

Define a tool with a zod schemadefine-tool

import { tool } from "@langchain/core/tools";
import { z } from "zod";

const getWeather = tool(
  async ({ city }) => `72F and sunny in ${city}`,
  {
    name: "get_weather",
    description: "Get current weather for a city",
    schema: z.object({ city: z.string() }),
  }
);

const modelWithTools = model.bindTools([getWeather]);

bindTools only attaches the schemas; the model returns tool_calls on the AIMessage and executing the tool (or looping) is your job unless you use langgraph agents.

Wrap any function as a Runnablecustom-runnable

import { RunnableLambda } from "@langchain/core/runnables";

const upper = RunnableLambda.from(async (s: string) => s.toUpperCase());

const chain = someChain.pipe(upper);
await upper.invoke("hi"); // "HI"

This is how arbitrary business logic joins a chain and inherits invoke, batch, and stream for free.

Run branches in parallel over one inputparallel-branches

import { RunnableMap } from "@langchain/core/runnables";

const combined = RunnableMap.from({
  summary: summarizeChain,
  tags: tagChain,
});

const { summary, tags } = await combined.invoke({ input: text });

Branches run concurrently and the result is an object with the same keys; this is also the standard trick for feeding retriever context plus the raw question into a prompt.

Process many inputs with bounded concurrencybatch-inputs

const results = await chain.batch(
  [{ input: a }, { input: b }, { input: c }],
  { maxConcurrency: 2 }
);

batch preserves input order in its results. Set maxConcurrency or you will discover your provider's rate limits the hard way.

Add retry and a fallback modelretries-and-fallbacks

const reliable = primaryModel
  .withRetry({ stopAfterAttempt: 3 })
  .withFallbacks([backupModel]);

const res = await reliable.invoke(messages);

withRetry retries the same Runnable on error; withFallbacks moves to the next one after the first fails, and both return a new Runnable rather than mutating the original.

Get typed structured output from a modelstructured-output

import { z } from "zod";

const schema = z.object({
  sentiment: z.enum(["pos", "neg", "neutral"]),
  confidence: z.number(),
});

const structured = model.withStructuredOutput(schema);
const out = await structured.invoke("I love this library");
// out.sentiment, out.confidence are typed

withStructuredOutput is defined on BaseChatModel in core but relies on the provider supporting tool calling or JSON mode; not every model can do it.

Force a single @langchain/core across packagespin-core-version

// package.json (npm)
{
  "overrides": {
    "@langchain/core": "1.2.4"
  }
}
// yarn or pnpm use "resolutions" instead

All @langchain/* packages must share one core instance or instanceof checks fail at runtime. Check with: npm ls @langchain/core.

Alternatives

PackageRegistryPick it when
ainpmYou are building a TypeScript or Next.js app and want streaming, tool calls, and UI hooks with a lighter abstraction than LangChain.
openainpmYou only target OpenAI-compatible APIs and prefer a direct SDK with no framework layer.
llamaindexnpmYour app is retrieval-heavy and you want indexes and query engines as the primary abstraction.