mrkeyoor.com_
Wed 05 Aug 19:56 UTC
PyPIAI / MLupdated 05 Aug 2026

langchain-core

langchain-core is the foundation layer of the LangChain ecosystem: the message types, prompt templates, tool definitions, output parsers, and the Runnable protocol that lets you pipe components together with the | operator. You rarely install it on purpose; langchain, langgraph, and every langchain-* provider package (langchain-openai, langchain-anthropic, and so on) depend on it, so its 41M weekly downloads are mostly transitive. If you write against its interfaces, you can swap chat models and vector stores without rewriting the plumbing.

Verdict

The de facto standard interface layer for LLM apps in Python, and the right dependency if you are in the LangChain or LangGraph ecosystem at all. If your app is one provider and a few prompts, the abstraction costs more than it returns; call the provider SDK directly.

API stability3/5The 1.0 release settled the core interfaces after years of movement, but the ecosystem's history is one of renamed packages and relocated imports, and deprecation cycles still run fast; pin every langchain-* package together.
Docs4/5reference.langchain.com and docs.langchain.com are thorough with runnable examples, but content is spread across LangChain, LangGraph, and LangSmith properties, and search results still surface 0.x-era pages that no longer apply.
Maintenance5/5Backed by the LangChain company with daily pushes to the monorepo, around 379 open issues (plus PRs) against enormous usage, and fast turnaround on provider API changes.
Ecosystem5/5The largest integration catalog in the space: hundreds of langchain-* provider and community packages, LangGraph and LangSmith built on top, and most vendors ship a LangChain integration on day one.

Use it if

  • You are building on LangChain or LangGraph anyway and want to import only the base abstractions (messages, prompts, tools) in library code without dragging in the full langchain package
  • You are writing a custom integration (a chat model wrapper, retriever, or tool) and need the standard interfaces the rest of the ecosystem expects
  • You want provider portability: code written against BaseChatModel and the message types runs against OpenAI, Anthropic, or a local model by swapping one object
  • You use the Runnable composition style (prompt | model | parser) and want batch, stream, retry, and fallback behavior for free on every component
Skip it if

Setup reality

pip install langchain-core is quick and pure Python, with pydantic v2, tenacity, jsonpatch, and langsmith as the notable dependencies (yes, the LangSmith client ships even if you never trace anything; it stays inert without an API key). The real setup cost is version discipline: langchain-core 1.x must line up with 1.x-era langchain and provider packages, and mixing a 0.3-era integration into a 1.x environment produces resolver conflicts or import errors. Old tutorials are a minefield because import paths moved repeatedly (langchain to langchain_community to langchain_core over the years), so anything pre-2025 needs translation.

Patterns

Build a chat prompt with variableschat-prompt-template

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a terse assistant. Answer in {language}."),
    ("human", "{question}"),
])
messages = prompt.invoke({"language": "French", "question": "What is LCEL?"})

Template variables use single braces, so literal JSON in a prompt must escape braces as {{ and }} or the template raises a KeyError.

Pipe prompt, model, and parser togethercompose-chain

from langchain_core.output_parsers import StrOutputParser

chain = prompt | model | StrOutputParser()
answer = chain.invoke({"language": "French", "question": "What is LCEL?"})

Any Runnable composes with |; the parser at the end is what turns an AIMessage into a plain string, so leaving it off hands you message objects.

Build a conversation from message objectsconstruct-messages

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

history = [
    SystemMessage("You are a helpful assistant."),
    HumanMessage("What is 2 + 2?"),
    AIMessage("4"),
    HumanMessage("Double it."),
]
result = model.invoke(history)

Models also accept ('human', 'text') tuples and plain strings; a bare string becomes a single HumanMessage.

Turn a function into a tool with @tooldefine-tool

from langchain_core.tools import tool

@tool
def get_weather(city: str) -> str:
    """Return current weather for a city."""
    return f"Sunny in {city}"

model_with_tools = model.bind_tools([get_weather])
ai_msg = model_with_tools.invoke("Weather in Pune?")
print(ai_msg.tool_calls)

The docstring is required; it becomes the tool description the model sees. bind_tools only requests calls, executing them and returning ToolMessages is on you (or LangGraph).

Get validated structured output from a modelstructured-output

from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

structured = model.with_structured_output(Person)
person = structured.invoke("Amit is 34 years old.")
print(person.age)

with_structured_output uses the provider's native tool-calling or JSON mode under the hood, so support and reliability vary by model.

Stream a response chunk by chunkstream-tokens

for chunk in chain.stream({"language": "English", "question": "Explain RAG."}):
    print(chunk, end="", flush=True)

Streaming a raw model yields AIMessageChunk objects you can sum with +; after StrOutputParser the chunks are already plain strings.

Run many inputs concurrently with batchbatch-inputs

questions = [{"language": "English", "question": q} for q in ["What is LCEL?", "What is RAG?"]]
answers = chain.batch(questions, config={"max_concurrency": 5})

Results come back in input order regardless of completion order; cap max_concurrency or you will hit provider rate limits on big lists.

Fan out to parallel sub-chainsparallel-branches

from langchain_core.runnables import RunnableParallel, RunnablePassthrough

mapper = RunnableParallel(
    summary=summarize_chain,
    original=RunnablePassthrough(),
)
out = mapper.invoke({"text": "long document..."})
print(out["summary"], out["original"])

A plain dict inside a chain is auto-coerced to RunnableParallel, which is why {'context': retriever, 'question': RunnablePassthrough()} works in RAG examples.

Drop a plain function into a chainwrap-function

from langchain_core.runnables import RunnableLambda

clean = RunnableLambda(lambda text: text.strip().lower())
chain = clean | prompt | model

Functions piped directly against another Runnable are wrapped automatically, but RunnableLambda is required when the function is the first step.

Add retries and a fallback modelretry-and-fallback

primary = model.with_retry(stop_after_attempt=3)
resilient = primary.with_fallbacks([backup_model])
result = (prompt | resilient | StrOutputParser()).invoke(inputs)

with_retry retries on any exception by default, including bad-request errors that will never succeed; scope it with retry_if_exception_type.

Parse model output into a Pydantic objectparse-json-output

from langchain_core.output_parsers import PydanticOutputParser

parser = PydanticOutputParser(pydantic_object=Person)
prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer as JSON.\n{format_instructions}"),
    ("human", "{query}"),
]).partial(format_instructions=parser.get_format_instructions())
person = (prompt | model | parser).invoke({"query": "Amit is 34."})

Prefer with_structured_output when the provider supports it; prompt-based parsing fails on chatty models that wrap JSON in prose.

Use the async API for concurrent appsasync-invoke

import asyncio

async def main():
    answer = await chain.ainvoke({"language": "English", "question": "What is LCEL?"})
    async for chunk in chain.astream({"language": "English", "question": "More detail."}):
        print(chunk, end="")

asyncio.run(main())

Every Runnable gets ainvoke, astream, and abatch automatically, but custom RunnableLambdas with blocking I/O will still block the event loop.

Alternatives

PackageRegistryPick it when
pydantic-aiPyPIYou want typed agents and structured output on top of pydantic with a far smaller abstraction surface.
llama-index-corePyPIYour application is retrieval-centric and you want data connectors and indexes as the primary abstraction.
haystack-aiPyPIYou want explicit, declarative pipelines for RAG and search rather than operator-overloaded composition.